rgwfuncs 0.0.25__py3-none-any.whl → 0.0.27__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
rgwfuncs/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # This file is automatically generated
2
2
  # Dynamically importing functions from modules
3
3
 
4
- from .algebra_lib import compute_algebraic_expression, get_prime_factors_latex, simplify_algebraic_expression, solve_algebraic_expression
4
+ from .algebra_lib import compute_algebraic_expression, compute_matrix_operation, compute_ordered_series_operation, get_prime_factors_latex, simplify_algebraic_expression, solve_algebraic_expression
5
5
  from .df_lib import append_columns, append_percentile_classification_column, append_ranged_classification_column, append_ranged_date_classification_column, append_rows, append_xgb_labels, append_xgb_logistic_regression_predictions, append_xgb_regression_predictions, bag_union_join, bottom_n_unique_values, cascade_sort, delete_rows, drop_duplicates, drop_duplicates_retain_first, drop_duplicates_retain_last, filter_dataframe, filter_indian_mobiles, first_n_rows, from_raw_data, insert_dataframe_in_sqlite_database, last_n_rows, left_join, limit_dataframe, load_data_from_path, load_data_from_query, load_data_from_sqlite_path, mask_against_dataframe, mask_against_dataframe_converse, numeric_clean, order_columns, print_correlation, print_dataframe, print_memory_usage, print_n_frequency_cascading, print_n_frequency_linear, rename_columns, retain_columns, right_join, send_data_to_email, send_data_to_slack, send_dataframe_via_telegram, sync_dataframe_to_sqlite_database, top_n_unique_values, union_join, update_rows
6
6
  from .docs_lib import docs
7
7
  from .str_lib import send_telegram_message
rgwfuncs/algebra_lib.py CHANGED
@@ -1,10 +1,30 @@
1
1
  import re
2
2
  import math
3
+ import ast
4
+ # import numpy as np
3
5
  from sympy import symbols, latex, simplify, solve, diff, Expr
4
6
  from sympy.parsing.sympy_parser import parse_expr
5
7
  from typing import Tuple, List, Dict, Optional
6
8
 
9
+
7
10
  def compute_algebraic_expression(expression: str) -> float:
11
+ """
12
+ Computes the numerical result of a given algebraic expression.
13
+
14
+ Evaluates an algebraic expression provided as a string and returns the computed result.
15
+ Supports various arithmetic operations, including addition, subtraction, multiplication,
16
+ division, and modulo, as well as mathematical functions from the math module.
17
+
18
+ Parameters:
19
+ expression (str): The algebraic expression to compute. This should be a string consisting
20
+ of arithmetic operations and supported math module functions.
21
+
22
+ Returns:
23
+ float: The evaluated numerical result of the expression.
24
+
25
+ Raises:
26
+ ValueError: If the expression cannot be evaluated due to syntax errors or other issues.
27
+ """
8
28
  try:
9
29
  # Direct numerical evaluation
10
30
  # Safely evaluate the expression using the math module
@@ -15,10 +35,23 @@ def compute_algebraic_expression(expression: str) -> float:
15
35
  except Exception as e:
16
36
  raise ValueError(f"Error computing expression: {e}")
17
37
 
38
+
18
39
  def simplify_algebraic_expression(expression: str) -> str:
40
+ """
41
+ Simplifies an algebraic expression and returns it in LaTeX format.
42
+
43
+ Takes an algebraic expression written in Python syntax and simplifies it. The result is
44
+ returned as a LaTeX formatted string, suitable for academic or professional documentation.
19
45
 
46
+ Parameters:
47
+ expression (str): The algebraic expression to simplify.
48
+
49
+ Returns:
50
+ str: The simplified expression represented as a LaTeX string.
51
+ """
20
52
 
21
- def recursive_parse_function_call(func_call: str, prefix: str, sym_vars: Dict[str, Expr]) -> Tuple[str, List[Expr]]:
53
+ def recursive_parse_function_call(
54
+ func_call: str, prefix: str, sym_vars: Dict[str, Expr]) -> Tuple[str, List[Expr]]:
22
55
  # print(f"Parsing function call: {func_call}")
23
56
 
24
57
  # Match the function name and arguments
@@ -30,7 +63,8 @@ def simplify_algebraic_expression(expression: str) -> str:
30
63
  args_str = match.group(2)
31
64
 
32
65
  # Check if it's a list for np
33
- if prefix == 'np' and args_str.startswith("[") and args_str.endswith("]"):
66
+ if prefix == 'np' and args_str.startswith(
67
+ "[") and args_str.endswith("]"):
34
68
  parsed_args = [ast.literal_eval(args_str.strip())]
35
69
  else:
36
70
  parsed_args = []
@@ -38,77 +72,98 @@ def simplify_algebraic_expression(expression: str) -> str:
38
72
  for arg in raw_args:
39
73
  arg = arg.strip()
40
74
  if re.match(r'\w+\.\w+\(', arg):
41
- # Recursively evaluate the argument if it's another function call
42
- arg_val = recursive_eval_func(re.match(r'\w+\.\w+\(.*\)', arg), sym_vars)
43
- parsed_args.append(parse_expr(arg_val, local_dict=sym_vars))
75
+ # Recursively evaluate the argument if it's another
76
+ # function call
77
+ arg_val = recursive_eval_func(
78
+ re.match(r'\w+\.\w+\(.*\)', arg), sym_vars)
79
+ parsed_args.append(
80
+ parse_expr(
81
+ arg_val,
82
+ local_dict=sym_vars))
44
83
  else:
45
84
  parsed_args.append(parse_expr(arg, local_dict=sym_vars))
46
85
 
47
86
  # print(f"Function name: {func_name}, Parsed arguments: {parsed_args}")
48
87
  return func_name, parsed_args
49
88
 
50
-
51
89
  def recursive_eval_func(match: re.Match, sym_vars: Dict[str, Expr]) -> str:
52
90
  # print("152", match)
53
91
  func_call = match.group(0)
54
92
  # print(f"153 Evaluating function call: {func_call}")
55
93
 
56
94
  if func_call.startswith("np."):
57
- func_name, args = recursive_parse_function_call(func_call, 'np', sym_vars)
95
+ func_name, args = recursive_parse_function_call(
96
+ func_call, 'np', sym_vars)
58
97
  if func_name == 'diff':
59
98
  expr = args[0]
60
99
  if isinstance(expr, list):
61
100
  # Calculate discrete difference
62
- diff_result = [expr[i] - expr[i - 1] for i in range(1, len(expr))]
101
+ diff_result = [expr[i] - expr[i - 1]
102
+ for i in range(1, len(expr))]
63
103
  return str(diff_result)
64
104
  # Perform symbolic differentiation
65
105
  diff_result = diff(expr)
66
106
  return str(diff_result)
67
107
 
68
108
  if func_call.startswith("math."):
69
- func_name, args = recursive_parse_function_call(func_call, 'math', sym_vars)
109
+ func_name, args = recursive_parse_function_call(
110
+ func_call, 'math', sym_vars)
70
111
  if hasattr(math, func_name):
71
112
  result = getattr(math, func_name)(*args)
72
113
  return str(result)
73
114
 
74
115
  if func_call.startswith("sym."):
75
- initial_method_match = re.match(r'(sym\.\w+\([^()]*\))(\.(\w+)\((.*?)\))*', func_call, re.DOTALL)
116
+ initial_method_match = re.match(
117
+ r'(sym\.\w+\([^()]*\))(\.(\w+)\((.*?)\))*', func_call, re.DOTALL)
76
118
  if initial_method_match:
77
119
  base_expr_str = initial_method_match.group(1)
78
- base_func_name, base_args = recursive_parse_function_call(base_expr_str, 'sym', sym_vars)
120
+ base_func_name, base_args = recursive_parse_function_call(
121
+ base_expr_str, 'sym', sym_vars)
79
122
  if base_func_name == 'solve':
80
123
  solutions = solve(base_args[0], base_args[1])
81
124
  # print(f"Solutions found: {solutions}")
82
125
 
83
- method_chain = re.findall(r'\.(\w+)\((.*?)\)', func_call, re.DOTALL)
84
- final_solutions = [execute_chained_methods(sol, [(m, [method_args.strip()]) for m, method_args in method_chain], sym_vars) for sol in solutions]
126
+ method_chain = re.findall(
127
+ r'\.(\w+)\((.*?)\)', func_call, re.DOTALL)
128
+ final_solutions = [execute_chained_methods(sol, [(m, [method_args.strip(
129
+ )]) for m, method_args in method_chain], sym_vars) for sol in solutions]
85
130
 
86
- return "[" + ",".join(latex(simplify(sol)) for sol in final_solutions) + "]"
131
+ return "[" + ",".join(latex(simplify(sol))
132
+ for sol in final_solutions) + "]"
87
133
 
88
134
  raise ValueError(f"Unknown function call: {func_call}")
89
135
 
90
- def execute_chained_methods(sym_expr: Expr, method_chain: List[Tuple[str, List[str]]], sym_vars: Dict[str, Expr]) -> Expr:
136
+ def execute_chained_methods(sym_expr: Expr,
137
+ method_chain: List[Tuple[str,
138
+ List[str]]],
139
+ sym_vars: Dict[str,
140
+ Expr]) -> Expr:
91
141
  for method_name, method_args in method_chain:
92
142
  # print(f"Executing method: {method_name} with arguments: {method_args}")
93
143
  method = getattr(sym_expr, method_name, None)
94
144
  if method:
95
145
  if method_name == 'subs' and isinstance(method_args[0], dict):
96
146
  kwargs = method_args[0]
97
- kwargs = {parse_expr(k, local_dict=sym_vars): parse_expr(v, local_dict=sym_vars) for k, v in kwargs.items()}
147
+ kwargs = {
148
+ parse_expr(
149
+ k,
150
+ local_dict=sym_vars): parse_expr(
151
+ v,
152
+ local_dict=sym_vars) for k,
153
+ v in kwargs.items()}
98
154
  sym_expr = method(kwargs)
99
155
  else:
100
- args = [parse_expr(arg.strip(), local_dict=sym_vars) for arg in method_args]
156
+ args = [parse_expr(arg.strip(), local_dict=sym_vars)
157
+ for arg in method_args]
101
158
  sym_expr = method(*args)
102
159
  # print(f"Result after {method_name}: {sym_expr}")
103
160
  return sym_expr
104
161
 
105
-
106
-
107
162
  variable_names = set(re.findall(r'\b[a-zA-Z]\w*\b', expression))
108
163
  sym_vars = {var: symbols(var) for var in variable_names}
109
164
 
110
165
  patterns = {
111
- #"numpy_diff_brackets": r"np\.diff\(\[.*?\]\)",
166
+ # "numpy_diff_brackets": r"np\.diff\(\[.*?\]\)",
112
167
  "numpy_diff_no_brackets": r"np\.diff\([^()]*\)",
113
168
  "math_functions": r"math\.\w+\((?:[^()]*(?:\([^()]*\)[^()]*)*)\)",
114
169
  # "sympy_functions": r"sym\.\w+\([^()]*\)(?:\.\w+\([^()]*\))?",
@@ -117,11 +172,14 @@ def simplify_algebraic_expression(expression: str) -> str:
117
172
  function_pattern = '|'.join(patterns.values())
118
173
 
119
174
  # Use a lambda function to pass additional arguments
120
- processed_expression = re.sub(function_pattern, lambda match: recursive_eval_func(match, sym_vars), expression)
175
+ processed_expression = re.sub(
176
+ function_pattern, lambda match: recursive_eval_func(
177
+ match, sym_vars), expression)
121
178
  # print("Level 2 processed_expression:", processed_expression)
122
179
 
123
180
  try:
124
- if processed_expression.startswith('[') and processed_expression.endswith(']'):
181
+ if processed_expression.startswith(
182
+ '[') and processed_expression.endswith(']'):
125
183
  return processed_expression
126
184
 
127
185
  expr = parse_expr(processed_expression, local_dict=sym_vars)
@@ -136,7 +194,28 @@ def simplify_algebraic_expression(expression: str) -> str:
136
194
  except Exception as e:
137
195
  raise ValueError(f"Error simplifying expression: {e}")
138
196
 
139
- def solve_algebraic_expression(expression: str, variable: str, subs: Optional[Dict[str, float]] = None) -> str:
197
+
198
+ def solve_algebraic_expression(
199
+ expression: str, variable: str, subs: Optional[Dict[str, float]] = None) -> str:
200
+ """
201
+ Solves an algebraic equation for a specified variable and returns solutions in LaTeX format.
202
+
203
+ Solves the given equation for a designated variable. May optionally include substitutions
204
+ for other variables in the equation. The solutions are provided as a LaTeX formatted string.
205
+
206
+ Parameters:
207
+ expression (str): The algebraic equation to solve.
208
+ variable (str): The variable to solve the equation for.
209
+ subs (Optional[Dict[str, float]]): An optional dictionary of substitutions for variables
210
+ in the equation.
211
+
212
+ Returns:
213
+ str: The solutions of the equation, formatted as a LaTeX string.
214
+
215
+ Raises:
216
+ ValueError: If the equation cannot be solved due to errors in expression or parameters.
217
+ """
218
+
140
219
  try:
141
220
  # Create symbols for the variables in the expression
142
221
  variable_symbols = set(re.findall(r'\b[a-zA-Z]\w*\b', expression))
@@ -153,7 +232,9 @@ def solve_algebraic_expression(expression: str, variable: str, subs: Optional[Di
153
232
  solutions = [simplify(sol.subs(subs_symbols)) for sol in solutions]
154
233
 
155
234
  # Convert solutions to LaTeX strings if possible
156
- latex_solutions = [latex(simplify(sol)) if sol.free_symbols else str(sol) for sol in solutions]
235
+ latex_solutions = [
236
+ latex(
237
+ simplify(sol)) if sol.free_symbols else str(sol) for sol in solutions]
157
238
  result = r"\left[" + ", ".join(latex_solutions) + r"\right]"
158
239
  print("158", result)
159
240
  return result
@@ -162,11 +243,212 @@ def solve_algebraic_expression(expression: str, variable: str, subs: Optional[Di
162
243
  raise ValueError(f"Error solving the expression: {e}")
163
244
 
164
245
 
246
+ def compute_matrix_operation(expression: str) -> str:
247
+ """
248
+ Computes the result of a matrix-like operation on 1D or 2D list inputs and returns it as a LaTeX string.
249
+
250
+ Evaluates an operation where lists are treated as matrices, performs operations on them sequentially, and
251
+ returns the result formatted as a LaTeX-style string.
252
+
253
+ Parameters:
254
+ expression (str): The matrix operation expression to compute. Example format includes operations such as "+", "-", "*", "/".
255
+
256
+ Returns:
257
+ str: The LaTeX-formatted string representation of the result or a message indicating an error in dimensions.
258
+ """
259
+
260
+ def elementwise_operation(matrix1: List[List[float]], matrix2: List[List[float]], operation: str) -> List[List[float]]:
261
+ if len(matrix1) != len(matrix2) or any(len(row1) != len(row2) for row1, row2 in zip(matrix1, matrix2)):
262
+ return "Operations between matrices must involve matrices of the same dimension"
263
+
264
+ if operation == '+':
265
+ return [[a + b for a, b in zip(row1, row2)] for row1, row2 in zip(matrix1, matrix2)]
266
+ elif operation == '-':
267
+ return [[a - b for a, b in zip(row1, row2)] for row1, row2 in zip(matrix1, matrix2)]
268
+ elif operation == '*':
269
+ return [[a * b for a, b in zip(row1, row2)] for row1, row2 in zip(matrix1, matrix2)]
270
+ elif operation == '/':
271
+ return [[a / b for a, b in zip(row1, row2) if b != 0] for row1, row2 in zip(matrix1, matrix2)]
272
+ else:
273
+ return f"Unsupported operation {operation}"
274
+
275
+ try:
276
+ # Use a stack-based method to properly parse matrices
277
+ elements = []
278
+ buffer = ''
279
+ bracket_level = 0
280
+ operators = set('+-*/')
281
+
282
+ for char in expression:
283
+ if char == '[':
284
+ if bracket_level == 0 and buffer.strip():
285
+ elements.append(buffer.strip())
286
+ buffer = ''
287
+ bracket_level += 1
288
+ elif char == ']':
289
+ bracket_level -= 1
290
+ if bracket_level == 0:
291
+ buffer += char
292
+ elements.append(buffer.strip())
293
+ buffer = ''
294
+ continue
295
+ if bracket_level == 0 and char in operators:
296
+ if buffer.strip():
297
+ elements.append(buffer.strip())
298
+ buffer = ''
299
+ elements.append(char)
300
+ else:
301
+ buffer += char
302
+
303
+ if buffer.strip():
304
+ elements.append(buffer.strip())
305
+
306
+ result = ast.literal_eval(elements[0])
307
+
308
+ if not any(isinstance(row, list) for row in result):
309
+ result = [result] # Convert 1D matrix to 2D
310
+
311
+ i = 1
312
+ while i < len(elements):
313
+ operation = elements[i]
314
+ matrix = ast.literal_eval(elements[i + 1])
315
+
316
+ if not any(isinstance(row, list) for row in matrix):
317
+ matrix = [matrix]
318
+
319
+ operation_result = elementwise_operation(result, matrix, operation)
320
+
321
+ # Check if the operation resulted in an error message
322
+ if isinstance(operation_result, str):
323
+ return operation_result
324
+
325
+ result = operation_result
326
+ i += 2
327
+
328
+ # Create a LaTeX-style matrix representation
329
+ matrix_entries = '\\\\'.join(' & '.join(str(x) for x in row) for row in result)
330
+ return r"\begin{bmatrix}" + f"{matrix_entries}" + r"\end{bmatrix}"
331
+
332
+ except Exception as e:
333
+ return f"Error computing matrix operation: {e}"
334
+
335
+
336
+ def compute_ordered_series_operation(expression: str) -> str:
337
+ """
338
+ Computes the result of operations on ordered series expressed as 1D lists, including discrete difference (ddd),
339
+ and returns it as a string.
340
+
341
+ The function first applies the discrete difference operator to any series where applicable, then evaluates
342
+ arithmetic operations between series.
343
+
344
+ Parameters:
345
+ expression (str): The series operation expression to compute. Includes operations "+", "-", "*", "/", and "ddd".
346
+
347
+ Returns:
348
+ str: The string representation of the resultant series after performing operations, or an error message
349
+ if the series lengths do not match.
350
+
351
+ Raises:
352
+ ValueError: If the expression cannot be evaluated.
353
+ """
354
+
355
+ def elementwise_operation(series1: List[float], series2: List[float], operation: str) -> List[float]:
356
+ if len(series1) != len(series2):
357
+ return "Operations between ordered series must involve series of equal length"
358
+
359
+ if operation == '+':
360
+ return [a + b for a, b in zip(series1, series2)]
361
+ elif operation == '-':
362
+ return [a - b for a, b in zip(series1, series2)]
363
+ elif operation == '*':
364
+ return [a * b for a, b in zip(series1, series2)]
365
+ elif operation == '/':
366
+ return [a / b for a, b in zip(series1, series2) if b != 0]
367
+ else:
368
+ return f"Unsupported operation {operation}"
369
+
370
+ def discrete_difference(series: list) -> list:
371
+ """Computes the discrete difference of a series."""
372
+ return [series[i + 1] - series[i] for i in range(len(series) - 1)]
373
+
374
+ try:
375
+ # First, apply the discrete difference operator where applicable
376
+ pattern = r'ddd\((\[[^\]]*\])\)'
377
+ matches = re.findall(pattern, expression)
378
+
379
+ for match in matches:
380
+ if match.strip() == '[]':
381
+ result_series = [] # Handle the empty list case
382
+ else:
383
+ series = ast.literal_eval(match)
384
+ result_series = discrete_difference(series)
385
+ expression = expression.replace(f'ddd({match})', str(result_series))
386
+
387
+ # Now parse and evaluate the full expression with basic operations
388
+ elements = []
389
+ buffer = ''
390
+ bracket_level = 0
391
+ operators = set('+-*/')
392
+
393
+ for char in expression:
394
+ if char == '[':
395
+ if bracket_level == 0 and buffer.strip():
396
+ elements.append(buffer.strip())
397
+ buffer = ''
398
+ bracket_level += 1
399
+ elif char == ']':
400
+ bracket_level -= 1
401
+ if bracket_level == 0:
402
+ buffer += char
403
+ elements.append(buffer.strip())
404
+ buffer = ''
405
+ continue
406
+ if bracket_level == 0 and char in operators:
407
+ if buffer.strip():
408
+ elements.append(buffer.strip())
409
+ buffer = ''
410
+ elements.append(char)
411
+ else:
412
+ buffer += char
413
+
414
+ if buffer.strip():
415
+ elements.append(buffer.strip())
416
+
417
+ result = ast.literal_eval(elements[0])
418
+
419
+ i = 1
420
+ while i < len(elements):
421
+ operation = elements[i]
422
+ series = ast.literal_eval(elements[i + 1])
423
+ operation_result = elementwise_operation(result, series, operation)
424
+
425
+ # Check if the operation resulted in an error message
426
+ if isinstance(operation_result, str):
427
+ return operation_result
428
+
429
+ result = operation_result
430
+ i += 2
431
+
432
+ return str(result)
433
+
434
+ except Exception as e:
435
+ return f"Error computing ordered series operation: {e}"
436
+
165
437
 
166
438
  def get_prime_factors_latex(n: int) -> str:
167
439
  """
168
- Return the prime factors of a number as a LaTeX expression.
440
+ Computes the prime factors of a number and returns the factorization as a LaTeX string.
441
+
442
+ Determines the prime factorization of the given integer. The result is formatted as a LaTeX
443
+ string, enabling easy integration into documents or presentations that require mathematical notation.
444
+
445
+ Parameters:
446
+ n (int): The number for which to compute prime factors.
447
+
448
+ Returns:
449
+ str: The LaTeX representation of the prime factorization.
169
450
  """
451
+
170
452
  factors = []
171
453
  while n % 2 == 0:
172
454
  factors.append(2)
@@ -179,8 +461,6 @@ def get_prime_factors_latex(n: int) -> str:
179
461
  factors.append(n)
180
462
 
181
463
  factor_counts = {factor: factors.count(factor) for factor in set(factors)}
182
- latex_factors = [f"{factor}^{{{count}}}" if count > 1 else str(factor) for factor, count in factor_counts.items()]
464
+ latex_factors = [f"{factor}^{{{count}}}" if count > 1 else str(
465
+ factor) for factor, count in factor_counts.items()]
183
466
  return " \\cdot ".join(latex_factors)
184
-
185
-
186
-
rgwfuncs/df_lib.py CHANGED
@@ -21,8 +21,8 @@ from email.mime.base import MIMEBase
21
21
  from email import encoders
22
22
  from googleapiclient.discovery import build
23
23
  import base64
24
- import inspect
25
- from typing import Optional, Callable, Dict, List, Tuple, Any
24
+ # import inspect
25
+ from typing import Optional, Dict, List, Tuple, Any
26
26
  import warnings
27
27
 
28
28
  # Suppress all FutureWarnings
@@ -798,7 +798,12 @@ def print_dataframe(df: pd.DataFrame, source: Optional[str] = None) -> None:
798
798
  gc.collect()
799
799
 
800
800
 
801
- def send_dataframe_via_telegram(df: pd.DataFrame, bot_name: str, message: Optional[str] = None, as_file: bool = True, remove_after_send: bool = True) -> None:
801
+ def send_dataframe_via_telegram(
802
+ df: pd.DataFrame,
803
+ bot_name: str,
804
+ message: Optional[str] = None,
805
+ as_file: bool = True,
806
+ remove_after_send: bool = True) -> None:
802
807
  """
803
808
  Send a DataFrame via Telegram using a specified bot configuration.
804
809
 
@@ -1635,7 +1640,12 @@ def print_n_frequency_cascading(
1635
1640
  report = generate_cascade_report(df, columns, n, order_by)
1636
1641
  print(json.dumps(report, indent=2))
1637
1642
 
1638
- def print_n_frequency_linear(df: pd.DataFrame, n: int, columns: list, order_by: str = "FREQ_DESC") -> None:
1643
+
1644
+ def print_n_frequency_linear(
1645
+ df: pd.DataFrame,
1646
+ n: int,
1647
+ columns: list,
1648
+ order_by: str = "FREQ_DESC") -> None:
1639
1649
  """
1640
1650
  Print the linear frequency of top n values for specified columns.
1641
1651
 
@@ -1682,23 +1692,36 @@ def print_n_frequency_linear(df: pd.DataFrame, n: int, columns: list, order_by:
1682
1692
  return val
1683
1693
 
1684
1694
  def sort_frequency(frequency, order_by):
1685
- keys = frequency.keys()
1695
+ # keys = frequency.keys()
1686
1696
 
1687
- # Convert keys to numerical values where possible, leaving `NaN` as a special string
1688
- parsed_keys = [(try_parse_numeric(key), key) for key in keys]
1697
+ # Convert keys to numerical values where possible, leaving `NaN` as a
1698
+ # special string
1699
+ # parsed_keys = [(try_parse_numeric(key), key) for key in keys]
1689
1700
 
1690
1701
  if order_by in {"BY_KEYS_ASC", "BY_KEYS_DESC"}:
1691
1702
  reverse = order_by == "BY_KEYS_DESC"
1692
- sorted_items = sorted(frequency.items(), key=lambda item: try_parse_numeric(item[0]), reverse=reverse)
1703
+ sorted_items = sorted(
1704
+ frequency.items(),
1705
+ key=lambda item: try_parse_numeric(
1706
+ item[0]),
1707
+ reverse=reverse)
1693
1708
  else:
1694
1709
  if order_by == "ASC":
1695
- sorted_items = sorted(frequency.items(), key=lambda item: item[0])
1710
+ sorted_items = sorted(
1711
+ frequency.items(), key=lambda item: item[0])
1696
1712
  elif order_by == "DESC":
1697
- sorted_items = sorted(frequency.items(), key=lambda item: item[0], reverse=True)
1713
+ sorted_items = sorted(
1714
+ frequency.items(),
1715
+ key=lambda item: item[0],
1716
+ reverse=True)
1698
1717
  elif order_by == "FREQ_ASC":
1699
- sorted_items = sorted(frequency.items(), key=lambda item: item[1])
1718
+ sorted_items = sorted(
1719
+ frequency.items(), key=lambda item: item[1])
1700
1720
  else: # Default to "FREQ_DESC"
1701
- sorted_items = sorted(frequency.items(), key=lambda item: item[1], reverse=True)
1721
+ sorted_items = sorted(
1722
+ frequency.items(),
1723
+ key=lambda item: item[1],
1724
+ reverse=True)
1702
1725
 
1703
1726
  return dict(sorted_items)
1704
1727
 
@@ -1850,7 +1873,10 @@ def right_join(
1850
1873
  return df1.merge(df2, how='right', left_on=left_on, right_on=right_on)
1851
1874
 
1852
1875
 
1853
- def insert_dataframe_in_sqlite_database(db_path: str, tablename: str, df: pd.DataFrame) -> None:
1876
+ def insert_dataframe_in_sqlite_database(
1877
+ db_path: str,
1878
+ tablename: str,
1879
+ df: pd.DataFrame) -> None:
1854
1880
  """
1855
1881
  Inserts a Pandas DataFrame into a SQLite database table.
1856
1882
 
@@ -1912,7 +1938,10 @@ def insert_dataframe_in_sqlite_database(db_path: str, tablename: str, df: pd.Dat
1912
1938
  df.to_sql(tablename, conn, if_exists='append', index=False)
1913
1939
 
1914
1940
 
1915
- def sync_dataframe_to_sqlite_database(db_path: str, tablename: str, df: pd.DataFrame) -> None:
1941
+ def sync_dataframe_to_sqlite_database(
1942
+ db_path: str,
1943
+ tablename: str,
1944
+ df: pd.DataFrame) -> None:
1916
1945
  """
1917
1946
  Processes and saves a DataFrame to an SQLite database, adding a timestamp column
1918
1947
  and replacing the existing table if needed. Creates the table if it does not exist.
rgwfuncs/docs_lib.py CHANGED
@@ -1,16 +1,17 @@
1
1
  import os
2
2
  import inspect
3
- from typing import Tuple, Optional, Dict, Callable
3
+ from typing import Optional
4
4
  import warnings
5
5
 
6
6
  # Suppress all FutureWarnings
7
7
  warnings.filterwarnings("ignore", category=FutureWarning)
8
8
 
9
+
9
10
  def docs(method_type_filter: Optional[str] = None) -> None:
10
11
  """
11
12
  Print a list of function names in alphabetical order from all modules.
12
13
  If method_type_filter is specified, print the docstrings of the functions
13
- that match the filter based on a substring. Using '*' as a filter will print
14
+ that match the filter based on a substring. Using '*' as a filter will print
14
15
  the docstrings for all functions.
15
16
 
16
17
  Parameters:
@@ -41,9 +42,10 @@ def docs(method_type_filter: Optional[str] = None) -> None:
41
42
  # List function names
42
43
  function_names = sorted(functions.keys())
43
44
  for name in function_names:
44
- # If a filter is provided or '*', check if the function name contains the filter
45
- if method_type_filter and (method_type_filter == '*' or method_type_filter in name):
45
+ # If a filter is provided or '*', check if the function name
46
+ # contains the filter
47
+ if method_type_filter and (
48
+ method_type_filter == '*' or method_type_filter in name):
46
49
  docstring: Optional[str] = functions[name].__doc__
47
50
  if docstring:
48
51
  print(f"\n{name}:\n{docstring}")
49
-
rgwfuncs/str_lib.py CHANGED
@@ -1,8 +1,7 @@
1
1
  import os
2
2
  import json
3
3
  import requests
4
- import inspect
5
- from typing import Tuple, Optional, Dict, Callable
4
+ from typing import Tuple
6
5
  import warnings
7
6
 
8
7
  # Suppress all FutureWarnings
@@ -37,19 +36,20 @@ def send_telegram_message(preset_name: str, message: str) -> None:
37
36
  return preset
38
37
  return None
39
38
 
40
- def get_telegram_bot_details(config: dict, preset_name: str) -> Tuple[str, str]:
39
+ def get_telegram_bot_details(
40
+ config: dict, preset_name: str) -> Tuple[str, str]:
41
41
  """Retrieve the Telegram bot token and chat ID from the preset."""
42
42
  preset = get_telegram_preset(config, preset_name)
43
43
  if not preset:
44
- raise RuntimeError(f"Telegram bot preset '{preset_name}' not found in the configuration file")
44
+ raise RuntimeError(
45
+ f"Telegram bot preset '{preset_name}' not found in the configuration file")
45
46
 
46
47
  bot_token = preset.get("bot_token")
47
48
  chat_id = preset.get("chat_id")
48
49
 
49
50
  if not bot_token or not chat_id:
50
51
  raise RuntimeError(
51
- f"Telegram bot token or chat ID for '{preset_name}' not found in the configuration file"
52
- )
52
+ f"Telegram bot token or chat ID for '{preset_name}' not found in the configuration file")
53
53
 
54
54
  return bot_token, chat_id
55
55
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rgwfuncs
3
- Version: 0.0.25
3
+ Version: 0.0.27
4
4
  Summary: A functional programming paradigm for mathematical modelling and data science
5
5
  Home-page: https://github.com/ryangerardwilson/rgwfunc
6
6
  Author: Ryan Gerard Wilson
@@ -252,6 +252,66 @@ Computes prime factors of a number and presents them in LaTeX format.
252
252
 
253
253
  --------------------------------------------------------------------------------
254
254
 
255
+ ### 5. `compute_matrix_operation`
256
+
257
+ Computes the results of 1D or 2D matrix operations and formats them as LaTeX strings.
258
+
259
+ - **Parameters:**
260
+ - `expression` (str): A string representing a sequence of matrix operations involving either 1D or 2D lists. Supported operations include addition (`+`), subtraction (`-`), multiplication (`*`), and division (`/`).
261
+
262
+ - **Returns:**
263
+ - `str`: The LaTeX-formatted string representation of the computed matrix, or an error message if the operations cannot be performed due to dimensional mismatches.
264
+
265
+ - **Example:**
266
+
267
+ from rgwfuncs import compute_matrix_operation
268
+
269
+ # Example with addition of 2D matrices
270
+ result = compute_matrix_operation("[[2, 6, 9], [1, 3, 5]] + [[1, 2, 3], [4, 5, 6]]")
271
+ print(result) # Output: \begin{bmatrix}3 & 8 & 12\\5 & 8 & 11\end{bmatrix}
272
+
273
+ # Example of mixed operations with 1D matrices treated as 2D
274
+ result = compute_matrix_operation("[3, 6, 9] + [1, 2, 3] - [2, 2, 2]")
275
+ print(result) # Output: \begin{bmatrix}2 & 6 & 10\end{bmatrix}
276
+
277
+ # Example with dimension mismatch
278
+ result = compute_matrix_operation("[[4, 3, 51]] + [[1, 1]]")
279
+ print(result) # Output: Operations between matrices must involve matrices of the same dimension
280
+
281
+ This function performs elementwise operations on both 1D and 2D matrices represented as Python lists and formats the result as a LaTeX string. It handles operations sequentially from left to right and gracefully handles dimension mismatches by returning a meaningful message. It utilizes Python's `ast.literal_eval` for safe and robust parsing.
282
+
283
+ --------------------------------------------------------------------------------
284
+
285
+ ### 6. `compute_ordered_series_operations`
286
+
287
+ Computes the result of operations on ordered series expressed as 1D lists, including the discrete difference operator `ddd`.
288
+
289
+ - **Parameters:**
290
+ - `expression` (str): A series operation expression. Supports operations such as "+", "-", "*", "/", and `ddd` for discrete differences.
291
+
292
+ - **Returns:**
293
+ - `str`: The string representation of the resultant series after performing operations, or an error message if series lengths do not match.
294
+
295
+ - **Example:**
296
+
297
+ from rgwfuncs import compute_ordered_series_operations
298
+
299
+ # Example with addition and discrete differences
300
+ result = compute_ordered_series_operations("ddd([2, 6, 9, 60]) + ddd([78, 79, 80])")
301
+ print(result) # Output: [4, 3, 51] + [1, 1]
302
+
303
+ # Example with elementwise subtraction
304
+ result = compute_ordered_series_operations("[10, 15, 21] - [5, 5, 5]")
305
+ print(result) # Output: [5, 10, 16]
306
+
307
+ # Example with length mismatch
308
+ result = compute_ordered_series_operations("[4, 3, 51] + [1, 1]")
309
+ print(result) # Output: Operations between ordered series must involve series of equal length
310
+
311
+ This function first applies the discrete difference operator to any series where applicable, then evaluates arithmetic operations between series. It returns a string representation of the result or an error message if the series lengths do not match. The function is robust, directly parsing and evaluating given series expressions with safety checks in place.
312
+
313
+ --------------------------------------------------------------------------------
314
+
255
315
  ## String Based Functions
256
316
 
257
317
  ### 1. send_telegram_message
@@ -0,0 +1,11 @@
1
+ rgwfuncs/__init__.py,sha256=6QXVaHomLh1AIbC-b-edLf-GS1oEqRT-JffHPNIKowA,1375
2
+ rgwfuncs/algebra_lib.py,sha256=VZS0d-sUGJvfHv00HmgD1htxBGbugsKkKuUKsJbLPkI,18205
3
+ rgwfuncs/df_lib.py,sha256=G_H3PXNVeseX2YLjkkrmO9eXA_7r29swUZlbPBDZjXA,66612
4
+ rgwfuncs/docs_lib.py,sha256=y3wSAOPO3qsA4HZ7xAtW8HimM8w-c8hjcEzMRLJ96ao,1960
5
+ rgwfuncs/str_lib.py,sha256=rtAdRlnSJIu3JhI-tA_A0wCiPK2m-zn5RoGpBxv_g-4,2228
6
+ rgwfuncs-0.0.27.dist-info/LICENSE,sha256=7EI8xVBu6h_7_JlVw-yPhhOZlpY9hP8wal7kHtqKT_E,1074
7
+ rgwfuncs-0.0.27.dist-info/METADATA,sha256=NSNgM-VFveOpzvmosZgjg7kSC9KovbyXspCTXCdMdrU,41874
8
+ rgwfuncs-0.0.27.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
9
+ rgwfuncs-0.0.27.dist-info/entry_points.txt,sha256=j-c5IOPIQ0252EaOV6j6STio56sbXl2C4ym_fQ0lXx0,43
10
+ rgwfuncs-0.0.27.dist-info/top_level.txt,sha256=aGuVIzWsKiV1f2gCb6mynx0zx5ma0B1EwPGFKVEMTi4,9
11
+ rgwfuncs-0.0.27.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- rgwfuncs/__init__.py,sha256=SZg1HPP5D_3QimoYFH8zongQ9D9XPZWp-Qi-MZglvXw,1315
2
- rgwfuncs/algebra_lib.py,sha256=aayZogB2Rp9JAo5kVHpauqX_R346eI_rIuE5QNEMlKM,7789
3
- rgwfuncs/df_lib.py,sha256=OfbnAii_RND_euTJVou9nJaDqRLNbIMTCbaBelAUDvk,66247
4
- rgwfuncs/docs_lib.py,sha256=vlO8Rr6PYzyd2ZAenV_6t_iZJ3CoHja3PSLJverlAT4,1941
5
- rgwfuncs/str_lib.py,sha256=PHvxAg7_mZ_xe7DWJiAQ-PQ2fkYddKe8G0iQ1L78aZ0,2252
6
- rgwfuncs-0.0.25.dist-info/LICENSE,sha256=7EI8xVBu6h_7_JlVw-yPhhOZlpY9hP8wal7kHtqKT_E,1074
7
- rgwfuncs-0.0.25.dist-info/METADATA,sha256=RQIG8bS4SFwHTycxQXpaYnyw2C8UIkQZEe7EfivMwao,38637
8
- rgwfuncs-0.0.25.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
9
- rgwfuncs-0.0.25.dist-info/entry_points.txt,sha256=j-c5IOPIQ0252EaOV6j6STio56sbXl2C4ym_fQ0lXx0,43
10
- rgwfuncs-0.0.25.dist-info/top_level.txt,sha256=aGuVIzWsKiV1f2gCb6mynx0zx5ma0B1EwPGFKVEMTi4,9
11
- rgwfuncs-0.0.25.dist-info/RECORD,,