@@ -646,13 +646,17 @@ def _join_multiline(self, *strcols):
646
646
st = ed
647
647
return '\n \n ' .join (str_lst )
648
648
649
- def to_latex (self , column_format = None , longtable = False , encoding = None ):
649
+ def to_latex (self , column_format = None , longtable = False , encoding = None ,
650
+ multicolumn = False , multicolumn_format = None , multirow = False ):
650
651
"""
651
652
Render a DataFrame to a LaTeX tabular/longtable environment output.
652
653
"""
653
654
654
655
latex_renderer = LatexFormatter (self , column_format = column_format ,
655
- longtable = longtable )
656
+ longtable = longtable ,
657
+ multicolumn = multicolumn ,
658
+ multicolumn_format = multicolumn_format ,
659
+ multirow = multirow )
656
660
657
661
if encoding is None :
658
662
encoding = 'ascii' if compat .PY2 else 'utf-8'
@@ -820,11 +824,15 @@ class LatexFormatter(TableFormatter):
820
824
HTMLFormatter
821
825
"""
822
826
823
- def __init__ (self , formatter , column_format = None , longtable = False ):
827
+ def __init__ (self , formatter , column_format = None , longtable = False ,
828
+ multicolumn = False , multicolumn_format = None , multirow = False ):
824
829
self .fmt = formatter
825
830
self .frame = self .fmt .frame
826
831
self .column_format = column_format
827
832
self .longtable = longtable
833
+ self .multicolumn = multicolumn
834
+ self .multicolumn_format = multicolumn_format
835
+ self .multirow = multirow
828
836
829
837
def write_result (self , buf ):
830
838
"""
@@ -850,10 +858,15 @@ def get_col_type(dtype):
850
858
clevels = self .frame .columns .nlevels
851
859
strcols .pop (0 )
852
860
name = any (self .frame .index .names )
861
+ cname = any (self .frame .columns .names )
862
+ lastcol = self .frame .index .nlevels - 1
853
863
for i , lev in enumerate (self .frame .index .levels ):
854
864
lev2 = lev .format ()
855
865
blank = ' ' * len (lev2 [0 ])
856
- lev3 = [blank ] * clevels
866
+ if cname and i == lastcol :
867
+ lev3 = [x if x else '{}' for x in self .frame .columns .names ]
868
+ else :
869
+ lev3 = [blank ] * clevels
857
870
if name :
858
871
lev3 .append (lev .name )
859
872
for level_idx , group in itertools .groupby (
@@ -873,6 +886,9 @@ def get_col_type(dtype):
873
886
compat .string_types ): # pragma: no cover
874
887
raise AssertionError ('column_format must be str or unicode, not %s'
875
888
% type (column_format ))
889
+ multicolumn_format = self .multicolumn_format
890
+ if multicolumn_format is None :
891
+ multicolumn_format = get_option ("display.latex.multicolumn_format" )
876
892
877
893
if not self .longtable :
878
894
buf .write ('\\ begin{tabular}{%s}\n ' % column_format )
@@ -881,10 +897,15 @@ def get_col_type(dtype):
881
897
buf .write ('\\ begin{longtable}{%s}\n ' % column_format )
882
898
buf .write ('\\ toprule\n ' )
883
899
884
- nlevels = self .frame .columns .nlevels
900
+ ilevels = self .frame .index .nlevels
901
+ clevels = self .frame .columns .nlevels
902
+ nlevels = clevels
885
903
if any (self .frame .index .names ):
886
904
nlevels += 1
887
- for i , row in enumerate (zip (* strcols )):
905
+ strrows = list (zip (* strcols ))
906
+ clinebuf = []
907
+
908
+ for i , row in enumerate (strrows ):
888
909
if i == nlevels and self .fmt .header :
889
910
buf .write ('\\ midrule\n ' ) # End of header
890
911
if self .longtable :
@@ -906,8 +927,51 @@ def get_col_type(dtype):
906
927
if x else '{}' ) for x in row ]
907
928
else :
908
929
crow = [x if x else '{}' for x in row ]
930
+ if i < clevels and self .fmt .header and self .multicolumn :
931
+ row2 = list (crow [:ilevels ])
932
+ ncol = 1
933
+ coltext = ''
934
+
935
+ def append_col ():
936
+ if ncol > 1 :
937
+ row2 .append ('\\ multicolumn{{{0:d}}}{{{1:s}}}{{{2:s}}}'
938
+ .format (ncol , multicolumn_format ,
939
+ coltext .strip ()))
940
+ else :
941
+ row2 .append (coltext )
942
+ for c in crow [ilevels :]:
943
+ if c .strip ():
944
+ if coltext :
945
+ append_col ()
946
+ coltext = c
947
+ ncol = 1
948
+ else :
949
+ ncol += 1
950
+ if coltext :
951
+ append_col ()
952
+ crow = row2
953
+ if i >= nlevels and self .fmt .index and self .multirow :
954
+ for j in range (ilevels ):
955
+ if crow [j ].strip ():
956
+ nrow = 1
957
+ for r in strrows [i + 1 :]:
958
+ if not r [j ].strip ():
959
+ nrow += 1
960
+ else :
961
+ break
962
+ if nrow > 1 :
963
+ crow [j ] = '\\ multirow{{{0:d}}}{{*}}{{{1:s}}}' \
964
+ .format (nrow , crow [j ].strip ())
965
+ clinebuf .append ([nrow , j + 1 ])
909
966
buf .write (' & ' .join (crow ))
910
967
buf .write (' \\ \\ \n ' )
968
+ if self .multirow and i < len (strrows ) - 1 :
969
+ for cl in clinebuf :
970
+ cl [0 ] -= 1
971
+ if cl [0 ] == 0 :
972
+ buf .write ('\cline{{{0:d}-{1:d}}}\n ' .format (cl [1 ],
973
+ len (strcols )))
974
+ clinebuf = [x for x in clinebuf if x [0 ]]
911
975
912
976
if not self .longtable :
913
977
buf .write ('\\ bottomrule\n ' )
0 commit comments