sovai 0.2.17__tar.gz → 0.2.18__tar.gz
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.
- {sovai-0.2.17 → sovai-0.2.18}/PKG-INFO +1 -1
- {sovai-0.2.17 → sovai-0.2.18}/pyproject.toml +1 -1
- {sovai-0.2.17 → sovai-0.2.18}/sovai/__init__.py +5 -3
- sovai-0.2.18/sovai/extensions/ask_df_llm.py +311 -0
- sovai-0.2.18/sovai/extensions/filter_df.py +513 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/pairwise.py +34 -9
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/pandas_extensions.py +32 -2
- {sovai-0.2.17 → sovai-0.2.18}/sovai/get_data.py +158 -0
- sovai-0.2.18/sovai/get_plots.py +225 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/get_reports.py +13 -2
- {sovai-0.2.17 → sovai-0.2.18}/sovai/get_tools.py +10 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/accounting/accounting_plots.py +3 -3
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/bankruptcy/bankruptcy_plots.py +21 -14
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/corp_risk/corp_risk_plots.py +23 -2
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/earnings_surprise/earnings_surprise_plots.py +2 -1
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/insider/insider_plots.py +5 -5
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/institutional/institutional_plots.py +9 -34
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/news/news_plots.py +13 -25
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/ratios/ratios_plots.py +2 -1
- {sovai-0.2.17 → sovai-0.2.18}/sovai/reports/accounting/accounting_balance_sheet.py +6 -2
- {sovai-0.2.17 → sovai-0.2.18}/sovai/reports/general/general_plots.py +3 -1
- sovai-0.2.18/sovai/tools/sec/llm_code_generator.py +101 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/utils/client_side.py +51 -12
- {sovai-0.2.17 → sovai-0.2.18}/sovai/utils/file_management.py +35 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/utils/helpers.py +2 -0
- sovai-0.2.18/sovai/utils/port_manager.py +25 -0
- sovai-0.2.17/sovai/get_plots.py +0 -201
- {sovai-0.2.17 → sovai-0.2.18}/LICENSE +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/README.md +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/api_config.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/assets/dropdown.css +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/assets/features_mapping.parq +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/basic_auth.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/computations/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/computations/functions.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/errors/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/errors/sovai_errors.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/anomalies.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/change_point_generator.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/clustering.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/core_kshape.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/cum_sum_torch.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/cusum.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/dimensionality_reduction.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/feature_extraction.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/feature_importance.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/feature_neutralizer.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/fractional_differencing.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/nowcasting.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/parallel_functions.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/pfa_feature_selector.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/regime_change.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/regime_change_pca.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/shapley_global_importance.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/shapley_importance.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/signal_evaluation.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/technical_indicators.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/time_decomposition.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/extensions/weight_optimization.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/get_compute.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/accounting/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/allocation/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/allocation/allocation_plots.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/bankruptcy/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/breakout/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/breakout/breakout_plots.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/corp_risk/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/earnings_surprise/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/insider/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/institutional/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/news/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/plots/ratios/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/reports/accounting/__inti__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/reports/bankruptcy/__inti__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/reports/bankruptcy/bankruptcy_monthly_top.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/reports/general/__inti__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/reports/news/__inti__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/reports/news/news_econometric_analysis.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/studies/nowcasting.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/token_auth.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/tools/sec/__inti__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/tools/sec/sec_10_k_8_k_filings.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/tools/sec/sec_edgar_search.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/utils/__init__.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/utils/converter.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/utils/datetime_formats.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/utils/get_tickers.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/utils/pandas_extensions copy.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/utils/plot.py +0 -0
- {sovai-0.2.17 → sovai-0.2.18}/sovai/utils/stream.py +0 -0
|
@@ -5,20 +5,22 @@ from .api_config import read_key, save_key, ApiConfig
|
|
|
5
5
|
from .get_data import data
|
|
6
6
|
from .basic_auth import basic_auth
|
|
7
7
|
from .token_auth import token_auth
|
|
8
|
-
from .utils.file_management import save_or_update_tickers
|
|
8
|
+
from .utils.file_management import save_or_update_tickers, save_or_update_codes
|
|
9
9
|
|
|
10
10
|
__version__ = "0.1.25"
|
|
11
11
|
|
|
12
12
|
# Call the function to ensure tickers data is up-to-date
|
|
13
13
|
save_or_update_tickers()
|
|
14
14
|
|
|
15
|
+
save_or_update_codes()
|
|
16
|
+
|
|
15
17
|
try:
|
|
16
18
|
from .get_plots import plot
|
|
17
19
|
from .get_reports import report
|
|
18
20
|
from .get_compute import compute
|
|
19
21
|
from .studies.nowcasting import nowcast
|
|
20
22
|
from .extensions.pandas_extensions import CustomDataFrame as extension
|
|
21
|
-
from .get_tools import sec_search, sec_filing
|
|
23
|
+
from .get_tools import sec_search, sec_filing, code
|
|
22
24
|
HAS_FULL_INSTALL = True
|
|
23
25
|
except ImportError:
|
|
24
26
|
HAS_FULL_INSTALL = False
|
|
@@ -26,4 +28,4 @@ except ImportError:
|
|
|
26
28
|
__all__ = ['read_key', 'save_key', 'ApiConfig', 'data', 'basic_auth', 'token_auth']
|
|
27
29
|
|
|
28
30
|
if HAS_FULL_INSTALL:
|
|
29
|
-
__all__ += ['plot', 'report', 'compute', 'nowcast', 'extension', 'sec_search', 'sec_filing']
|
|
31
|
+
__all__ += ['plot', 'report', 'compute', 'nowcast', 'extension', 'sec_search', 'sec_filing', 'code']
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import openai
|
|
3
|
+
from textwrap import dedent
|
|
4
|
+
import re
|
|
5
|
+
from typing import Dict, Any, List, Union, Tuple
|
|
6
|
+
import io
|
|
7
|
+
from sovai.tools.sec.llm_code_generator import get_openai_key
|
|
8
|
+
import logging
|
|
9
|
+
from rapidfuzz import process, fuzz
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
# Add these lines at the beginning of the file
|
|
13
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
14
|
+
|
|
15
|
+
_ask_cache: Dict[str, Any] = {}
|
|
16
|
+
|
|
17
|
+
verbose = False
|
|
18
|
+
mutable = False
|
|
19
|
+
models = ['gpt-3.5-turbo', 'gpt-4']
|
|
20
|
+
completion_config = {}
|
|
21
|
+
|
|
22
|
+
def sample_unique_values(df, max_samples=5):
|
|
23
|
+
sample_data = {}
|
|
24
|
+
for column in df.columns:
|
|
25
|
+
unique_values = df[column].dropna().unique()
|
|
26
|
+
sample = unique_values[:max_samples].tolist()
|
|
27
|
+
sample_data[column] = sample
|
|
28
|
+
return sample_data
|
|
29
|
+
|
|
30
|
+
def format_sample_data(sample_data):
|
|
31
|
+
formatted_data = "Sample data from the DataFrame:\n\n"
|
|
32
|
+
for column, values in sample_data.items():
|
|
33
|
+
formatted_data += f"{column}:\n"
|
|
34
|
+
for value in values:
|
|
35
|
+
formatted_data += f" - {value}\n"
|
|
36
|
+
formatted_data += "\n"
|
|
37
|
+
return formatted_data
|
|
38
|
+
|
|
39
|
+
def find_best_match(query: str, choices: List[str], threshold: int = 80) -> Union[str, None]:
|
|
40
|
+
best_match = process.extractOne(query, choices, scorer=fuzz.ratio, score_cutoff=threshold)
|
|
41
|
+
return best_match[0] if best_match else None
|
|
42
|
+
|
|
43
|
+
def parse_value(value: str) -> Union[float, int]:
|
|
44
|
+
multipliers = {'k': 1e3, 'm': 1e6, 'b': 1e9}
|
|
45
|
+
if value.endswith('%'):
|
|
46
|
+
return float(value.rstrip('%')) / 100
|
|
47
|
+
for suffix, multiplier in multipliers.items():
|
|
48
|
+
if value.lower().endswith(suffix):
|
|
49
|
+
return float(value[:-1]) * multiplier
|
|
50
|
+
return float(value)
|
|
51
|
+
|
|
52
|
+
def create_standard_query(condition: str, df: pd.DataFrame) -> Tuple[str, str]:
|
|
53
|
+
pattern = r'(\w+)\s*([<>=]+|like|contains)\s*(.+)'
|
|
54
|
+
match = re.match(pattern, condition, re.IGNORECASE)
|
|
55
|
+
if not match:
|
|
56
|
+
raise ValueError(f"Invalid condition format: {condition}")
|
|
57
|
+
|
|
58
|
+
column, operator, value = match.groups()
|
|
59
|
+
best_match = find_best_match(column, df.columns.tolist())
|
|
60
|
+
if not best_match:
|
|
61
|
+
raise ValueError(f"No close match found for column: {column}")
|
|
62
|
+
|
|
63
|
+
original_column = column
|
|
64
|
+
column = best_match
|
|
65
|
+
|
|
66
|
+
operator = operator.lower().strip()
|
|
67
|
+
value = value.strip()
|
|
68
|
+
|
|
69
|
+
is_string_type = df[column].dtype == 'object'
|
|
70
|
+
|
|
71
|
+
if operator in ['like', 'contains'] or (operator == '=' and is_string_type):
|
|
72
|
+
query = f"`{column}`.str.contains('{value}', case=False, na=False)"
|
|
73
|
+
display_operator = 'like'
|
|
74
|
+
else:
|
|
75
|
+
parsed_value = parse_value(value)
|
|
76
|
+
query = f"`{column}` {operator} {parsed_value}"
|
|
77
|
+
display_operator = operator
|
|
78
|
+
|
|
79
|
+
display_condition = f"{column} ({original_column}) {display_operator} {value}" if column != original_column else condition
|
|
80
|
+
return query, display_condition
|
|
81
|
+
|
|
82
|
+
def is_simple_condition(condition: str) -> bool:
|
|
83
|
+
simple_pattern = r'\w+\s*([<>=]+|like|contains)\s*.+'
|
|
84
|
+
return bool(re.match(simple_pattern, condition, re.IGNORECASE))
|
|
85
|
+
|
|
86
|
+
def split_conditions(condition: str) -> List[str]:
|
|
87
|
+
pattern = r'\s+(?:and|or)\s+(?![^()]*\))'
|
|
88
|
+
conditions = re.split(pattern, condition, flags=re.IGNORECASE)
|
|
89
|
+
return [cond.strip() for cond in conditions if cond.strip()]
|
|
90
|
+
|
|
91
|
+
class Ask:
|
|
92
|
+
def __init__(self, *, verbose=None, mutable=None):
|
|
93
|
+
self.verbose = verbose if verbose is not None else globals().get('verbose', False)
|
|
94
|
+
self.mutable = mutable if mutable is not None else globals().get('mutable', False)
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def _fill_template(template, **kw):
|
|
98
|
+
result = dedent(template.lstrip('\n').rstrip())
|
|
99
|
+
for k, v in kw.items():
|
|
100
|
+
result = result.replace(f'{{{k}}}', str(v))
|
|
101
|
+
m = re.match(r'\{[a-zA-Z0-9_]*\}', result)
|
|
102
|
+
if m:
|
|
103
|
+
raise Exception(f'Expected variable: {m.group(0)}')
|
|
104
|
+
return result
|
|
105
|
+
|
|
106
|
+
def _get_prompt(self, goal, arg):
|
|
107
|
+
if isinstance(arg, pd.DataFrame):
|
|
108
|
+
buf = io.StringIO()
|
|
109
|
+
arg.info(buf=buf)
|
|
110
|
+
df_info = buf.getvalue()
|
|
111
|
+
columns = ", ".join(arg.columns)
|
|
112
|
+
sample_data = sample_unique_values(arg)
|
|
113
|
+
formatted_sample_data = format_sample_data(sample_data)
|
|
114
|
+
else:
|
|
115
|
+
raise ValueError("Input must be a pandas DataFrame for filtering operations.")
|
|
116
|
+
|
|
117
|
+
return self._fill_template(TEMPLATE, df_info=df_info.strip(), columns=columns, sample_data=formatted_sample_data, goal=goal.strip())
|
|
118
|
+
|
|
119
|
+
def _run_prompt(self, prompt):
|
|
120
|
+
global _ask_cache
|
|
121
|
+
if prompt in _ask_cache:
|
|
122
|
+
return _ask_cache[prompt]
|
|
123
|
+
|
|
124
|
+
openai.api_key = get_openai_key()
|
|
125
|
+
|
|
126
|
+
for model in models:
|
|
127
|
+
try:
|
|
128
|
+
completion = openai.chat.completions.create(
|
|
129
|
+
model=model,
|
|
130
|
+
messages=[
|
|
131
|
+
{"role": "system", "content": "You are an AI assistant that writes Python code for DataFrame filtering operations."},
|
|
132
|
+
{"role": "user", "content": prompt},
|
|
133
|
+
],
|
|
134
|
+
**completion_config,
|
|
135
|
+
)
|
|
136
|
+
result = completion.choices[0].message.content
|
|
137
|
+
_ask_cache[prompt] = result
|
|
138
|
+
return result
|
|
139
|
+
except Exception as e:
|
|
140
|
+
print(f"Error with model {model}: {str(e)}. Trying next model if available.")
|
|
141
|
+
if model == models[-1]:
|
|
142
|
+
raise Exception("All models failed to generate a response.")
|
|
143
|
+
|
|
144
|
+
raise Exception("Unexpected error: No models were tried.")
|
|
145
|
+
|
|
146
|
+
def _extract_code_block(self, text):
|
|
147
|
+
pattern = r'```(\s*(py|python)\s*\n)?([\s\S]*?)```'
|
|
148
|
+
m = re.search(pattern, text)
|
|
149
|
+
if not m:
|
|
150
|
+
return text
|
|
151
|
+
return m.group(3)
|
|
152
|
+
|
|
153
|
+
## if you want verbose, print Source here.
|
|
154
|
+
def _eval(self, source, *args):
|
|
155
|
+
_args_ = args
|
|
156
|
+
scope = dict(_args_=args)
|
|
157
|
+
try:
|
|
158
|
+
exec(self._fill_template('''
|
|
159
|
+
import pandas as pd
|
|
160
|
+
{source}
|
|
161
|
+
_result_ = process(*_args_)
|
|
162
|
+
''', source=source), scope)
|
|
163
|
+
result = scope['_result_']
|
|
164
|
+
if not isinstance(result, pd.DataFrame):
|
|
165
|
+
raise ValueError("The function did not return a DataFrame.")
|
|
166
|
+
if len(result) == 0:
|
|
167
|
+
print("Warning: Filtering resulted in an empty DataFrame. Returning original DataFrame.")
|
|
168
|
+
return args[0]
|
|
169
|
+
if len(result) == len(args[0]):
|
|
170
|
+
print("Warning: Filtering did not remove any rows. Check if the condition was applied correctly.")
|
|
171
|
+
return result
|
|
172
|
+
except Exception as e:
|
|
173
|
+
print(f"Error in filtering: {str(e)}. Returning original DataFrame.")
|
|
174
|
+
return args[0]
|
|
175
|
+
|
|
176
|
+
def _code(self, goal, arg):
|
|
177
|
+
prompt = self._get_prompt(goal, arg)
|
|
178
|
+
result = self._run_prompt(prompt)
|
|
179
|
+
if self.verbose:
|
|
180
|
+
print()
|
|
181
|
+
print(result)
|
|
182
|
+
return self._extract_code_block(result)
|
|
183
|
+
|
|
184
|
+
def code(self, *args):
|
|
185
|
+
print(self._code(*args))
|
|
186
|
+
|
|
187
|
+
def prompt(self, *args):
|
|
188
|
+
print(self._get_prompt(*args))
|
|
189
|
+
|
|
190
|
+
def __call__(self, goal, *args):
|
|
191
|
+
source = self._code(goal, *args)
|
|
192
|
+
return self._eval(source, *args)
|
|
193
|
+
|
|
194
|
+
def apply_condition_with_retry(self, df: pd.DataFrame, condition: str, max_retries: int = 3) -> pd.DataFrame:
|
|
195
|
+
original_len = len(df)
|
|
196
|
+
|
|
197
|
+
sub_conditions = split_conditions(condition)
|
|
198
|
+
if len(sub_conditions) > 1:
|
|
199
|
+
if self.verbose:
|
|
200
|
+
print(f"Detected multiple conditions: {sub_conditions}")
|
|
201
|
+
for sub_condition in sub_conditions:
|
|
202
|
+
df = self.apply_condition_with_retry(df, sub_condition, max_retries)
|
|
203
|
+
return df
|
|
204
|
+
|
|
205
|
+
if is_simple_condition(condition):
|
|
206
|
+
try:
|
|
207
|
+
query, display_condition = create_standard_query(condition, df)
|
|
208
|
+
filtered_df = df.query(query)
|
|
209
|
+
if len(filtered_df) < original_len:
|
|
210
|
+
if self.verbose:
|
|
211
|
+
print(f"Applied standard filter: {display_condition}")
|
|
212
|
+
return filtered_df
|
|
213
|
+
except Exception as e:
|
|
214
|
+
if self.verbose:
|
|
215
|
+
print(f"Standard filtering failed: {str(e)}. Falling back to GPT method.")
|
|
216
|
+
|
|
217
|
+
for attempt in range(max_retries):
|
|
218
|
+
try:
|
|
219
|
+
filtered_df = self(condition, df)
|
|
220
|
+
if isinstance(filtered_df, pd.DataFrame) and len(filtered_df) < original_len:
|
|
221
|
+
return filtered_df
|
|
222
|
+
elif self.verbose:
|
|
223
|
+
print(f"Attempt {attempt + 1}: GPT filtering didn't remove any rows. Retrying...")
|
|
224
|
+
|
|
225
|
+
alternative_conditions = [f"filter {condition}", f"select {condition}", f"find {condition}"]
|
|
226
|
+
for alt_condition in alternative_conditions:
|
|
227
|
+
try:
|
|
228
|
+
filtered_df = self(alt_condition, df)
|
|
229
|
+
if isinstance(filtered_df, pd.DataFrame) and len(filtered_df) < original_len:
|
|
230
|
+
return filtered_df
|
|
231
|
+
except Exception:
|
|
232
|
+
pass
|
|
233
|
+
|
|
234
|
+
columns_str = ", ".join(df.columns)
|
|
235
|
+
general_condition = f"Based on the columns {columns_str}, {condition}"
|
|
236
|
+
filtered_df = self(general_condition, df)
|
|
237
|
+
if isinstance(filtered_df, pd.DataFrame) and len(filtered_df) < original_len:
|
|
238
|
+
return filtered_df
|
|
239
|
+
except Exception as e:
|
|
240
|
+
if self.verbose:
|
|
241
|
+
print(f"Error on GPT attempt {attempt + 1}: {str(e)}")
|
|
242
|
+
time.sleep(1)
|
|
243
|
+
|
|
244
|
+
if self.verbose:
|
|
245
|
+
print("All filtering attempts failed. Returning original DataFrame.")
|
|
246
|
+
return df
|
|
247
|
+
|
|
248
|
+
if not hasattr(pd.DataFrame, 'ask'):
|
|
249
|
+
@pd.api.extensions.register_dataframe_accessor('ask')
|
|
250
|
+
class AskAccessor:
|
|
251
|
+
def __init__(self, pandas_obj):
|
|
252
|
+
self._validate(pandas_obj)
|
|
253
|
+
self._obj = pandas_obj
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
def _validate(obj):
|
|
257
|
+
if not isinstance(obj, pd.DataFrame):
|
|
258
|
+
raise AttributeError("'ask' accessor is only available for DataFrames.")
|
|
259
|
+
|
|
260
|
+
def _ask(self, **kw):
|
|
261
|
+
return Ask(**kw)
|
|
262
|
+
|
|
263
|
+
def _data(self, **kw):
|
|
264
|
+
if not globals().get('mutable', False) and not kw.get('mutable'):
|
|
265
|
+
return self._obj.copy()
|
|
266
|
+
return self._obj
|
|
267
|
+
|
|
268
|
+
def __call__(self, goal, *args, **kw):
|
|
269
|
+
ask = self._ask(**kw)
|
|
270
|
+
data = self._data(**kw)
|
|
271
|
+
return ask(goal, data, *args)
|
|
272
|
+
|
|
273
|
+
def code(self, goal, *args, **kw):
|
|
274
|
+
ask = self._ask(**kw)
|
|
275
|
+
data = self._data(**kw)
|
|
276
|
+
return ask.code(goal, data, *args)
|
|
277
|
+
|
|
278
|
+
def prompt(self, goal, *args, **kw):
|
|
279
|
+
ask = self._ask(**kw)
|
|
280
|
+
data = self._data(**kw)
|
|
281
|
+
return ask.prompt(goal, data, *args)
|
|
282
|
+
|
|
283
|
+
def apply_condition_with_retry(self, condition, max_retries=3, **kw):
|
|
284
|
+
ask = self._ask(**kw)
|
|
285
|
+
data = self._data(**kw)
|
|
286
|
+
return ask.apply_condition_with_retry(data, condition, max_retries)
|
|
287
|
+
else:
|
|
288
|
+
pass
|
|
289
|
+
|
|
290
|
+
TEMPLATE = """
|
|
291
|
+
Write a Python function `process(df)` which takes a pandas DataFrame as input.
|
|
292
|
+
DataFrame information:
|
|
293
|
+
{df_info}
|
|
294
|
+
|
|
295
|
+
Available columns: {columns}
|
|
296
|
+
|
|
297
|
+
Sample data:
|
|
298
|
+
{sample_data}
|
|
299
|
+
|
|
300
|
+
This function should filter the DataFrame based on the following goal: {goal}
|
|
301
|
+
|
|
302
|
+
Important instructions:
|
|
303
|
+
1. Only use columns that are listed in the 'Available columns'.
|
|
304
|
+
2. The function must return a filtered DataFrame.
|
|
305
|
+
3. If no filtering is possible or if the filtering results in an empty DataFrame, return the original DataFrame with a comment explaining why.
|
|
306
|
+
4. Do not use any external libraries other than pandas.
|
|
307
|
+
5. Ensure that your filtering logic is correct and doesn't inadvertently return an empty DataFrame.
|
|
308
|
+
6. Only define the function without trying to call it!
|
|
309
|
+
|
|
310
|
+
Write the function in a Python code block with all necessary imports and no example usage.
|
|
311
|
+
"""
|