11from IPython .display import display , Markdown , Latex
22import io
3+ import re
34from contextlib import redirect_stdout
4- from sympy import Symbol , latex
5+ from sympy import Symbol , latex , Matrix
56import numpy as np
7+ from tabulate import tabulate
68
9+ # Define special characters and symbols
10+ special_characters = {
11+ "diam" : r"\oslash" ,
12+ "apos" : r"'" ,
13+ "sum" : r"\sum" ,
14+ "comma" : r"," ,
15+ }
716
8- def put_out (offset : int = 0 , precision : int = 2 , rows : int = 3 , horizontal : bool = True ) -> None :
17+ def parse_cell_variables (offset : int = 0 ) -> dict :
918 """
10- Renders the variables and their values as LaTeX equations in a Jupyter notebook .
19+ Parses the cell history to extract variable names and their corresponding values .
1120
1221 Args:
1322 offset (int): The number of previous cells to include for variable extraction. Defaults to 0.
14- precision (int): The precision for numerical values. Defaults to 2.
15- rows (int): Maximum number of equations per row in horizontal display. Defaults to 3.
16- horizontal (bool): Whether to display equations horizontally or vertically. Defaults to True.
1723
1824 Returns:
19- None
25+ dict: A dictionary with variable names as keys and their corresponding values from the user namespace.
2026 """
21-
2227 ipy = get_ipython ()
2328 out = io .StringIO ()
2429
@@ -34,100 +39,141 @@ def put_out(offset: int = 0, precision: int = 2, rows: int = 3, horizontal: bool
3439 user_ns = ipy .user_ns
3540 variables = {name : user_ns [name ] for name in variable_names if name in user_ns }
3641
37-
38- def format_value (value ):
39- """Formats the value based on its type."""
40- if isinstance (value , (int , float )):
41- return round (value , precision )
42- elif isinstance (value , np .ndarray ):
43- return np .array2string (np .round (value , precision ), separator = "," )
44- elif hasattr (value , "magnitude" ):
45- magnitude = np .round (value .magnitude , precision )
46- if isinstance (magnitude , np .ndarray ):
47- magnitude_str = np .array2string (magnitude , separator = "," )
48- return f"{ magnitude_str } \\ { latex (Symbol (str (value .units )))} "
49- else :
50- return f"{ magnitude } \\ { latex (Symbol (str (value .units )))} "
51- else :
52- return value
42+ return variables
5343
5444
55- # Format the variables and their values
56- formatted_vars = {name : format_value (value ) for name , value in variables .items ()}
5745
58- if horizontal :
59- # Horizontal display with aligned '=' signs
60- var_list = list (formatted_vars .items ())
46+ def format_name (name : str , symbols : dict = special_characters ) -> str :
47+ """Formats the input string with specific LaTeX replacements, handles underscores, and converts 'txt_' prefix to plain text."""
48+
49+ # Regex to find words, underscores, and numbers separately
50+ name_parts = re .findall (r'[a-zA-Z]+|_|\d+' , name )
51+
52+ result_parts = []
53+ i = 0
6154
62- # check if there are less variables than rows. This results in clean aligning of the equations
63- if len (var_list )< rows :
64- rows = len (var_list )
55+ while i < len (name_parts ):
56+ part = name_parts [i ]
6557
66- markdown_str = "$$\n \\ begin{aligned}\n "
67- # Iterate through the variables
68- for i in range (0 , len (var_list ), rows ):
69- row = var_list [i : i + rows ]
70- row_str = " \\ quad & " .join (
71- [f"{ latex (Symbol (var_name ))} & = { value } " for var_name , value in row ]
72- )
73- # Add placeholders for missing variables in the row
74- if len (row ) < rows & rows != 1 :
75- row_str += " \\ quad & " * (rows - len (row )) + " \n "
76-
77- markdown_str += row_str + " \\ \\ \n "
78- # strip the last space
79- if markdown_str .endswith (" \\ \\ \n " ):
80- markdown_str = markdown_str [:- 4 ]
81- markdown_str += "\\ end{aligned}\n $$"
82-
83- else :
84- markdown_str = ""
85- for var_name , value in formatted_vars .items ():
86- markdown_str += f"$${ latex (Symbol (var_name ))} = { value } $$\n \n "
87-
88- display (Markdown (markdown_str ))
89-
58+ # Handle the 'sum_' exception
59+ if part == 'sum' and i + 1 < len (name_parts ) and name_parts [i + 1 ] == '_' :
60+ result_parts .append (symbols .get (part , part ))
61+ i += 1 # Skip the underscore after 'sum'
62+
63+ # Handle the '_strich' exception
64+ elif part == '_' and i + 1 < len (name_parts ) and name_parts [i + 1 ] == 'apos' :
65+ result_parts .append (symbols .get (name_parts [i + 1 ], name_parts [i + 1 ]))
66+ i += 1 # Skip both underscore and 'strich'
67+
68+ # Replace regular parts using the dictionary
69+ elif part in symbols :
70+ result_parts .append (symbols [part ])
71+
72+ # Preserve numbers and non-matching underscores
73+ else :
74+ result_parts .append (part )
75+
76+ i += 1
9077
91- def dict_to_markdown_table (data : dict ) -> str :
92- if not data :
93- return ""
94-
95- # Extract keys and values
96- items = list (data .items ())
97- rows = []
98-
99- # Pair items into rows with 4 columns (2 key-value pairs per row)
100- for i in range (0 , len (items ), 2 ):
101- row = []
102- for j in range (2 ):
103- if i + j < len (items ):
104- key , value = items [i + j ]
105- row .extend ([str (key ), str (value )])
106- else :
107- row .extend (["-" , "-" ]) # Fill empty cells for incomplete rows
108- rows .append (row )
109-
110- # Headers for the 4 columns
111- headers = ["Bezeichnung" , "Wert" , "Bezeichnung" , "Wert" ]
112-
113- # Calculate column widths
114- col_widths = [max (len (row [i ]) for row in [headers ] + rows ) for i in range (4 )]
78+ # Join all parts back together
79+ name_replaced = '' .join (result_parts )
11580
116- # Create the markdown table
117- table = []
81+ # make a symbol out of it
82+ name_replaced = latex (Symbol (name_replaced ))
83+
84+ return name_replaced
11885
119- # Header row
120- header_row = "| " + " | " .join (headers [i ].ljust (col_widths [i ]) for i in range (4 )) + " |"
121- table .append (header_row )
86+ def format_value (value , precision : float = 2 ):
87+ """Formats the value based on its type."""
88+ if isinstance (value , (int , float )):
89+ return round (value , precision )
12290
123- # Divider
124- divider = "| " + " | " .join ("-" * col_widths [i ] for i in range (4 )) + " |"
125- table .append (divider )
91+ elif isinstance (value , np .ndarray ):
92+ # Handle numpy arrays as matrices
93+ rounded_value = np .round (value , precision )
94+ matrix = Matrix (rounded_value .tolist ())
95+ return latex (matrix )
12696
127- # Data rows
128- for row in rows :
129- row_str = "| " + " | " . join ( row [ i ]. ljust ( col_widths [ i ] ) for i in range ( 4 )) + " |"
130- table . append ( row_str )
97+ elif isinstance ( value , list ):
98+ # Handle lists as vectors
99+ formatted_list = [ format_value ( item , precision ) for item in value ]
100+ return formatted_list
131101
132- return "\n " .join (table )
102+ elif hasattr (value , "magnitude" ):
103+ # Handle Pint quantities
104+ magnitude = np .round (value .magnitude , precision )
105+ if isinstance (magnitude , np .ndarray ):
106+ # Handle numpy arrays of Pint quantities as matrices
107+ magnitude_str = np .array2string (magnitude , separator = "," )
108+ return f"{ latex (Matrix (magnitude .tolist ()))} \\ { latex (Symbol (str (value .units )))} "
109+ else :
110+ # Handle scalar Pint quantities
111+ return f"{ magnitude } \\ { latex (Symbol (str (value .units )))} "
112+ else :
113+ return value
114+
115+ def dict_to_markdown_table (dict :dict , symbols : dict = special_characters , precision :int = 2 , tablefmt : str = 'pipe' ):
116+ # Convert dictionary to list of lists with appropriate headers
117+ formatted_data = [[key , '$' + str (format_value (value , precision ))+ '$' ] for key , value in dict .items ()]
118+ headers = ['Bezeichnung' , 'Wert' ]
119+
120+ # Generate the Markdown table
121+ markdown_table = tabulate (formatted_data , headers = headers , tablefmt = tablefmt )
122+
123+ return markdown_table
124+
125+
126+
127+ def put_out (precision : int = 2 , offset : int = 0 , rows : int = 3 , symbols : dict = special_characters , tablefmt :str = 'pipe' ) -> None :
128+ """
129+ Renders the variables and their values as LaTeX equations in a Jupyter notebook.
130+
131+ Args:
132+ precision (int): The precision for numerical values. Defaults to 2.
133+ offset (int): The number of previous cells to include for variable extraction. Defaults to 0.
134+ rows (int): Maximum number of equations per row in horizontal display. Defaults to 3.
135+ symbols (dict): Dictionary of symbols for LaTeX formatting.
136+
137+ Returns:
138+ None
139+ """
140+ # Use the new cell parser function
141+ variables = parse_cell_variables (offset )
142+
143+
144+ formatted_vars = {}
145+
146+ for name , value in variables .items ():
147+ if type (value ) == dict :
148+ display (Markdown (dict_to_markdown_table (value , precision = precision , tablefmt = tablefmt )))
149+
150+ else :
151+ formatted_vars .update ({format_name (name , symbols = symbols ): format_value (value , precision = precision )})
152+
153+ # Horizontal display with aligned '=' signs
154+ var_list = list (formatted_vars .items ())
155+
156+ if len (var_list ) == 0 :
157+ return
158+ elif len (var_list ) < rows :
159+ rows = len (var_list )
160+
161+ markdown_str = "$$\n \\ begin{aligned}\n "
162+ for i in range (0 , len (var_list ), rows ):
163+ row = var_list [i : i + rows ]
164+ row_str = " \\ quad & " .join (
165+ [f"{ var_name } & = { value } " for var_name , value in row ]
166+ )
167+ if len (row ) < rows and rows != 1 :
168+ row_str += " \\ quad & " * (rows - len (row )) + " \n "
169+
170+ markdown_str += row_str + " \\ \\ \n "
171+
172+ if markdown_str .endswith (" \\ \\ \n " ):
173+ markdown_str = markdown_str [:- 4 ]
174+ markdown_str += "\\ end{aligned}\n $$"
175+
176+ display (Markdown (markdown_str ))
177+
178+
133179
0 commit comments