1+ {
2+ "cells": [
3+ {
4+ "cell_type": "markdown",
5+ "metadata": {},
6+ "source": [
7+ "---\n",
8+ "title: Convert a NumPy array to LaTeX\n",
9+ "description: LaTeX matrices and determinants from NumPy arrays\n",
10+ "author: 'Enrique Pérez Herrero'\n",
11+ "date: '08-02-2023'\n",
12+ "editor: source\n",
13+ "image: 'theta3x3matrix.png'\n",
14+ "draft: false\n",
15+ "categories:\n",
16+ " - Python\n",
17+ " - Numpy\n",
18+ " - Latex\n",
19+ " - Determinant\n",
20+ "---\n",
21+ "\n",
22+ "\n",
23+ "\n",
24+ "\n",
25+ "# Convert Numpy array to $LaTeX$\n",
26+ "\n",
27+ "Sometimes, when you are writing a statistics or a math article, you must go\n",
28+ "through a tedious process of typing and testing numerous lines of text until\n",
29+ "your code works. This can be especially boring when dealing with long matrices.\n",
30+ "If you want to improve your productivity, automating this process comes in very\n",
31+ "handy, and [Python](https://www.python.org/) is particularly useful for that.\n",
32+ "\n",
33+ "\n",
34+ "## Functions for matrices and determinants\n",
35+ "\n",
36+ "The functions `matrix2latex` and `det2latex` generate an indented $LaTeX$ string\n",
37+ "that can be copied or directly rendered in your math article.\n",
38+ "\n",
39+ "The `INDENT_SPACES` parameter specifies the number of spaces used for\n",
40+ "indentation.\n"
41+ ],
42+ "id": "127a872b"
43+ },
44+ {
45+ "cell_type": "code",
46+ "metadata": {},
47+ "source": [
48+ "#| label: latex_functions\n",
49+ "import numpy as np\n",
50+ "\n",
51+ "INDENT_SPACES = 3\n",
52+ "\n",
53+ "def indent(num_indent=1):\n",
54+ " \"\"\"\n",
55+ " Number of spaces for indentation\n",
56+ " \"\"\"\n",
57+ " return num_indent * INDENT_SPACES * \" \"\n",
58+ "\n",
59+ "def matrix2latex(matrix):\n",
60+ " \"\"\"\n",
61+ " Convert a NumPy array to LaTeX code as a matrix\n",
62+ " \"\"\"\n",
63+ " left_latex = r\"\\left(\" + \"\\n\" + indent(1) + r\"\\begin{array}\"\n",
64+ " right_latex = indent(1) + r\"\\end{array}\" + \"\\n\" + r\"\\right)\"\n",
65+ " m_cols = matrix.shape[1]\n",
66+ " array_cols = \"{\" + \"r\" * m_cols + \"}\\n\"\n",
67+ " elements_latex = \"\"\n",
68+ " for row in matrix:\n",
69+ " elements_latex = \\\n",
70+ " elements_latex + indent(2) + \" & \".join([str(x) for x in row]) + \\\n",
71+ " r\" \\\\ \" + \"\\n\"\n",
72+ " latex = left_latex + array_cols + elements_latex + right_latex\n",
73+ " return f\"$$\\n{latex}\\n$$\"\n",
74+ "\n",
75+ "def det2latex(matrix):\n",
76+ " \"\"\"\n",
77+ " Convert a NumPy array to LaTeX code as a determinant\n",
78+ " \"\"\"\n",
79+ " left_latex = r\"\\begin{vmatrix}\" + \"\\n\"\n",
80+ " right_latex = r\"\\end{vmatrix}\"\n",
81+ " m_cols = matrix.shape[1]\n",
82+ " elements_latex = \"\"\n",
83+ " for row in matrix:\n",
84+ " elements_latex = \\\n",
85+ " elements_latex + indent(1) + \" & \".join([str(x) for x in row]) + \\\n",
86+ " r\" \\\\ \" + \"\\n\"\n",
87+ " latex = left_latex + elements_latex + right_latex\n",
88+ " return f\"$$\\n{latex}\\n$$\""
89+ ],
90+ "id": "latex_functions",
91+ "execution_count": null,
92+ "outputs": []
93+ },
94+ {
95+ "cell_type": "markdown",
96+ "metadata": {},
97+ "source": [
98+ "### Output string\n",
99+ "\n",
100+ "Using the [NumPy](https://numpy.org/) function `np.eye`, we can create an\n",
101+ "identity matrix of the desired dimensions, making it a convenient tool for\n",
102+ "testing our functions.\n"
103+ ],
104+ "id": "b0e17845"
105+ },
106+ {
107+ "cell_type": "code",
108+ "metadata": {},
109+ "source": [
110+ "print(matrix2latex(np.eye(3, dtype=int)))"
111+ ],
112+ "id": "bf31123b",
113+ "execution_count": null,
114+ "outputs": []
115+ },
116+ {
117+ "cell_type": "markdown",
118+ "metadata": {},
119+ "source": [
120+ "## Rendering output in a document\n",
121+ "\n",
122+ "If you are working in a [Jupyter](https://jupyter.org/) notebook, you can render\n",
123+ "a $LaTeX$ string using the `Math` function from the `IPython` library, which can\n",
124+ "be imported as follows:\n"
125+ ],
126+ "id": "27e76398"
127+ },
128+ {
129+ "cell_type": "code",
130+ "metadata": {},
131+ "source": [
132+ "#| eval: false\n",
133+ "from IPython.display import Math"
134+ ],
135+ "id": "29c0bda7",
136+ "execution_count": null,
137+ "outputs": []
138+ },
139+ {
140+ "cell_type": "markdown",
141+ "metadata": {},
142+ "source": [
143+ "If you are writing a `Python` chunk in a `Quarto` file in\n",
144+ "[RStudio](https://posit.co/download/rstudio-desktop/), you need to include the\n",
145+ "option `#| output: asis` at the top of the code chunk. This option determines\n",
146+ "how the output is rendered in the final document.\n"
147+ ],
148+ "id": "22e95356"
149+ },
150+ {
151+ "cell_type": "code",
152+ "metadata": {},
153+ "source": [
154+ "#| label: identity_det\n",
155+ "#| output: asis\n",
156+ "print(det2latex(np.eye(3, dtype=int)))"
157+ ],
158+ "id": "identity_det",
159+ "execution_count": null,
160+ "outputs": []
161+ },
162+ {
163+ "cell_type": "code",
164+ "metadata": {},
165+ "source": [
166+ "#| label: identity_matrix\n",
167+ "#| output: asis\n",
168+ "# Identity 4x4 matrix\n",
169+ "identity_4 = np.eye(4, dtype=int)\n",
170+ "identity_4_latex = r\"$$I_4 = \" + f\"{matrix2latex(identity_4)}\"[2:]\n",
171+ "print(identity_4_latex)"
172+ ],
173+ "id": "identity_matrix",
174+ "execution_count": null,
175+ "outputs": []
176+ },
177+ {
178+ "cell_type": "markdown",
179+ "metadata": {},
180+ "source": [
181+ "## Rendering the output to a file\n",
182+ "\n",
183+ "The $LaTeX$ string can be rendered and saved to a file using the\n",
184+ "following function, which is based on the `SymPy` library.\n"
185+ ],
186+ "id": "c767ecf6"
187+ },
188+ {
189+ "cell_type": "code",
190+ "metadata": {},
191+ "source": [
192+ "#| eval: false\n",
193+ "from sympy import preview\n",
194+ "\n",
195+ "def latex2png(latex, filename, fontsize=300):\n",
196+ " \"\"\"\n",
197+ " Render LaTeX code to a PNG image\n",
198+ " \"\"\"\n",
199+ " return preview(latex,\n",
200+ " viewer='file',\n",
201+ " filename=filename,\n",
202+ " euler=False,\n",
203+ " dvioptions=['-D', f'{str(fontsize)}'])"
204+ ],
205+ "id": "1d678be2",
206+ "execution_count": null,
207+ "outputs": []
208+ },
209+ {
210+ "cell_type": "markdown",
211+ "metadata": {},
212+ "source": [
213+ "## Other types of automated matrices\n",
214+ "\n",
215+ "### Matrices with numbered elements\n"
216+ ],
217+ "id": "2109eb16"
218+ },
219+ {
220+ "cell_type": "code",
221+ "metadata": {},
222+ "source": [
223+ "def element_matrix(n, notation=r\"x\"):\n",
224+ " \"\"\"\n",
225+ " Matrix with elements in algebraic notation \n",
226+ " \"\"\"\n",
227+ " vec_function = \\\n",
228+ " np.vectorize(lambda i, j: notation + \"_{\" + f\"{i + 1}{j + 1}\" + \"}\")\n",
229+ " return np.fromfunction(vec_function,\n",
230+ " shape=(n, n),\n",
231+ " dtype=int)"
232+ ],
233+ "id": "e113a4fb",
234+ "execution_count": null,
235+ "outputs": []
236+ },
237+ {
238+ "cell_type": "code",
239+ "metadata": {},
240+ "source": [
241+ "#| label: element_matrix_example\n",
242+ "#| output: asis\n",
243+ "print(matrix2latex(element_matrix(5, notation=r\"\\theta\")))"
244+ ],
245+ "id": "element_matrix_example",
246+ "execution_count": null,
247+ "outputs": []
248+ },
249+ {
250+ "cell_type": "markdown",
251+ "metadata": {},
252+ "source": [
253+ "### Triangular matrices\n",
254+ "\n",
255+ "* _Upper Triangular Matrix_: In an upper triangular matrix, all elements below the\n",
256+ "main diagonal are zero.\n"
257+ ],
258+ "id": "23a78e28"
259+ },
260+ {
261+ "cell_type": "code",
262+ "metadata": {},
263+ "source": [
264+ "def up_triangular_matrix(n):\n",
265+ " \"\"\"\n",
266+ " Upper Triangular matrix filled with ones and zeros\n",
267+ " \"\"\"\n",
268+ " return np.fromfunction(lambda i, j: 1 * np.less_equal(i , j),\n",
269+ " shape=(n, n),\n",
270+ " dtype=int)"
271+ ],
272+ "id": "82e86e4b",
273+ "execution_count": null,
274+ "outputs": []
275+ },
276+ {
277+ "cell_type": "markdown",
278+ "metadata": {},
279+ "source": [
280+ "* _Lower Triangular Matrix_: In a lower triangular matrix, all elements above the main diagonal are zero.\n"
281+ ],
282+ "id": "9b7fa372"
283+ },
284+ {
285+ "cell_type": "code",
286+ "metadata": {},
287+ "source": [
288+ "def lw_triangular_matrix(n):\n",
289+ " \"\"\"\n",
290+ " Lower Triangular matrix filled with ones and zeros\n",
291+ " \"\"\"\n",
292+ " return np.fromfunction(lambda i, j: 1 * np.greater_equal(i , j),\n",
293+ " shape=(n, n),\n",
294+ " dtype=int)"
295+ ],
296+ "id": "bac1687e",
297+ "execution_count": null,
298+ "outputs": []
299+ },
300+ {
301+ "cell_type": "code",
302+ "metadata": {},
303+ "source": [
304+ "#| label: upper_matrix_example\n",
305+ "#| output: asis\n",
306+ "print(matrix2latex(up_triangular_matrix(5)))"
307+ ],
308+ "id": "upper_matrix_example",
309+ "execution_count": null,
310+ "outputs": []
311+ }
312+ ],
313+ "metadata": {
314+ "kernelspec": {
315+ "name": "shiny-test",
316+ "language": "python",
317+ "display_name": "Shiny Test",
318+ "path": "/home/eph/.local/share/jupyter/kernels/shiny-test"
319+ }
320+ },
321+ "nbformat": 4,
322+ "nbformat_minor": 5
323+ }
0 commit comments