Convert Numpy array to LaTeX

LaTeX matrices and determinants from numpy arrays
python
numpy
latex
Author

Enrique Pérez Herrero

Published

August 2, 2023

Sierra de las Nieves (Málaga)

Convert Numpy array to \(LaTeX\)

Sometimes, when you are writing a statistics or a math article, you must go through a tedious process of typing and testing numerous lines of text until your \(LaTeX\) code works. This can be especially boring when dealing with long matrices. If you want to improve your productivity, automating this process comes in very handy, and Python is particularly useful for that.

Functions for matrices and determinants

The functions matrix2latex and det2latex return an indented \(LaTeX\) string that can be copied or rendered in your math article.

The parameter INDENT_SPACES controls the number of indentation spaces.

Code
import numpy as np

INDENT_SPACES = 3

def indent(num_indent=1):
    """
    Number of indentation spaces
    """
    return num_indent * INDENT_SPACES * " "

def matrix2latex(matrix):
    """
    Convert numpy array to latex code as a matrix
    """
    left_latex = r"\left(" + "\n" + indent(1) + r"\begin{array}"
    right_latex = indent(1) + r"\end{array}" + "\n" + r"\right)"
    m_cols = matrix.shape[1]
    array_cols = "{" + "r" * m_cols + "}\n"
    elements_latex = ""
    for row in matrix:
        elements_latex = \
          elements_latex + indent(2) + " & ".join([str(x) for x in row]) + \
            r" \\ " + "\n"
    latex = left_latex + array_cols + elements_latex + right_latex
    return f"$$\n{latex}\n$$"

def det2latex(matrix):
    """
    Convert numpy array to latex code as a determinant
    """
    left_latex = r"\begin{vmatrix}" + "\n"
    right_latex = r"\end{vmatrix}"
    m_cols = matrix.shape[1]
    elements_latex = ""
    for row in matrix:
        elements_latex = \
          elements_latex + indent(1) + " & ".join([str(x) for x in row]) + \
            r" \\ " + "\n"
    latex = left_latex + elements_latex + right_latex
    return f"$$\n{latex}\n$$"

Output string

With the NumPy function np.eye, we can create an identity matrix of the desired dimensions, and this seems to be a good way to test our functions.

Code
print(matrix2latex(np.eye(3, dtype=int)))
$$
\left(
   \begin{array}{rrr}
      1 & 0 & 0 \\ 
      0 & 1 & 0 \\ 
      0 & 0 & 1 \\ 
   \end{array}
\right)
$$

Rendering the output in a document

If you are working within a Jupyter notebook you can render a latex string with the aid of the Math function included in the IPython library, which can be imported with:

Code
from IPython.display import Math

But if you are coding a Python chunk in a Quarto file on RStudio, you need to add the option #| output: asis at the top of the code chunk. This option controls how the output is rendered in the final document.

Code
print(det2latex(np.eye(3, dtype=int)))

\[ \begin{vmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end{vmatrix} \]

Code
# Identity 4x4 matrix
identity_4 = np.eye(4, dtype=int)
identity_4_latex = r"$$I_4 = " + f"{matrix2latex(identity_4)}"[2:]
print(identity_4_latex)

\[I_4 = \left( \begin{array}{rrrr} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ \end{array} \right) \]

Rendering the output in a file

The \(LaTeX\) string can be rendered and saved into a file with the aid of the following function, based on the library sympy.

Code
from sympy import preview

def latex2png(latex, filename, fontsize=300):
    """
    Render latex code to png image
    """
    return preview(latex,
                   viewer='file',
                   filename=filename,
                   euler=False,
                   dvioptions=['-D', f'{str(fontsize)}'])

Other automated matrices

Matrices with numbered elements

Code
def element_matrix(n, notation=r"x"):
    """
    Matrix with algebraic notation elements 
    """
    vec_function = \
      np.vectorize(lambda i, j: notation + "_{" + f"{i + 1}{j + 1}" + "}")
    return np.fromfunction(vec_function,
                           shape=(n, n),
                           dtype=int)
Code
print(matrix2latex(element_matrix(5, notation=r"\theta")))

\[ \left( \begin{array}{rrrrr} \theta_{11} & \theta_{12} & \theta_{13} & \theta_{14} & \theta_{15} \\ \theta_{21} & \theta_{22} & \theta_{23} & \theta_{24} & \theta_{25} \\ \theta_{31} & \theta_{32} & \theta_{33} & \theta_{34} & \theta_{35} \\ \theta_{41} & \theta_{42} & \theta_{43} & \theta_{44} & \theta_{45} \\ \theta_{51} & \theta_{52} & \theta_{53} & \theta_{54} & \theta_{55} \\ \end{array} \right) \]

Triangular matrices

  • Upper Triangular Matrix: In an upper triangular matrix, all elements below the main diagonal (elements that lie on or below the main diagonal) are zeros.

  • Lower Triangular Matrix: In a lower triangular matrix, all elements above the main diagonal (elements that lie on or above the main diagonal) are zeros

Code
def up_triangular_matrix(n):
    """
    Upper Triangular matrix filled with ones and zeroes
    """
    return np.fromfunction(lambda i, j:  1 * np.less_equal(i , j),
                           shape=(n, n),
                           dtype=int)
  • Lower Triangular Matrix: In a lower triangular matrix, all elements above the main diagonal (elements that lie on or above the main diagonal) are zeros
Code
def lw_triangular_matrix(n):
    """
    Lower Triangular matrix filled with ones and zeroes
    """
    return np.fromfunction(lambda i, j:  1 * np.greater_equal(i , j),
                           shape=(n, n),
                           dtype=int)
Code
print(matrix2latex(up_triangular_matrix(5)))

\[ \left( \begin{array}{rrrrr} 1 & 1 & 1 & 1 & 1 \\ 0 & 1 & 1 & 1 & 1 \\ 0 & 0 & 1 & 1 & 1 \\ 0 & 0 & 0 & 1 & 1 \\ 0 & 0 & 0 & 0 & 1 \\ \end{array} \right) \]