@@ -152,6 +152,7 @@ class DataFrameFormatter(object):
152
152
153
153
self.to_string() : console-friendly tabular output
154
154
self.to_html() : html table
155
+ self.to_latex() : LaTeX tabular environment table
155
156
156
157
"""
157
158
@@ -190,63 +191,103 @@ def __init__(self, frame, buf=None, columns=None, col_space=None,
190
191
else :
191
192
self .columns = frame .columns
192
193
193
- def to_string (self , force_unicode = False ):
194
+ def _to_str_columns (self , force_unicode = False ):
194
195
"""
195
- Render a DataFrame to a console-friendly tabular output .
196
+ Render a DataFrame to a list of columns (as lists of strings) .
196
197
"""
197
198
frame = self .frame
198
199
199
- to_write = []
200
+ # may include levels names also
201
+ str_index = self ._get_formatted_index ()
202
+ str_columns = self ._get_formatted_column_labels ()
200
203
201
- if len (frame .columns ) == 0 or len (frame .index ) == 0 :
202
- info_line = (u'Empty %s\n Columns: %s\n Index: %s'
203
- % (type (self .frame ).__name__ ,
204
- frame .columns , frame .index ))
205
- to_write .append (info_line )
206
- else :
207
- # may include levels names also
208
- str_index = self ._get_formatted_index ()
209
- str_columns = self ._get_formatted_column_labels ()
210
-
211
- stringified = []
212
-
213
- for i , c in enumerate (self .columns ):
214
- if self .header :
215
- fmt_values = self ._format_col (i )
216
- cheader = str_columns [i ]
217
- max_len = max (max (_strlen (x ) for x in fmt_values ),
218
- max (len (x ) for x in cheader ))
219
- if self .justify == 'left' :
220
- cheader = [x .ljust (max_len ) for x in cheader ]
221
- else :
222
- cheader = [x .rjust (max_len ) for x in cheader ]
223
- fmt_values = cheader + fmt_values
224
- stringified .append (_make_fixed_width (fmt_values ,
225
- self .justify ))
226
- else :
227
- stringified = [_make_fixed_width (self ._format_col (i ),
228
- self .justify )
229
- for i , c in enumerate (self .columns )]
204
+ stringified = []
230
205
231
- if self .index :
232
- to_write .append (adjoin (1 , str_index , * stringified ))
206
+ for i , c in enumerate (self .columns ):
207
+ if self .header :
208
+ fmt_values = self ._format_col (i )
209
+ cheader = str_columns [i ]
210
+ max_len = max (max (_strlen (x ) for x in fmt_values ),
211
+ max (len (x ) for x in cheader ))
212
+ if self .justify == 'left' :
213
+ cheader = [x .ljust (max_len ) for x in cheader ]
214
+ else :
215
+ cheader = [x .rjust (max_len ) for x in cheader ]
216
+ fmt_values = cheader + fmt_values
217
+ stringified .append (_make_fixed_width (fmt_values ,
218
+ self .justify ))
233
219
else :
234
- to_write .append (adjoin (1 , * stringified ))
220
+ stringified = [_make_fixed_width (self ._format_col (i ),
221
+ self .justify )
222
+ for i , c in enumerate (self .columns )]
223
+
224
+ strcols = stringified
225
+ if self .index :
226
+ strcols .insert (0 , str_index )
235
227
236
228
if not py3compat .PY3 :
237
229
if force_unicode :
238
- to_write = [ unicode ( s ) for s in to_write ]
230
+ strcols = map ( lambda col : map ( unicode , col ), strcols )
239
231
else :
240
232
# generally everything is plain strings, which has ascii
241
233
# encoding. problem is when there is a char with value over 127
242
234
# - everything then gets converted to unicode.
243
235
try :
244
- for s in to_write :
245
- str (s )
236
+ map (lambda col : map (str , col ), strcols )
246
237
except UnicodeError :
247
- to_write = [unicode (s ) for s in to_write ]
238
+ strcols = map (lambda col : map (unicode , col ), strcols )
239
+
240
+ return strcols
241
+
242
+ def to_string (self , force_unicode = False ):
243
+ """
244
+ Render a DataFrame to a console-friendly tabular output.
245
+ """
246
+ frame = self .frame
247
+
248
+ if len (frame .columns ) == 0 or len (frame .index ) == 0 :
249
+ info_line = (u'Empty %s\n Columns: %s\n Index: %s'
250
+ % (type (self .frame ).__name__ ,
251
+ frame .columns , frame .index ))
252
+ text = info_line
253
+ else :
254
+ strcols = self ._to_str_columns (force_unicode )
255
+ text = adjoin (1 , * strcols )
256
+
257
+ self .buf .writelines (text )
258
+
259
+ def to_latex (self , force_unicode = False , column_format = None ):
260
+ """
261
+ Render a DataFrame to a LaTeX tabular environment output.
262
+ """
263
+ frame = self .frame
264
+
265
+ if len (frame .columns ) == 0 or len (frame .index ) == 0 :
266
+ info_line = (u'Empty %s\n Columns: %s\n Index: %s'
267
+ % (type (self .frame ).__name__ ,
268
+ frame .columns , frame .index ))
269
+ strcols = [[info_line ]]
270
+ else :
271
+ strcols = self ._to_str_columns (force_unicode )
272
+
273
+ if column_format is None :
274
+ column_format = '|l|%s|' % '|' .join ('c' for _ in strcols )
275
+ else :
276
+ assert isinstance (column_format , str )
248
277
249
- self .buf .writelines (to_write )
278
+ self .buf .write ('\\ begin{tabular}{%s}\n ' % column_format )
279
+ self .buf .write ('\\ hline\n ' )
280
+
281
+ nlevels = frame .index .nlevels
282
+ for i , row in enumerate (izip (* strcols )):
283
+ if i == nlevels :
284
+ self .buf .write ('\\ hline\n ' ) # End of header
285
+ crow = [(x .replace ('_' , '\\ _' ).replace ('%' , '\\ %' ).replace ('&' , '\\ &' ) if x else '{}' ) for x in row ]
286
+ self .buf .write (' & ' .join (crow ))
287
+ self .buf .write (' \\ \\ \n ' )
288
+
289
+ self .buf .write ('\\ hline\n ' )
290
+ self .buf .write ('\\ end{tabular}\n ' )
250
291
251
292
def _format_col (self , i ):
252
293
col = self .columns [i ]
@@ -256,7 +297,7 @@ def _format_col(self, i):
256
297
na_rep = self .na_rep ,
257
298
space = self .col_space )
258
299
259
- def to_html (self ):
300
+ def to_html (self , classes = None ):
260
301
"""
261
302
Render a DataFrame to a html table.
262
303
"""
@@ -295,7 +336,13 @@ def write_tr(l, indent=0, indent_delta=4, header=False, align=None):
295
336
indent_delta = 2
296
337
frame = self .frame
297
338
298
- write ('<table border="1">' , indent )
339
+ _classes = ['dataframe' ] # Default class.
340
+ if classes is not None :
341
+ if isinstance (classes , str ):
342
+ classes = classes .split ()
343
+ assert isinstance (classes , (list , tuple ))
344
+ _classes .extend (classes )
345
+ write ('<table border="1" class="%s">' % ' ' .join (_classes ), indent )
299
346
300
347
def _column_header ():
301
348
if self .index :
@@ -364,13 +411,20 @@ def _maybe_bold_row(x):
364
411
fmt_values [i ] = self ._format_col (i )
365
412
366
413
# write values
414
+ index_formatter = self .formatters .get ('__index__' , None )
367
415
for i in range (len (frame )):
368
416
row = []
417
+
369
418
if self .index :
419
+ index_value = frame .index [i ]
420
+ if index_formatter :
421
+ index_value = index_formatter (index_value )
422
+
370
423
if isinstance (frame .index , MultiIndex ):
371
- row .extend (_maybe_bold_row (frame . index [ i ] ))
424
+ row .extend (_maybe_bold_row (index_value ))
372
425
else :
373
- row .append (_maybe_bold_row (frame .index [i ]))
426
+ row .append (_maybe_bold_row (index_value ))
427
+
374
428
for j in range (len (self .columns )):
375
429
row .append (fmt_values [j ][i ])
376
430
write_tr (row , indent , indent_delta )
@@ -425,6 +479,7 @@ def has_column_names(self):
425
479
return _has_names (self .frame .columns )
426
480
427
481
def _get_formatted_index (self ):
482
+ # Note: this is only used by to_string(), not by to_html().
428
483
index = self .frame .index
429
484
columns = self .frame .columns
430
485
0 commit comments