rgwfuncs 0.0.22__py3-none-any.whl → 0.0.24__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,5 +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
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, df_docs, 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
+ from .docs_lib import docs
5
7
  from .str_lib import send_telegram_message, str_docs
@@ -0,0 +1,186 @@
1
+ import re
2
+ import math
3
+ from sympy import symbols, latex, simplify, solve, diff, Expr
4
+ from sympy.parsing.sympy_parser import parse_expr
5
+ from typing import Tuple, List, Dict, Optional
6
+
7
+ def compute_algebraic_expression(expression: str) -> float:
8
+ try:
9
+ # Direct numerical evaluation
10
+ # Safely evaluate the expression using the math module
11
+ numeric_result = eval(expression, {"__builtins__": None, "math": math})
12
+
13
+ # Convert to float if possible
14
+ return float(numeric_result)
15
+ except Exception as e:
16
+ raise ValueError(f"Error computing expression: {e}")
17
+
18
+ def simplify_algebraic_expression(expression: str) -> str:
19
+
20
+
21
+ def recursive_parse_function_call(func_call: str, prefix: str, sym_vars: Dict[str, Expr]) -> Tuple[str, List[Expr]]:
22
+ # print(f"Parsing function call: {func_call}")
23
+
24
+ # Match the function name and arguments
25
+ match = re.match(fr'{prefix}\.(\w+)\((.*)\)', func_call, re.DOTALL)
26
+ if not match:
27
+ raise ValueError(f"Invalid function call: {func_call}")
28
+
29
+ func_name = match.group(1)
30
+ args_str = match.group(2)
31
+
32
+ # Check if it's a list for np
33
+ if prefix == 'np' and args_str.startswith("[") and args_str.endswith("]"):
34
+ parsed_args = [ast.literal_eval(args_str.strip())]
35
+ else:
36
+ parsed_args = []
37
+ raw_args = re.split(r',(?![^{]*\})', args_str)
38
+ for arg in raw_args:
39
+ arg = arg.strip()
40
+ 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))
44
+ else:
45
+ parsed_args.append(parse_expr(arg, local_dict=sym_vars))
46
+
47
+ # print(f"Function name: {func_name}, Parsed arguments: {parsed_args}")
48
+ return func_name, parsed_args
49
+
50
+
51
+ def recursive_eval_func(match: re.Match, sym_vars: Dict[str, Expr]) -> str:
52
+ # print("152", match)
53
+ func_call = match.group(0)
54
+ # print(f"153 Evaluating function call: {func_call}")
55
+
56
+ if func_call.startswith("np."):
57
+ func_name, args = recursive_parse_function_call(func_call, 'np', sym_vars)
58
+ if func_name == 'diff':
59
+ expr = args[0]
60
+ if isinstance(expr, list):
61
+ # Calculate discrete difference
62
+ diff_result = [expr[i] - expr[i - 1] for i in range(1, len(expr))]
63
+ return str(diff_result)
64
+ # Perform symbolic differentiation
65
+ diff_result = diff(expr)
66
+ return str(diff_result)
67
+
68
+ if func_call.startswith("math."):
69
+ func_name, args = recursive_parse_function_call(func_call, 'math', sym_vars)
70
+ if hasattr(math, func_name):
71
+ result = getattr(math, func_name)(*args)
72
+ return str(result)
73
+
74
+ if func_call.startswith("sym."):
75
+ initial_method_match = re.match(r'(sym\.\w+\([^()]*\))(\.(\w+)\((.*?)\))*', func_call, re.DOTALL)
76
+ if initial_method_match:
77
+ base_expr_str = initial_method_match.group(1)
78
+ base_func_name, base_args = recursive_parse_function_call(base_expr_str, 'sym', sym_vars)
79
+ if base_func_name == 'solve':
80
+ solutions = solve(base_args[0], base_args[1])
81
+ # print(f"Solutions found: {solutions}")
82
+
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]
85
+
86
+ return "[" + ",".join(latex(simplify(sol)) for sol in final_solutions) + "]"
87
+
88
+ raise ValueError(f"Unknown function call: {func_call}")
89
+
90
+ def execute_chained_methods(sym_expr: Expr, method_chain: List[Tuple[str, List[str]]], sym_vars: Dict[str, Expr]) -> Expr:
91
+ for method_name, method_args in method_chain:
92
+ # print(f"Executing method: {method_name} with arguments: {method_args}")
93
+ method = getattr(sym_expr, method_name, None)
94
+ if method:
95
+ if method_name == 'subs' and isinstance(method_args[0], dict):
96
+ 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()}
98
+ sym_expr = method(kwargs)
99
+ else:
100
+ args = [parse_expr(arg.strip(), local_dict=sym_vars) for arg in method_args]
101
+ sym_expr = method(*args)
102
+ # print(f"Result after {method_name}: {sym_expr}")
103
+ return sym_expr
104
+
105
+
106
+
107
+ variable_names = set(re.findall(r'\b[a-zA-Z]\w*\b', expression))
108
+ sym_vars = {var: symbols(var) for var in variable_names}
109
+
110
+ patterns = {
111
+ #"numpy_diff_brackets": r"np\.diff\(\[.*?\]\)",
112
+ "numpy_diff_no_brackets": r"np\.diff\([^()]*\)",
113
+ "math_functions": r"math\.\w+\((?:[^()]*(?:\([^()]*\)[^()]*)*)\)",
114
+ # "sympy_functions": r"sym\.\w+\([^()]*\)(?:\.\w+\([^()]*\))?",
115
+ }
116
+
117
+ function_pattern = '|'.join(patterns.values())
118
+
119
+ # Use a lambda function to pass additional arguments
120
+ processed_expression = re.sub(function_pattern, lambda match: recursive_eval_func(match, sym_vars), expression)
121
+ # print("Level 2 processed_expression:", processed_expression)
122
+
123
+ try:
124
+ if processed_expression.startswith('[') and processed_expression.endswith(']'):
125
+ return processed_expression
126
+
127
+ expr = parse_expr(processed_expression, local_dict=sym_vars)
128
+ final_result = simplify(expr)
129
+
130
+ if final_result.free_symbols:
131
+ latex_result = latex(final_result)
132
+ return latex_result
133
+ else:
134
+ return str(final_result)
135
+
136
+ except Exception as e:
137
+ raise ValueError(f"Error simplifying expression: {e}")
138
+
139
+ def solve_algebraic_expression(expression: str, variable: str, subs: Optional[Dict[str, float]] = None) -> str:
140
+ try:
141
+ # Create symbols for the variables in the expression
142
+ variable_symbols = set(re.findall(r'\b[a-zA-Z]\w*\b', expression))
143
+ sym_vars = {var: symbols(var) for var in variable_symbols}
144
+
145
+ # Parse the expression and solve it
146
+ expr = parse_expr(expression, local_dict=sym_vars)
147
+ var_symbol = symbols(variable)
148
+ solutions = solve(expr, var_symbol)
149
+
150
+ # Apply substitutions if provided
151
+ if subs:
152
+ subs_symbols = {symbols(k): v for k, v in subs.items()}
153
+ solutions = [simplify(sol.subs(subs_symbols)) for sol in solutions]
154
+
155
+ # Convert solutions to LaTeX strings if possible
156
+ latex_solutions = [latex(simplify(sol)) if sol.free_symbols else str(sol) for sol in solutions]
157
+ result = r"\left[" + ", ".join(latex_solutions) + r"\right]"
158
+ print("158", result)
159
+ return result
160
+
161
+ except Exception as e:
162
+ raise ValueError(f"Error solving the expression: {e}")
163
+
164
+
165
+
166
+ def get_prime_factors_latex(n: int) -> str:
167
+ """
168
+ Return the prime factors of a number as a LaTeX expression.
169
+ """
170
+ factors = []
171
+ while n % 2 == 0:
172
+ factors.append(2)
173
+ n //= 2
174
+ for i in range(3, int(math.sqrt(n)) + 1, 2):
175
+ while n % i == 0:
176
+ factors.append(i)
177
+ n //= i
178
+ if n > 2:
179
+ factors.append(n)
180
+
181
+ 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()]
183
+ return " \\cdot ".join(latex_factors)
184
+
185
+
186
+
rgwfuncs/df_lib.py CHANGED
@@ -1708,47 +1708,36 @@ def print_n_frequency_linear(df: pd.DataFrame, n: int, columns: list, order_by:
1708
1708
 
1709
1709
  return report
1710
1710
 
1711
- def can_convert_keys_to_int(keys):
1712
- """Helper function to check if all keys can be converted to integers."""
1711
+ def try_parse_numeric(val):
1712
+ """Attempt to parse a value as an integer or float."""
1713
1713
  try:
1714
- [int(key) for key in keys]
1715
- return True
1714
+ return int(val)
1716
1715
  except ValueError:
1717
- return False
1716
+ try:
1717
+ return float(val)
1718
+ except ValueError:
1719
+ return val
1718
1720
 
1719
1721
  def sort_frequency(frequency, order_by):
1720
1722
  keys = frequency.keys()
1721
-
1722
- # Determine if we can sort by integer values of keys
1723
- if can_convert_keys_to_int(keys):
1724
- keys = list(map(int, keys)) # Convert keys to integers, and keep original order
1725
1723
 
1726
- if order_by == "ASC":
1727
- return dict(sorted(frequency.items(), key=lambda item: item[0]))
1728
- elif order_by == "DESC":
1729
- return dict(
1730
- sorted(
1731
- frequency.items(),
1732
- key=lambda item: item[0],
1733
- reverse=True))
1734
- elif order_by == "FREQ_ASC":
1735
- return dict(sorted(frequency.items(), key=lambda item: item[1]))
1736
- elif order_by == "BY_KEYS_ASC":
1737
- if isinstance(keys[0], int):
1738
- return dict(sorted(frequency.items(), key=lambda item: int(item[0])))
1739
- else:
1740
- return dict(sorted(frequency.items()))
1741
- elif order_by == "BY_KEYS_DESC":
1742
- if isinstance(keys[0], int):
1743
- return dict(sorted(frequency.items(), key=lambda item: int(item[0]), reverse=True))
1744
- else:
1745
- return dict(sorted(frequency.items(), reverse=True))
1746
- else: # Default to "FREQ_DESC"
1747
- return dict(
1748
- sorted(
1749
- frequency.items(),
1750
- key=lambda item: item[1],
1751
- reverse=True))
1724
+ # Convert keys to numerical values where possible, leaving `NaN` as a special string
1725
+ parsed_keys = [(try_parse_numeric(key), key) for key in keys]
1726
+
1727
+ if order_by in {"BY_KEYS_ASC", "BY_KEYS_DESC"}:
1728
+ reverse = order_by == "BY_KEYS_DESC"
1729
+ sorted_items = sorted(frequency.items(), key=lambda item: try_parse_numeric(item[0]), reverse=reverse)
1730
+ else:
1731
+ if order_by == "ASC":
1732
+ sorted_items = sorted(frequency.items(), key=lambda item: item[0])
1733
+ elif order_by == "DESC":
1734
+ sorted_items = sorted(frequency.items(), key=lambda item: item[0], reverse=True)
1735
+ elif order_by == "FREQ_ASC":
1736
+ sorted_items = sorted(frequency.items(), key=lambda item: item[1])
1737
+ else: # Default to "FREQ_DESC"
1738
+ sorted_items = sorted(frequency.items(), key=lambda item: item[1], reverse=True)
1739
+
1740
+ return dict(sorted_items)
1752
1741
 
1753
1742
  report = generate_linear_report(df, columns, n, order_by)
1754
1743
  print(json.dumps(report, indent=2))
rgwfuncs/docs_lib.py ADDED
@@ -0,0 +1,50 @@
1
+ import os
2
+ import inspect
3
+ from typing import Tuple, Optional, Dict, Callable
4
+ import warnings
5
+
6
+ # Suppress all FutureWarnings
7
+ warnings.filterwarnings("ignore", category=FutureWarning)
8
+
9
+ def docs(method_type_filter: Optional[str] = None) -> None:
10
+ """
11
+ Print a list of function names in alphabetical order from all modules.
12
+ If method_type_filter is specified, print the docstrings of the functions
13
+ that match the filter. Using '*' as a filter will print the docstrings
14
+ for all functions.
15
+
16
+ Parameters:
17
+ method_type_filter: Optional filter string representing a function name,
18
+ or '*' to display docstrings for all functions.
19
+ """
20
+
21
+ # Directory containing your modules
22
+ module_dir = os.path.dirname(__file__)
23
+
24
+ # Iterate over each file in the module directory
25
+ for filename in sorted(os.listdir(module_dir)):
26
+ if filename.endswith('.py') and filename != '__init__.py':
27
+ module_name, _ = os.path.splitext(filename)
28
+ print(f"\n# {module_name}.py")
29
+
30
+ # Import the module
31
+ module_path = f"rgwfuncs.{module_name}"
32
+ module = __import__(module_path, fromlist=[module_name])
33
+
34
+ # Get all functions from the module
35
+ functions = {
36
+ name: obj for name, obj
37
+ in inspect.getmembers(module, inspect.isfunction)
38
+ if obj.__module__ == module_path
39
+ }
40
+
41
+ # List function names
42
+ function_names = sorted(functions.keys())
43
+ for name in function_names:
44
+ print(f"{name}")
45
+
46
+ # If a filter is provided or '*', print the docstring
47
+ if method_type_filter and (method_type_filter == '*' or method_type_filter == name):
48
+ docstring: Optional[str] = functions[name].__doc__
49
+ if docstring:
50
+ print(f"\n{name}:\n{docstring}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rgwfuncs
3
- Version: 0.0.22
3
+ Version: 0.0.24
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
@@ -1002,22 +1002,31 @@ Print the cascading frequency of top n values for specified columns.
1002
1002
  --------------------------------------------------------------------------------
1003
1003
 
1004
1004
  ### 37. `print_n_frequency_linear`
1005
- Print the linear frequency of top n values for specified columns.
1006
1005
 
1007
- Parameters:
1008
- - df (pd.DataFrame)
1009
- - n (int)
1010
- - columns (str): Comma-separated columns.
1011
- - `order_by` (str)
1006
+ Prints the linear frequency of the top `n` values for specified columns.
1007
+
1008
+ #### Parameters:
1009
+ - **df** (`pd.DataFrame`): The DataFrame to analyze.
1010
+ - **n** (`int`): The number of top values to print for each column.
1011
+ - **columns** (`list`): A list of column names to be analyzed.
1012
+ - **order_by** (`str`): The order of frequency. The available options are:
1013
+ - `"ASC"`: Sort keys in ascending lexicographical order.
1014
+ - `"DESC"`: Sort keys in descending lexicographical order.
1015
+ - `"FREQ_ASC"`: Sort the frequencies in ascending order (least frequent first).
1016
+ - `"FREQ_DESC"`: Sort the frequencies in descending order (most frequent first).
1017
+ - `"BY_KEYS_ASC"`: Sort keys in ascending order, numerically if possible, handling special strings like 'NaN' as typical entries.
1018
+ - `"BY_KEYS_DESC"`: Sort keys in descending order, numerically if possible, handling special strings like 'NaN' as typical entries.
1019
+
1020
+ #### Example:
1012
1021
 
1013
- • Example:
1014
-
1015
1022
  from rgwfuncs import print_n_frequency_linear
1016
1023
  import pandas as pd
1017
1024
 
1018
- df = pd.DataFrame({'City': ['NY','LA','NY','SF','LA','LA']})
1019
- print_n_frequency_linear(df, 2, 'City', 'FREQ_DESC')
1020
-
1025
+ df = pd.DataFrame({'City': ['NY', 'LA', 'NY', 'SF', 'LA', 'LA']})
1026
+ print_n_frequency_linear(df, 2, ['City'], 'FREQ_DESC')
1027
+
1028
+ This example analyzes the `City` column, printing the top 2 most frequent values in descending order of frequency.
1029
+
1021
1030
 
1022
1031
  --------------------------------------------------------------------------------
1023
1032
 
@@ -0,0 +1,11 @@
1
+ rgwfuncs/__init__.py,sha256=UrSka0KkoaZfLtODgbEbGvo67-L0LK1-e9waRn2a95g,1334
2
+ rgwfuncs/algebra_lib.py,sha256=aayZogB2Rp9JAo5kVHpauqX_R346eI_rIuE5QNEMlKM,7789
3
+ rgwfuncs/df_lib.py,sha256=8KMn4FucI19EFBHUoGOS7R4mo0degg6A6802sjy7BH4,67677
4
+ rgwfuncs/docs_lib.py,sha256=iZlQMNS52FuiblCI0oXJVznSuCndeG6WqZfsm-Xnd7U,1918
5
+ rgwfuncs/str_lib.py,sha256=I5B0WOGaLUGaedMG7hqiKnIqV7Jc9h1RYlgOiC_-iGY,3678
6
+ rgwfuncs-0.0.24.dist-info/LICENSE,sha256=7EI8xVBu6h_7_JlVw-yPhhOZlpY9hP8wal7kHtqKT_E,1074
7
+ rgwfuncs-0.0.24.dist-info/METADATA,sha256=-Nv5cA1xWJNfDSp53_jkaTce5n93hAH7mS7bv2IFWes,35516
8
+ rgwfuncs-0.0.24.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
9
+ rgwfuncs-0.0.24.dist-info/entry_points.txt,sha256=j-c5IOPIQ0252EaOV6j6STio56sbXl2C4ym_fQ0lXx0,43
10
+ rgwfuncs-0.0.24.dist-info/top_level.txt,sha256=aGuVIzWsKiV1f2gCb6mynx0zx5ma0B1EwPGFKVEMTi4,9
11
+ rgwfuncs-0.0.24.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- rgwfuncs/__init__.py,sha256=2nrp3c5VmVrKh0Ih6zELL8niH9nAHN0XnObqe-EpxlE,1169
2
- rgwfuncs/df_lib.py,sha256=nd-F7NGraUINuZUIqYHo2QwUNTjESgBlxKDQbbqlhS8,68050
3
- rgwfuncs/str_lib.py,sha256=I5B0WOGaLUGaedMG7hqiKnIqV7Jc9h1RYlgOiC_-iGY,3678
4
- rgwfuncs-0.0.22.dist-info/LICENSE,sha256=7EI8xVBu6h_7_JlVw-yPhhOZlpY9hP8wal7kHtqKT_E,1074
5
- rgwfuncs-0.0.22.dist-info/METADATA,sha256=aQ86F5Sb5XUGej267Y-hwVuAYqObbuNfy5vcbCxmgYY,34680
6
- rgwfuncs-0.0.22.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
7
- rgwfuncs-0.0.22.dist-info/entry_points.txt,sha256=j-c5IOPIQ0252EaOV6j6STio56sbXl2C4ym_fQ0lXx0,43
8
- rgwfuncs-0.0.22.dist-info/top_level.txt,sha256=aGuVIzWsKiV1f2gCb6mynx0zx5ma0B1EwPGFKVEMTi4,9
9
- rgwfuncs-0.0.22.dist-info/RECORD,,