def matrix2latex(matr, filename=None, *environments, **keywords):
    r'''
    Takes a python matrix or nested list and converts to a LaTeX table or matrix.
    Author: [email protected], inspired by the work of [email protected] who has written
    a `similar package for 
    matlab <http://www.mathworks.com/matlabcentral/fileexchange/4894-matrix2latex>`_

    The following packages and definitions are recommended in the latex preamble

    .. code-block:: latex

      \providecommand{\e}[1]{\ensuremath{\times 10^{#1}}} % scientific notation, 1\e{9} will print as 1x10^9
      \usepackage{amsmath} % needed for pmatrix
      \usepackage{booktabs} % Fancy tables
      ...
      \begin{document}
      ...

    :param list matr: The numpy matrix/array or a nested list to convert.

    :param str filename: File to place output, extension .tex is added automatically. File can be included in a LaTeX
      document by ``\input{filename}``. If filename is None
      or not a string it is ignored.

    :arg environments: A list specifing the begin and end block.
        Example: ``matrix2latex(m, None, "align*", "pmatrix")`` gives the matrix

        .. code-block:: latex

            \begin{align*}
                \begin{pmatrix}
                   1 & 2 \\
                   3 & 4
                \end{pmatrix}
            \end{align*}

        The default is generating a table using the ``table``, ``center`` and ``tabular``
        environment, hence
        ``matrix2latex(m, "test", "table", "center", "tabular" ...)``
        can be written as
        ``matrix2latex(m, "test", ...)``

    :key headerRow:
        A row at the top used to label the columns.
        Must be a list of strings. Can be a nested list for multiple headings.
        If two or more items are repeated, a multicolumn is inserted, so:
        ``headerRow=['a', 'a']``
        will produces ``\multicolumn{2}{c}{Item}`` with an appropriate cmidrule beneath.
        To avoid this behavior ensure each consecutive item is unique, for instance:
        ``headerRow=['a', 'a ']``
        will produces the expected ``a & a`` (note the space after the second ``a``).

    :key headerColumn:
        A column used to label the rows. 
        Must be a list of strings

    :key transpose:
        Flips the table around in case you messed up. Equivalent to
        ``matrix2latex(m.H, ...)``
        if m is a numpy matrix.

    :key caption:
        Use to define a caption for your table.
        Inserts ``\caption`` after ``\begin{center}``,
        note that without the center environment the caption is currently ignored.

    :key label:
        Used to insert ``\label{tab:...}`` after ``\end{tabular}``
        Default is filename without extension.

    :key format:
        Printf syntax format, e.g. ``$%.2f$``. Default is ``$%g$``.
        This format is then used for all the elements in the table.

    :key formatColumn:
        A list of printf-syntax formats, e.g. ``[$%.2f$, $%g$]``
        Must be of same length as the number of columns.
        Format i is then used for column i.
        This is useful if some of your data should be printed with more significant figures
        than other parts.

    :key alignment:
        Used as an option when tabular is given as enviroment.
        ``\begin{tabular}{alignment}``
        A latex alignment like ``c``, ``l`` or ``r``.
        Can be given either as one per column e.g. ``"ccc"``.
        Or if only a single character is given e.g. ``"c"``,
        it will produce the correct amount depending on the number of columns.
        Default is ``"r"``.

    :key position:
        Used for the table environment to specify the optional parameter "position specifier"
        Default is ``'[' + 'htp' + ']'``
        If you want to place your table manually, do not use the table environment.

    Note that many of these options only has an effect when typesetting a table,
    if the correct environment is not given the arguments are simply ignored.
    
    :return str table:
      Returns the latex formated output as a string.
    '''
    headerRow = None
    headerColumn = None

    #
    # Convert to list
    #
    # If pandas
    try:
        headerColumn = list(matr.index)
    except (AttributeError, TypeError):
        pass
    try:
        headerRow = [list(matr.columns)]
    except (AttributeError, TypeError):
        pass
    try:
        matr = matr.to_records(index=False)
    except AttributeError:
        pass
    # If numpy (vops: must be placed below pandas check)
    try:
        matr = matr.tolist()
    except AttributeError:
        pass # lets hope it looks like a list

    #
    # Define matrix-size
    # 
    m = len(matr)
    try:
        n = len(matr[0]) # may raise TypeError
        for row in matr:
            n = max(n, len(row)) # keep max length
    except TypeError: # no length in this dimension (vector...)
        # convert [1, 2] to [[1], [2]]
        newMatr = list()
        [newMatr.append([matr[ix]]) for ix in range(m)]
        matr = newMatr
        m = len(matr)
        n = len(matr[0])
    except IndexError:
        m = 0
        n = 0
    #assert m > 0 and n > 0, "Expected positive matrix dimensions, got %g by %g matrix" % (m, n)
#   Bug with transpose:
#     # If header and/or column labels are longer use those lengths
#     try:
#         m = max(m, len(keywords['headerColumn'])) # keep max length
#     except KeyError:
#         pass
#     try:
#         n = max(n, len(keywords['headerRow'])) # keep max length
#     except KeyError:
#         pass
    
    #
    # Default values
    #

    # Keywords
    formatNumber = "$%g$"
    formatColumn = None
    if n != 0:
        alignment = "c"*n               # cccc
    else:
        alignment = "c"

    caption = None
    label = None
    position = "htp"            # position specifier for floating table environment

    # 
    # Conflicts
    #
    if "format" in keywords and "formatColumn" in keywords:
        warnings.warn('Specifying both format and formatColumn is not supported, using formatColumn')
        del keywords["format"]
        
    #
    # User-defined values
    # 
    for key in keywords:
        value = keywords[key]
        if key == "format":
            assertKeyFormat(value)
            formatNumber = value
            formatColumn = None         # never let both formatColumn and formatNumber to be defined
        elif key == "formatColumn":
            formatColumn = value
            formatNumber = None
        elif key == "alignment":
            if len(value) == 1:
                alignment = value*n # rrrr
            else:
                alignment = value
            assertKeyAlignment(alignment, n)
        elif key == "headerRow":
            if value == None:
                headerRow = None
            else:
                if not(type(value[0]) == list):
                    value = [value]         # just one header
                #assertListString(value, "headerRow") # todo: update
                headerRow = list(value)
        elif key == "headerColumn":
            if value == None:
                headerColumn = None
            else:
                assertListString(value, "headerColumn")
                headerColumn = list(value)
        elif key == "caption":
            assertStr(value, "caption")
            caption = value
        elif key == "label":
            assertStr(value, "label")
            if value.startswith('tab:'):
                label = value[len('tab:'):] # this will be added later in the code, avoids 'tab:tab:' as label
            else:
                label = value
        elif key == "filename":
            assertStr(value, "filename")
            filename = value
        elif key == "position":
            assertStr(value, "position")
            position = value
        elif key == "environments":
            environments = value
        elif key == "transpose":
            newMatr = list(zip(*matr))
#             for j in range(0, n):
#                 row = list()
#                 for i in range(0, m):
#                     row.append(matr[i][j])
#                 newMatr.append(row)
            copyKeywords = dict(keywords) # can't del original since we are inside for loop.
            del copyKeywords['transpose']
            # Recursion!
            return matrix2latex(newMatr, filename, *environments, **copyKeywords)
        else:
            raise ValueError("Error: key not recognized '%s'" % key)

    if headerColumn != None:
        alignment = "r" + alignment

    # Environments
    if environments is None:    # environments=None passed, do not add any environments.
        environments = []
    elif len(environments) == 0: # no environment give, assume table
        environments = ("table", "center", "tabular")

    if formatColumn == None:
        formatColumn = list()
        for j in range(0, n):
            formatColumn.append(formatNumber)

    if headerColumn != None and headerRow != None and len(headerRow[0]) == n:
        for i in range(len(headerRow)):
            headerRow[i].insert(0, "")

    # 
    # Set outputFile
    # 
    f = None
    if isinstance(filename, str) and filename != '':
        if not filename.endswith('.tex'): # assure propper file extension
            filename += '.tex'
        f = open(filename, 'w')
        if label == None:
            label = os.path.basename(filename) # get basename
            label = label[:-len(".tex")]  # remove extension

    f = IOString(f)
    #
    # Begin block
    # 
    for ixEnv in range(0, len(environments)):
        f.write("\t"*ixEnv)
        f.write(r"\begin{%s}" % environments[ixEnv])
        # special environments:
        if environments[ixEnv] == "table":
            f.write("[" + position + "]")
        elif environments[ixEnv] == "center":
            if caption != None:
                f.write("\n"+"\t"*ixEnv)
                f.write(r"\caption{%s}" % fix(caption))
            if label != None:
                f.write("\n"+"\t"*ixEnv)
                f.write(r"\label{tab:%s}" % label)
        elif environments[ixEnv] in table_alignment:
            f.write("{" + alignment + "}\n")
            f.write("\t"*ixEnv)
            f.write(r"\toprule")
        elif environments[ixEnv] in matrix_alignment:
            f.write("[" + alignment[0] + "]\n") #These environment you can add
        # newline
        f.write("\n")
    tabs = len(environments)            # number of \t to use

    # 
    # Table block
    # 

    # Row labels
    if headerRow != None:
        for row in range(len(headerRow)): # for each header
            i = 0
            start, end = list(), list() # of cmidrule
            f.write("\t"*tabs)    
            while i < len(headerRow[row]): # for each element (skipping repeating ones)
                j = 1
                # check for legal index then check if current element is equal to next (repeating)
                repeating = i+j < len(headerRow[row]) and headerRow[row][i] == headerRow[row][i + j]
                if repeating:
                    while repeating:        # figure out how long it repeats (j)
                        j += 1
                        repeating = i+j < len(headerRow[row]) and headerRow[row][i] == headerRow[row][i + j]
                    f.write(r'\multicolumn{%d}{c}{%s}' % (j, headerRow[row][i])) # multicol heading
                    start.append(i);end.append(j+i)
                    i += j                 # skip ahed
                else:
                    f.write('{%s}' % headerRow[row][i]) # normal heading
                    i += 1
                if i < len(headerRow[row]): # if not last element
                    f.write(' & ')
                    
            f.write(r'\\')
            for s, e in zip(start, end):
                f.write(r'\cmidrule(r){%d-%d}' % (s+1, e))
            f.write('\n')
        if len(start) == 0:             # do not use if cmidrule is used on last header
            f.write('\t'*tabs)
            f.write('\\midrule\n')

    # Values
    for i in range(0, m):
        f.write("\t"*tabs)
        for j in range(0, n):

            if j == 0:                  # first row
                if headerColumn != None:
                    try:
                        f.write("{%s} & " % headerColumn[i])
                    except IndexError:
                        f.write('&')

            try: # get current element
                if '%s' not in formatColumn[j]:
                    try:
                        e = float(matr[i][j]) # current element
                    except ValueError: # can't convert to float, use string
                        formatColumn[j] = '%s'
                        e = matr[i][j]
                    except TypeError:       # raised for None
                        e = None
                else:
                    e = matr[i][j]
            except IndexError:
                e = None
                
            if e == None or isnan(e):#e == float('NaN'):
                f.write("{-}")
            elif e == float('inf'):
                f.write(r"$\infty$")
            elif e == float('-inf'):
                f.write(r"$-\infty$")                
            else:
                fcj = formatColumn[j]

                formated = fcj % e
                formated = fix(formated, table=True) # fix 1e+2
                f.write('%s' % formated)
            if j != n-1:                # not last row
                f.write(" & ")
            else:                       # last row
                f.write(r"\\")
                f.write("\n")

    #
    # End block
    #
    for ixEnv in range(0, len(environments)):
        ixEnv = len(environments)-1 - ixEnv # reverse order
        # special environments:
        if environments[ixEnv] == "center":
            pass
        elif environments[ixEnv] == "tabular":
            f.write("\t"*ixEnv)
            f.write(r"\bottomrule"+"\n")
        f.write("\t"*ixEnv)
        f.write(r"\end{%s}" % environments[ixEnv])
        if ixEnv != 0:
            f.write("\n")

    f.close()
    return f.__str__()
def matrix2latex(matr, filename=None, *environments, **keywords):
    r'''
A pdf version of this documentation is available as doc<date>.pdf
Takes a python matrix or nested list and converts to a LaTeX table or matrix.
Author: [email protected], inspired by the work of [email protected] who has written
a similar package for matlab
\url{http://www.mathworks.com/matlabcentral/fileexchange/4894-matrix2latex}

This software is published under the GNU GPL, by the free software
foundation. For further reading see: 
http://www.gnu.org/licenses/licenses.html#GPL

The following packages and definitions are recommended in the latex preamble 
% scientific notation, 1\e{9} will print as 1x10^9
\providecommand{\e}[1]{\ensuremath{\times 10^{#1}}}
\usepackage{amsmath} % needed for pmatrix
\usepackage{booktabs} % Fancy tables
...
\begin{document}
...

Arguments:
  
matrix
  A numpy matrix or a nested list

Filename
  File to place output, extension .tex is added automatically. File can be included in a LaTeX
  document by \input{filename}. Output will always be returned in a string. If filename is None
  or not a string it is ignored.
  
*environments
  Use 
matrix2latex(m, None, "align*", "pmatrix", ...) for matrix.
  This will give
  \begin{align*}
    \begin{pmatrix}
      1 & 2 \\
      3 & 4
    \end{pmatrix}
  \end{align*}
  Use 
matrix2latex(m, "test", "table", "center", "tabular" ...) for table.
  Table is default so given no arguments: table, center and tabular will be used.
  The above command is then equivalent to \\
matrix2latex(m, "test", ...)

Example

  from matrix2latex import matrix2latex
  m = [[1, 2, 3], [1, 4, 9]] # python nested list
  t = matrix2latex(m)
  print t

\begin{lstlisting}
  \begin{table}[ht]
    \begin{center}
      \begin{tabular}{cc}
        \toprule
        $1$ & $1$\\
        $2$ & $4$\\
        $3$ & $9$\\
        \bottomrule
      \end{tabular}
    \end{center}
  \end{table}
\end{lstlisting}

**keywords
headerRow
    A row at the top used to label the columns.
    Must be a list of strings.

Using the same example from above we can add row labels

rl = ['$x$', '$x^2$']
t = matrix2latex(m, headerRow=rl)

headerColumn
    A column used to label the rows.
    Must be a list of strings

transpose
Flips the table around in case you messed up. Equivalent to
matrix2latex(m.H, ...)
if m is a numpy matrix.
Note the use of headerColumn in the example.
cl = ['$x$', '$x^2$']
t = matrix2latex(m, headerColumn=cl, transpose=True)

caption
    Use to define a caption for your table.
    Inserts \verb!\caption! after \verb!\end{tabular}!.
Always use informative captions!

t = matrix2latex(m, headerRow=rl, 
                 caption='Nice table!')

label
Used to insert \verb!\label{tab:...}! after \verb!\end{tabular}!
Default is filename without extension.

We can use label='niceTable' but if we save it to file
the default label is the filename, so:

matrix2latex(m, 'niceTable', headerRow=rl, 
                 caption='Nice table!')

can be referenced by \verb!\ref{tab:niceTable}!. Table \ref{tab:niceTable}
was included in latex by \verb!\input{niceTable}!.

format
Printf syntax format, e.g. $%.2f$. Default is $%g$.
  This format is then used for all the elements in the table.

m = [[1, 2, 3], [1, 1/2, 1/3]]
rl = ['$x$', '$1/x$']
t = matrix2latex(m, headerRow=rl,
                 format='%.2f')

formatColumn
A list of printf-syntax formats, e.g. [$%.2f$, $%g$]
Must be of same length as the number of columns.
Format i is then used for column i.
This is useful if some of your data should be printed with more significant figures
than other parts

t = matrix2latex(m, headerRow=rl,
                 formatColumn=['%g', '%.2f'])

alignment
Used as an option when tabular is given as enviroment.
\verb!\begin{tabular}{alignment}!
A latex alignment like c, l or r.
Can be given either as one per column e.g. "ccc".
Or if only a single character is given e.g. "c",
it will produce the correct amount depending on the number of columns.
Default is "r".

Note that many of these options only has an effect when typesetting a table,
if the correct environment is not given the arguments are simply ignored.

The options presented by this program represents what I need when creating a table,
if you need a more sophisticated table you must either change the python code
(feel free to submit a patch) or manually adjust the output afterwards.
\url{http://en.wikibooks.org/wiki/LaTeX/Tables} gives an excellent overview
of some advanced table techniques.
    '''
    #
    # Convert to list
    #
    try:
        matr = matr.tolist()
    except AttributeError:
        pass  # lets hope it looks like a list

    #
    # Define matrix-size
    #
    m = len(matr)
    try:
        n = len(matr[0])
    except TypeError:  # no length in this dimension (vector...)
        # convert [1, 2] to [[1], [2]]
        newMatr = list()
        [newMatr.append([matr[ix]]) for ix in range(m)]
        matr = newMatr
        m = len(matr)
        n = len(matr[0])
    except IndexError:
        m = 0
        n = 0
    #assert m > 0 and n > 0, "Expected positive matrix dimensions, got %g by %g matrix" % (m, n)

    #
    # Default values
    #

    # Keywords
    formatNumber = "$%g$"
    formatColumn = None
    if n != 0:
        alignment = "c" * n  # cccc
    else:
        alignment = "c"

    headerRow = None
    headerColumn = None
    caption = None
    label = None

    #
    # Conflicts
    #
    if "format" in keywords and "formatColumn" in keywords:
        print(
            'Using both format and formatColumn is not supported, using formatColumn'
        )
        del keywords["format"]

    #
    # User-defined values
    #
    for key in keywords:
        value = keywords[key]
        if key == "format":
            assertKeyFormat(value)
            formatNumber = value
            formatColumn = None  # never let both formatColumn and formatNumber to be defined
        elif key == "formatColumn":
            formatColumn = value
            formatNumber = None
        elif key == "alignment":
            if len(value) == 1:
                alignment = value * n  # rrrr
            else:
                alignment = value
            assertKeyAlignment(alignment, n)
        elif key == "headerRow":
            assertListString(value, "headerRow")
            headerRow = list(value)
        elif key == "headerColumn":
            assertListString(value, "headerColumn")
            headerColumn = list(value)
        elif key == "caption":
            assertStr(value, "caption")
            caption = value
        elif key == "label":
            assertStr(value, "label")
            if value.startswith('tab:'):
                label = value[len(
                    'tab:'
                ):]  # this will be added later in the code, avoids 'tab:tab:' as label
            else:
                label = value
        elif key == "transpose":
            newMatr = list()
            for j in range(0, n):
                row = list()
                for i in range(0, m):
                    row.append(matr[i][j])
                newMatr.append(row)
            copyKeywords = dict(
                keywords)  # can't del original since we are inside for loop.
            del copyKeywords['transpose']
            # Recursion!
            return matrix2latex(newMatr, filename, *environments,
                                **copyKeywords)
        else:
            sys.stderr.write("Error: key not recognized '%s'\n" % key)
            sys.exit(2)

    if "headerColumn" in keywords:
        alignment = "r" + alignment

    # Environments
    if len(environments) == 0:  # no environment give, assume table
        environments = ("table", "center", "tabular")

    if formatColumn == None:
        formatColumn = list()
        for j in range(0, n):
            formatColumn.append(formatNumber)

    if headerColumn != None and headerRow != None and len(headerRow) == n:
        headerRow.insert(0, "")

    #
    # Set outputFile
    #
    f = None
    if isinstance(filename, str) and filename != '':
        if not filename.endswith('.tex'):  # assure propper file extension
            filename += '.tex'
        f = open(filename, 'w')
        if label == None:
            label = os.path.basename(filename)  # get basename
            label = label.replace(
                ".tex",
                "")  # remove extension. TODO: bug with filename=foo.texFoo.tex

    f = IOString(f)
    #
    # Begin block
    #
    for ixEnv in range(0, len(environments)):
        f.write("\t" * ixEnv)
        f.write(r"\begin{%s}" % environments[ixEnv])
        # special environments:
        if environments[ixEnv] == "table":
            f.write("[htbp]")
        elif environments[ixEnv] == "center":
            if caption != None:
                f.write("\n" + "\t" * ixEnv)
                f.write(r"\caption{%s}" % fixEngineeringNotation.fix(caption))
            if label != None:
                f.write("\n" + "\t" * ixEnv)
                f.write(r"\label{tab:%s}" % label)
        elif environments[ixEnv] == "tabular":
            f.write("{" + alignment + "}\n")
            f.write("\t" * ixEnv)
            f.write(r"\toprule")
        # newline
        f.write("\n")
    tabs = len(environments)  # number of \t to use

    #
    # Table block
    #

    # Row labels
    if headerRow != None:
        f.write("\t" * tabs)
        for j in range(0, len(headerRow)):
            f.write(r"%s" % headerRow[j])
            if j != len(headerRow) - 1:
                f.write(" & ")
            else:
                f.write(r"\\" + "\n")
                f.write("\t" * tabs)
                f.write(r"\midrule" + "\n")

    # Values
    for i in range(0, m):
        f.write("\t" * tabs)
        for j in range(0, n):

            if j == 0:  # first row
                if headerColumn != None:
                    f.write("%s & " % headerColumn[i])

            if '%s' not in formatColumn[j]:
                try:
                    e = float(matr[i][j])  # current element
                except ValueError:  # can't convert to float, use string
                    e = matr[i][j]
                    formatColumn[j] = '%s'
                except TypeError:  # raised for None
                    e = None
            else:
                e = matr[i][j]

            if e == None or math.isnan(e):
                f.write("-")
            elif e == float('inf'):
                f.write(r"$\infty$")
            elif e == float('-inf'):
                f.write(r"$-\infty$")
            else:
                fcj = formatColumn[j]
                formated = fcj % e
                formated = fixEngineeringNotation.fix(formated,
                                                      table=True)  # fix 1e+2
                f.write(formated)
            if j != n - 1:  # not last row
                f.write(" & ")
            else:  # last row
                f.write(r"\\")
                f.write("\n")

    #
    # End block
    #
    for ixEnv in range(0, len(environments)):
        ixEnv = len(environments) - 1 - ixEnv  # reverse order
        # special environments:
        if environments[ixEnv] == "center":
            pass
        elif environments[ixEnv] == "tabular":
            f.write("\t" * ixEnv)
            f.write(r"\bottomrule" + "\n")
        f.write("\t" * ixEnv)
        f.write(r"\end{%s}" % environments[ixEnv])
        if ixEnv != 0:
            f.write("\n")

    f.close()
    return f.__str__()
Beispiel #3
0
def matrix2latex(matr, filename=None, *environments, **keywords):
    r'''
A pdf version of this documentation is available as doc<date>.pdf
Takes a python matrix or nested list and converts to a LaTeX table or matrix.
Author: [email protected], inspired by the work of [email protected] who has written
a similar package for matlab
\url{http://www.mathworks.com/matlabcentral/fileexchange/4894-matrix2latex}

This software is published under the GNU GPL, by the free software
foundation. For further reading see: 
http://www.gnu.org/licenses/licenses.html#GPL

The following packages and definitions are recommended in the latex preamble 
% scientific notation, 1\e{9} will print as 1x10^9
\providecommand{\e}[1]{\ensuremath{\times 10^{#1}}}
\usepackage{amsmath} % needed for pmatrix
\usepackage{booktabs} % Fancy tables
...
\begin{document}
...

Arguments:
  
matrix
  A numpy matrix or a nested list

Filename
  File to place output, extension .tex is added automatically. File can be included in a LaTeX
  document by \input{filename}. Output will always be returned in a string. If filename is None
  or not a string it is ignored.
  
*environments
  Use 
matrix2latex(m, None, "align*", "pmatrix", ...) for matrix.
  This will give
  \begin{align*}
    \begin{pmatrix}
      1 & 2 \\
      3 & 4
    \end{pmatrix}
  \end{align*}
  Use 
matrix2latex(m, "test", "table", "center", "tabular" ...) for table.
  Table is default so given no arguments: table, center and tabular will be used.
  The above command is then equivalent to \\
matrix2latex(m, "test", ...)

Example

  from matrix2latex import matrix2latex
  m = [[1, 2, 3], [1, 4, 9]] # python nested list
  t = matrix2latex(m)
  print t

\begin{lstlisting}
  \begin{table}[ht]
    \begin{center}
      \begin{tabular}{cc}
        \toprule
        $1$ & $1$\\
        $2$ & $4$\\
        $3$ & $9$\\
        \bottomrule
      \end{tabular}
    \end{center}
  \end{table}
\end{lstlisting}

**keywords
headerRow
    A row at the top used to label the columns.
    Must be a list of strings.

Using the same example from above we can add row labels

rl = ['$x$', '$x^2$']
t = matrix2latex(m, headerRow=rl)

headerColumn
    A column used to label the rows.
    Must be a list of strings

transpose
Flips the table around in case you messed up. Equivalent to
matrix2latex(m.H, ...)
if m is a numpy matrix.
Note the use of headerColumn in the example.
cl = ['$x$', '$x^2$']
t = matrix2latex(m, headerColumn=cl, transpose=True)

caption
    Use to define a caption for your table.
    Inserts \verb!\caption! after \verb!\end{tabular}!.
Always use informative captions!

t = matrix2latex(m, headerRow=rl, 
                 caption='Nice table!')

label
Used to insert \verb!\label{tab:...}! after \verb!\end{tabular}!
Default is filename without extension.

We can use label='niceTable' but if we save it to file
the default label is the filename, so:

matrix2latex(m, 'niceTable', headerRow=rl, 
                 caption='Nice table!')

can be referenced by \verb!\ref{tab:niceTable}!. Table \ref{tab:niceTable}
was included in latex by \verb!\input{niceTable}!.

format
Printf syntax format, e.g. $%.2f$. Default is $%g$.
  This format is then used for all the elements in the table.

m = [[1, 2, 3], [1, 1/2, 1/3]]
rl = ['$x$', '$1/x$']
t = matrix2latex(m, headerRow=rl,
                 format='%.2f')

formatColumn
A list of printf-syntax formats, e.g. [$%.2f$, $%g$]
Must be of same length as the number of columns.
Format i is then used for column i.
This is useful if some of your data should be printed with more significant figures
than other parts

t = matrix2latex(m, headerRow=rl,
                 formatColumn=['%g', '%.2f'])

alignment
Used as an option when tabular is given as enviroment.
\verb!\begin{tabular}{alignment}!
A latex alignment like c, l or r.
Can be given either as one per column e.g. "ccc".
Or if only a single character is given e.g. "c",
it will produce the correct amount depending on the number of columns.
Default is "r".

Note that many of these options only has an effect when typesetting a table,
if the correct environment is not given the arguments are simply ignored.

The options presented by this program represents what I need when creating a table,
if you need a more sophisticated table you must either change the python code
(feel free to submit a patch) or manually adjust the output afterwards.
\url{http://en.wikibooks.org/wiki/LaTeX/Tables} gives an excellent overview
of some advanced table techniques.
    '''
    #
    # Convert to list
    #
    try:
        matr = matr.tolist()
    except AttributeError:
        pass # lets hope it looks like a list

    #
    # Define matrix-size
    # 
    m = len(matr)
    try:
        n = len(matr[0])
    except TypeError: # no length in this dimension (vector...)
        # convert [1, 2] to [[1], [2]]
        newMatr = list()
        [newMatr.append([matr[ix]]) for ix in range(m)]
        matr = newMatr
        m = len(matr)
        n = len(matr[0])
    except IndexError:
        m = 0
        n = 0
    #assert m > 0 and n > 0, "Expected positive matrix dimensions, got %g by %g matrix" % (m, n)
    
    #
    # Default values
    #

    # Keywords
    formatNumber = "$%g$"
    formatColumn = None
    if n != 0:
        alignment = "c"*n               # cccc
    else:
        alignment = "c"

    headerRow = None
    headerColumn = None
    caption = None
    label = None

    # 
    # Conflicts
    #
    if "format" in keywords and "formatColumn" in keywords:
        print('Using both format and formatColumn is not supported, using formatColumn')
        del keywords["format"]
        
    #
    # User-defined values
    # 
    for key in keywords:
        value = keywords[key]
        if key == "format":
            assertKeyFormat(value)
            formatNumber = value
            formatColumn = None         # never let both formatColumn and formatNumber to be defined
        elif key == "formatColumn":
            formatColumn = value
            formatNumber = None
        elif key == "alignment":
            if len(value) == 1:
                alignment = value*n # rrrr
            else:
                alignment = value
            assertKeyAlignment(alignment, n)
        elif key == "headerRow":
            assertListString(value, "headerRow")
            headerRow = list(value)
        elif key == "headerColumn":
            assertListString(value, "headerColumn")
            headerColumn = list(value)
        elif key == "caption":
            assertStr(value, "caption")
            caption = value
        elif key == "label":
            assertStr(value, "label")
            if value.startswith('tab:'):
                label = value[len('tab:'):] # this will be added later in the code, avoids 'tab:tab:' as label
            else:
                label = value
        elif key == "transpose":
            newMatr = list()
            for j in range(0, n):
                row = list()
                for i in range(0, m):
                    row.append(matr[i][j])
                newMatr.append(row)
            copyKeywords = dict(keywords) # can't del original since we are inside for loop.
            del copyKeywords['transpose']
            # Recursion!
            return matrix2latex(newMatr, filename, *environments, **copyKeywords)
        else:
            sys.stderr.write("Error: key not recognized '%s'\n" % key)
            sys.exit(2)
            
    if "headerColumn" in keywords:
        alignment = "r" + alignment

    # Environments
    if len(environments) == 0:          # no environment give, assume table
        environments = ("table", "center", "tabular")

    if formatColumn == None:
        formatColumn = list()
        for j in range(0, n):
            formatColumn.append(formatNumber)

    if headerColumn != None and headerRow != None and len(headerRow) == n:
        headerRow.insert(0, "")

    # 
    # Set outputFile
    # 
    f = None
    if isinstance(filename, str) and filename != '':
        if not filename.endswith('.tex'): # assure propper file extension
            filename += '.tex'
        f = open(filename, 'w')
        if label == None:
            label = os.path.basename(filename) # get basename
            label = label.replace(".tex", "")  # remove extension. TODO: bug with filename=foo.texFoo.tex

    f = IOString(f)
    #
    # Begin block
    # 
    for ixEnv in range(0, len(environments)):
        f.write("\t"*ixEnv)
        f.write(r"\begin{%s}" % environments[ixEnv])
        # special environments:
        if environments[ixEnv] == "table":
            f.write("[htbp]")
        elif environments[ixEnv] == "center":
            if caption != None:
                f.write("\n"+"\t"*ixEnv)
                f.write(r"\caption{%s}" % fixEngineeringNotation.fix(caption))
            if label != None:
                f.write("\n"+"\t"*ixEnv)
                f.write(r"\label{tab:%s}" % label)
        elif environments[ixEnv] == "tabular":
            f.write("{" + alignment + "}\n")
            f.write("\t"*ixEnv)
            f.write(r"\toprule")
        # newline
        f.write("\n")
    tabs = len(environments)            # number of \t to use

    # 
    # Table block
    # 

    # Row labels
    if headerRow != None:
        f.write("\t"*tabs)
        for j in range(0, len(headerRow)):
            f.write(r"%s" % headerRow[j])
            if j != len(headerRow)-1:
                f.write(" & ")
            else:
                f.write(r"\\"+ "\n")
                f.write("\t"*tabs)
                f.write(r"\midrule" + "\n")
                
    # Values
    for i in range(0, m):
        f.write("\t"*tabs)
        for j in range(0, n):

            if j == 0:                  # first row
                if headerColumn != None:
                    f.write("%s & " % headerColumn[i])
                    
            if '%s' not in formatColumn[j]:
                try:
                    e = float(matr[i][j])            # current element
                except ValueError: # can't convert to float, use string
                    e = matr[i][j]
                    formatColumn[j] = '%s'
                except TypeError:       # raised for None
                    e = None
            else:
                e = matr[i][j]

            if e == None or math.isnan(e):
                f.write("-")
            elif e == float('inf'):
                f.write(r"$\infty$")
            elif e == float('-inf'):
                f.write(r"$-\infty$")                
            else:
                fcj = formatColumn[j]
                formated = fcj % e
                formated = fixEngineeringNotation.fix(formated, table=True) # fix 1e+2
                f.write(formated)
            if j != n-1:                # not last row
                f.write(" & ")
            else:                       # last row
                f.write(r"\\")
                f.write("\n")

    #
    # End block
    #
    for ixEnv in range(0, len(environments)):
        ixEnv = len(environments)-1 - ixEnv # reverse order
        # special environments:
        if environments[ixEnv] == "center":
            pass
        elif environments[ixEnv] == "tabular":
            f.write("\t"*ixEnv)
            f.write(r"\bottomrule"+"\n")
        f.write("\t"*ixEnv)
        f.write(r"\end{%s}" % environments[ixEnv])
        if ixEnv != 0:
            f.write("\n")

    f.close()
    return f.__str__()
Beispiel #4
0
def matrix2latex(matr, filename=None, *environments, **keywords):
    r'''
A detailed pdf version of this documentation is available as doc<date>.pdf
Takes a python matrix or nested list and converts to a LaTeX table or matrix.
Author: [email protected], inspired by the work of [email protected] who has written
a similar package for matlab
\url{http://www.mathworks.com/matlabcentral/fileexchange/4894-matrix2latex}

The following packages and definitions are recommended in the latex preamble 
\providecommand{\e}[1]{\ensuremath{\times 10^{#1}}} % scientific notation, 1\e{9} will print as 1x10^9
\usepackage{amsmath} % needed for pmatrix
\usepackage{booktabs} % Fancy tables
...
\begin{document}
...

Arguments:
  
matrix
  A numpy matrix or a nested list

Filename
  File to place output, extension .tex is added automatically. File can be included in a LaTeX
  document by \input{filename}. Output will always be returned in a string. If filename is None
  or not a string it is ignored.
  
*environments
A list specifing the begin and end block.
  Use 
matrix2latex(m, None, "align*", "pmatrix", ...) for matrix.
  This will give
  \begin{align*}
    \begin{pmatrix}
      1 & 2 \\
      3 & 4
    \end{pmatrix}
  \end{align*}
  Use 
matrix2latex(m, "test", "table", "center", "tabular" ...) for table.
  Table is default so given no arguments: table, center and tabular will be used.
  The above command is then equivalent to \\
matrix2latex(m, "test", ...)

**keywords
headerRow
    A row at the top used to label the columns.
    Must be a list of strings. Can be a nested list for multiple headings.
    If two or more items are repeated, a multicolumn is inserted, so:
    headerRow=['a', 'a']
    will produces "\multicolumn{2}{c}{Item}" with an appropriate cmidrule beneath.
    To avoid this behavior ensure each consecutive item is unique, for instance:
    headerRow=['a', 'a ']
    will produces the expected "a & a".

headerColumn
    A column used to label the rows.
    Must be a list of strings

transpose
    Flips the table around in case you messed up. Equivalent to
    matrix2latex(m.H, ...)
    if m is a numpy matrix.

caption
    Use to define a caption for your table.
    Inserts \caption after \begin{center},
    note that without the center environment the caption is currently ignored.
    
label
    Used to insert \verb!\label{tab:...}! after \verb!\end{tabular}!
    Default is filename without extension.

format
    Printf syntax format, e.g. $%.2f$. Default is $%g$.
    This format is then used for all the elements in the table.

formatColumn
    A list of printf-syntax formats, e.g. [$%.2f$, $%g$]
    Must be of same length as the number of columns.
    Format i is then used for column i.
    This is useful if some of your data should be printed with more significant figures
    than other parts

alignment
    Used as an option when tabular is given as enviroment.
    \verb!\begin{tabular}{alignment}!
    A latex alignment like c, l or r.
    Can be given either as one per column e.g. "ccc".
    Or if only a single character is given e.g. "c",
    it will produce the correct amount depending on the number of columns.
    Default is "r".

position
    Used for the table environment to specify the optional parameter "position specifier"
    Default is '[' + 'htp' + ']'
    If you want to place your table manually, do not use the table environment.

Note that many of these options only has an effect when typesetting a table,
if the correct environment is not given the arguments are simply ignored.
    '''
    headerRow = None
    headerColumn = None

    #
    # Convert to list
    #
    # If pandas
    try:
        headerColumn = list(matr.index)
    except (AttributeError, TypeError):
        pass
    try:
        headerRow = [list(matr.columns)]
    except (AttributeError, TypeError):
        pass
    try:
        matr = matr.to_records(index=False)
    except AttributeError:
        pass
    # If numpy (vops: must be placed below pandas check)
    try:
        matr = matr.tolist()
    except AttributeError:
        pass  # lets hope it looks like a list

    #
    # Define matrix-size
    #
    m = len(matr)
    try:
        n = len(matr[0])  # may raise TypeError
        for row in matr:
            n = max(n, len(row))  # keep max length
    except TypeError:  # no length in this dimension (vector...)
        # convert [1, 2] to [[1], [2]]
        newMatr = list()
        [newMatr.append([matr[ix]]) for ix in range(m)]
        matr = newMatr
        m = len(matr)
        n = len(matr[0])
    except IndexError:
        m = 0
        n = 0
    #assert m > 0 and n > 0, "Expected positive matrix dimensions, got %g by %g matrix" % (m, n)
#   Bug with transpose:
#     # If header and/or column labels are longer use those lengths
#     try:
#         m = max(m, len(keywords['headerColumn'])) # keep max length
#     except KeyError:
#         pass
#     try:
#         n = max(n, len(keywords['headerRow'])) # keep max length
#     except KeyError:
#         pass

#
# Default values
#

# Keywords
    formatNumber = "$%g$"
    formatColumn = None
    if n != 0:
        alignment = "c" * n  # cccc
    else:
        alignment = "c"

    caption = None
    label = None
    position = "htp"  # position specifier for floating table environment

    #
    # Conflicts
    #
    if "format" in keywords and "formatColumn" in keywords:
        print(
            'Using both format and formatColumn is not supported, using formatColumn'
        )
        del keywords["format"]

    #
    # User-defined values
    #
    for key in keywords:
        value = keywords[key]
        if key == "format":
            assertKeyFormat(value)
            formatNumber = value
            formatColumn = None  # never let both formatColumn and formatNumber to be defined
        elif key == "formatColumn":
            formatColumn = value
            formatNumber = None
        elif key == "alignment":
            if len(value) == 1:
                alignment = value * n  # rrrr
            else:
                alignment = value
            assertKeyAlignment(alignment, n)
        elif key == "headerRow":
            if value == None:
                headerRow = None
            else:
                if not (type(value[0]) == list):
                    value = [value]  # just one header
                #assertListString(value, "headerRow") # todo: update
                headerRow = list(value)
        elif key == "headerColumn":
            if value == None:
                headerColumn = None
            else:
                assertListString(value, "headerColumn")
                headerColumn = list(value)
        elif key == "caption":
            assertStr(value, "caption")
            caption = value
        elif key == "label":
            assertStr(value, "label")
            if value.startswith('tab:'):
                label = value[len(
                    'tab:'
                ):]  # this will be added later in the code, avoids 'tab:tab:' as label
            else:
                label = value
        elif key == "filename":
            assertStr(value, "filename")
            filename = value
        elif key == "position":
            assertStr(value, "position")
            position = value
        elif key == "transpose":
            newMatr = list(zip(*matr))
            #             for j in range(0, n):
            #                 row = list()
            #                 for i in range(0, m):
            #                     row.append(matr[i][j])
            #                 newMatr.append(row)
            copyKeywords = dict(
                keywords)  # can't del original since we are inside for loop.
            del copyKeywords['transpose']
            # Recursion!
            return matrix2latex(newMatr, filename, *environments,
                                **copyKeywords)
        else:
            sys.stderr.write("Error: key not recognized '%s'\n" % key)
            sys.exit(2)

    if headerColumn != None:
        alignment = "r" + alignment

    # Environments
    if len(environments) == 0:  # no environment give, assume table
        environments = ("table", "center", "tabular")

    if formatColumn == None:
        formatColumn = list()
        for j in range(0, n):
            formatColumn.append(formatNumber)

    if headerColumn != None and headerRow != None and len(headerRow[0]) == n:
        for i in range(len(headerRow)):
            headerRow[i].insert(0, "")

    #
    # Set outputFile
    #
    f = None
    if isinstance(filename, str) and filename != '':
        if not filename.endswith('.tex'):  # assure propper file extension
            filename += '.tex'
        f = open(filename, 'w')
        if label == None:
            label = os.path.basename(filename)  # get basename
            label = label[:-len(".tex")]  # remove extension

    f = IOString(f)
    #
    # Begin block
    #
    for ixEnv in range(0, len(environments)):
        f.write("\t" * ixEnv)
        f.write(r"\begin{%s}" % environments[ixEnv])
        # special environments:
        if environments[ixEnv] == "table":
            f.write("[" + position + "]")
        elif environments[ixEnv] == "center":
            if caption != None:
                f.write("\n" + "\t" * ixEnv)
                f.write(r"\caption{%s}" % fix(caption))
            if label != None:
                f.write("\n" + "\t" * ixEnv)
                f.write(r"\label{tab:%s}" % label)
        elif environments[ixEnv] in table_alignment:
            f.write("{" + alignment + "}\n")
            f.write("\t" * (ixEnv + 1))
            f.write(r"\toprule")
        elif environments[ixEnv] in matrix_alignment:
            f.write("[" + alignment[0] + "]\n")  #These environment you can add
        # newline
        f.write("\n")
    tabs = len(environments)  # number of \t to use

    #
    # Table block
    #

    # Row labels
    if headerRow != None:
        for row in range(len(headerRow)):  # for each header
            i = 0
            start, end = list(), list()  # of cmidrule
            f.write("\t" * tabs)
            while i < len(headerRow[row]
                          ):  # for each element (skipping repeating ones)
                j = 1
                # check for legal index then check if current element is equal to next (repeating)
                repeating = i + j < len(
                    headerRow[row]) and headerRow[row][i] == headerRow[row][i +
                                                                            j]
                if repeating:
                    while repeating:  # figure out how long it repeats (j)
                        j += 1
                        repeating = i + j < len(headerRow[row]) and headerRow[
                            row][i] == headerRow[row][i + j]
                    f.write(r'\multicolumn{%d}{c}{%s}' %
                            (j, headerRow[row][i]))  # multicol heading
                    start.append(i)
                    end.append(j + i)
                    i += j  # skip ahed
                else:
                    f.write('{%s}' % headerRow[row][i])  # normal heading
                    i += 1
                if i < len(headerRow[row]):  # if not last element
                    f.write(' & ')

            f.write(r'\\')
            #            for s, e in zip(start, end):                       deaktiviert von MB am 01.04.15
            #                f.write(r'\cmidrule(r){%d-%d}' % (s+1, e))
            f.write('\n')


#        if len(start) == 0:             # do not use if cmidrule is used on last header
#            f.write('\t'*tabs)
#            f.write('\\midrule\n')
    f.write('\t' * tabs)
    f.write('\\midrule\n')

    # Values
    for i in range(0, m):
        f.write("\t" * tabs)
        for j in range(0, n):

            if j == 0:  # first row
                if headerColumn != None:
                    try:
                        f.write("{%s} & " % headerColumn[i])
                    except IndexError:
                        f.write('&')

            try:  # get current element
                if '%s' not in formatColumn[j]:
                    try:
                        e = float(matr[i][j])  # current element
                    except ValueError:  # can't convert to float, use string
                        formatColumn[j] = '%s'
                        e = matr[i][j]
                    except TypeError:  # raised for None
                        e = None
                else:
                    e = matr[i][j]
            except IndexError:
                e = None

            if e == None or isnan(e):  #e == float('NaN'):
                f.write("{-}")
            elif e == float('inf'):
                f.write(r"$\infty$")
            elif e == float('-inf'):
                f.write(r"$-\infty$")
            else:
                fcj = formatColumn[j]

                reg = re.match(
                    '%.(\d)g',
                    fcj)  # Change the %.3g pattern to nicefloat instead
                try:
                    e = nice(e, int(reg.group(1)))
                    fcj = '%s'
                except Exception:
                    pass  #sys.stderr.write('%s %s %s\n' %(e, reg, err))

                formated = fcj % e
                formated = fix(formated, table=True)  # fix 1e+2
                f.write('%s' % formated)
            if j != n - 1:  # not last row
                f.write(" & ")
            else:  # last row
                f.write(r"\\")
                f.write("\n")

    #
    # End block
    #
    for ixEnv in range(0, len(environments)):
        ixEnv = len(environments) - 1 - ixEnv  # reverse order
        # special environments:
        if environments[ixEnv] == "center":
            pass
        elif environments[ixEnv] == "tabular":
            f.write("\t" * (ixEnv + 1))
            f.write(r"\bottomrule" + "\n")
        f.write("\t" * ixEnv)
        f.write(r"\end{%s}" % environments[ixEnv])
        if ixEnv != 0:
            f.write("\n")

    f.close()
    return f.__str__()