upgini 1.2.156.dev5__tar.gz → 1.2.157.dev1__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.
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/PKG-INFO +1 -1
- upgini-1.2.157.dev1/src/upgini/__about__.py +1 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/binary.py +48 -124
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/date.py +20 -8
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/feature.py +38 -19
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/groupby.py +13 -4
- upgini-1.2.157.dev1/src/upgini/autofe/operand.py +73 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/operator.py +20 -7
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/timeseries/base.py +3 -1
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/timeseries/cross.py +5 -3
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/unary.py +21 -10
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/vector.py +10 -6
- upgini-1.2.156.dev5/src/upgini/__about__.py +0 -1
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/.gitignore +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/LICENSE +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/README.md +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/pyproject.toml +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/__init__.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/ads.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/ads_management/__init__.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/ads_management/ads_manager.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/__init__.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/all_operators.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/timeseries/__init__.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/timeseries/delta.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/timeseries/lag.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/timeseries/roll.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/timeseries/trend.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/timeseries/volatility.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/autofe/utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/data_source/__init__.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/data_source/data_source_publisher.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/dataset.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/errors.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/features_enricher.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/http.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/mdc/__init__.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/mdc/context.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/metadata.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/metrics.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/normalizer/__init__.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/normalizer/normalize_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/resource_bundle/__init__.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/resource_bundle/exceptions.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/resource_bundle/strings.properties +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/resource_bundle/strings_widget.properties +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/sampler/__init__.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/sampler/base.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/sampler/random_under_sampler.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/sampler/utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/search_task.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/spinner.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/Roboto-Regular.ttf +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/__init__.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/base_search_key_detector.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/blocked_time_series.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/config.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/country_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/custom_loss_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/cv_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/datetime_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/deduplicate_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/display_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/email_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/fallback_progress_bar.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/feature_info.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/features_validator.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/format.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/hash_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/ip_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/mstats.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/one_hot_encoder.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/phone_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/postal_code_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/progress_bar.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/psi.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/sample_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/sklearn_ext.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/sort.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/target_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/track_info.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/ts_utils.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/utils/warning_counter.py +0 -0
- {upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/version_validator.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.2.157.dev1"
|
|
@@ -6,6 +6,7 @@ import numpy as np
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
from jarowinkler import jarowinkler_similarity
|
|
8
8
|
|
|
9
|
+
from upgini.autofe.operand import OperandValue
|
|
9
10
|
from upgini.autofe.operator import PandasOperator, VectorizableMixin
|
|
10
11
|
|
|
11
12
|
|
|
@@ -15,7 +16,9 @@ class Min(PandasOperator):
|
|
|
15
16
|
is_symmetrical: bool = True
|
|
16
17
|
has_symmetry_importance: bool = True
|
|
17
18
|
|
|
18
|
-
def calculate_binary(self, left:
|
|
19
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
20
|
+
left = left.as_series()
|
|
21
|
+
right = right.as_series()
|
|
19
22
|
return np.minimum(left, right)
|
|
20
23
|
|
|
21
24
|
|
|
@@ -25,7 +28,9 @@ class Max(PandasOperator):
|
|
|
25
28
|
is_symmetrical: bool = True
|
|
26
29
|
has_symmetry_importance: bool = True
|
|
27
30
|
|
|
28
|
-
def calculate_binary(self, left:
|
|
31
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
32
|
+
left = left.as_series()
|
|
33
|
+
right = right.as_series()
|
|
29
34
|
return np.maximum(left, right)
|
|
30
35
|
|
|
31
36
|
|
|
@@ -37,7 +42,9 @@ class Add(PandasOperator, VectorizableMixin):
|
|
|
37
42
|
has_symmetry_importance: bool = True
|
|
38
43
|
is_vectorizable: bool = True
|
|
39
44
|
|
|
40
|
-
def calculate_binary(self, left:
|
|
45
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
46
|
+
left = left.as_series()
|
|
47
|
+
right = right.as_series()
|
|
41
48
|
return left + right
|
|
42
49
|
|
|
43
50
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -56,7 +63,9 @@ class Subtract(PandasOperator, VectorizableMixin):
|
|
|
56
63
|
has_symmetry_importance: bool = True
|
|
57
64
|
is_vectorizable: bool = True
|
|
58
65
|
|
|
59
|
-
def calculate_binary(self, left:
|
|
66
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
67
|
+
left = left.as_series()
|
|
68
|
+
right = right.as_series()
|
|
60
69
|
return left - right
|
|
61
70
|
|
|
62
71
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -75,7 +84,9 @@ class Multiply(PandasOperator, VectorizableMixin):
|
|
|
75
84
|
has_symmetry_importance: bool = True
|
|
76
85
|
is_vectorizable: bool = True
|
|
77
86
|
|
|
78
|
-
def calculate_binary(self, left:
|
|
87
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
88
|
+
left = left.as_series()
|
|
89
|
+
right = right.as_series()
|
|
79
90
|
return left * right
|
|
80
91
|
|
|
81
92
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -94,7 +105,9 @@ class Divide(PandasOperator, VectorizableMixin):
|
|
|
94
105
|
is_vectorizable: bool = True
|
|
95
106
|
output_type: Optional[str] = "float"
|
|
96
107
|
|
|
97
|
-
def calculate_binary(self, left:
|
|
108
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
109
|
+
left = left.as_series()
|
|
110
|
+
right = right.as_series()
|
|
98
111
|
return left / right.replace(0, np.nan)
|
|
99
112
|
|
|
100
113
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -111,7 +124,9 @@ class Combine(PandasOperator):
|
|
|
111
124
|
has_symmetry_importance: bool = True
|
|
112
125
|
output_type: Optional[str] = "object"
|
|
113
126
|
|
|
114
|
-
def calculate_binary(self, left:
|
|
127
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
128
|
+
left = left.as_series()
|
|
129
|
+
right = right.as_series()
|
|
115
130
|
temp = left.astype(str) + "_" + right.astype(str)
|
|
116
131
|
temp[left.isna() | right.isna()] = np.nan
|
|
117
132
|
return pd.Series(temp, index=left.index)
|
|
@@ -126,7 +141,9 @@ class CombineThenFreq(PandasOperator):
|
|
|
126
141
|
is_distribution_dependent: bool = True
|
|
127
142
|
input_type: Optional[str] = "discrete"
|
|
128
143
|
|
|
129
|
-
def calculate_binary(self, left:
|
|
144
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
145
|
+
left = left.as_series()
|
|
146
|
+
right = right.as_series()
|
|
130
147
|
temp = left.astype(str) + "_" + right.astype(str)
|
|
131
148
|
temp[left.isna() | right.isna()] = np.nan
|
|
132
149
|
value_counts = temp.value_counts(normalize=True)
|
|
@@ -140,120 +157,25 @@ class Distance(PandasOperator):
|
|
|
140
157
|
is_symmetrical: bool = True
|
|
141
158
|
has_symmetry_importance: bool = True
|
|
142
159
|
|
|
143
|
-
def
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
dots = np.sum(left_mat * right_mat, axis=1)
|
|
163
|
-
denom = np.sqrt(np.sum(left_mat * left_mat, axis=1)) * np.sqrt(np.sum(right_mat * right_mat, axis=1))
|
|
164
|
-
results = np.full(len(left_mat), np.nan, dtype=np.float64)
|
|
165
|
-
valid = denom > 0
|
|
166
|
-
results[valid] = 1 - dots[valid] / denom[valid]
|
|
167
|
-
return results
|
|
168
|
-
|
|
169
|
-
def _try_fast_distance(
|
|
170
|
-
self, left_arr: np.ndarray, right_arr: np.ndarray, row_present: np.ndarray, n: int
|
|
171
|
-
) -> Optional[np.ndarray]:
|
|
172
|
-
valid_idx = np.flatnonzero(row_present)
|
|
173
|
-
if len(valid_idx) == 0:
|
|
174
|
-
return None
|
|
175
|
-
|
|
176
|
-
left_mat = self._stack_vectors(left_arr, valid_idx)
|
|
177
|
-
right_mat = self._stack_vectors(right_arr, valid_idx)
|
|
178
|
-
if left_mat is None or right_mat is None:
|
|
179
|
-
return None
|
|
180
|
-
|
|
181
|
-
stacked_results = self._cosine_distance_stacked(left_mat, right_mat)
|
|
182
|
-
if row_present.all():
|
|
183
|
-
return stacked_results
|
|
184
|
-
|
|
185
|
-
results = np.full(n, np.nan, dtype=np.float64)
|
|
186
|
-
results[valid_idx] = stacked_results
|
|
187
|
-
return results
|
|
188
|
-
|
|
189
|
-
def _parse_vectors(self, left_arr: np.ndarray, right_arr: np.ndarray) -> tuple[list, list, np.ndarray]:
|
|
190
|
-
n = len(left_arr)
|
|
191
|
-
left_vecs: list[Optional[np.ndarray]] = [None] * n
|
|
192
|
-
right_vecs: list[Optional[np.ndarray]] = [None] * n
|
|
193
|
-
row_valid = np.zeros(n, dtype=bool)
|
|
194
|
-
for i, (left_value, right_value) in enumerate(zip(left_arr, right_arr)):
|
|
195
|
-
left_vec = self._to_float_vector(left_value)
|
|
196
|
-
right_vec = self._to_float_vector(right_value)
|
|
197
|
-
left_vecs[i] = left_vec
|
|
198
|
-
right_vecs[i] = right_vec
|
|
199
|
-
if left_vec is not None and right_vec is not None and left_vec.size > 0 and right_vec.size > 0:
|
|
200
|
-
row_valid[i] = True
|
|
201
|
-
return left_vecs, right_vecs, row_valid
|
|
202
|
-
|
|
203
|
-
def _masked_cosine_distance(
|
|
204
|
-
self, left_mat: np.ndarray, right_mat: np.ndarray, row_valid: np.ndarray
|
|
205
|
-
) -> np.ndarray:
|
|
206
|
-
results = self._cosine_distance_stacked(left_mat, right_mat)
|
|
207
|
-
if len(results) == len(row_valid):
|
|
208
|
-
results[~row_valid] = np.nan
|
|
209
|
-
return results
|
|
210
|
-
|
|
211
|
-
def _calculate_rowwise(
|
|
212
|
-
self, results: np.ndarray, indices: np.ndarray, left_vecs: list, right_vecs: list
|
|
213
|
-
) -> None:
|
|
214
|
-
for index in indices:
|
|
215
|
-
left_vec = left_vecs[index]
|
|
216
|
-
right_vec = right_vecs[index]
|
|
217
|
-
dot = np.sum(left_vec * right_vec)
|
|
218
|
-
denom = np.sqrt(np.sum(left_vec * left_vec)) * np.sqrt(np.sum(right_vec * right_vec))
|
|
219
|
-
if denom != 0:
|
|
220
|
-
results[index] = 1 - dot / denom
|
|
221
|
-
|
|
222
|
-
def _calculate_fallback_distance(
|
|
223
|
-
self, left_arr: np.ndarray, right_arr: np.ndarray, n: int, index: pd.Index
|
|
224
|
-
) -> pd.Series:
|
|
225
|
-
results = np.full(n, np.nan, dtype=np.float64)
|
|
226
|
-
left_vecs, right_vecs, row_valid = self._parse_vectors(left_arr, right_arr)
|
|
227
|
-
if not row_valid.any():
|
|
228
|
-
return pd.Series(results, index=index, dtype=np.float64)
|
|
229
|
-
|
|
230
|
-
valid_idx = np.flatnonzero(row_valid)
|
|
231
|
-
dim = left_vecs[valid_idx[0]].size
|
|
232
|
-
if not all(left_vecs[i].size == dim and right_vecs[i].size == dim for i in valid_idx):
|
|
233
|
-
self._calculate_rowwise(results, valid_idx, left_vecs, right_vecs)
|
|
234
|
-
return pd.Series(results, index=index, dtype=np.float64)
|
|
235
|
-
|
|
236
|
-
left_mat = np.zeros((n, dim), dtype=np.float64)
|
|
237
|
-
right_mat = np.zeros((n, dim), dtype=np.float64)
|
|
238
|
-
left_mat[valid_idx] = np.stack([left_vecs[i] for i in valid_idx])
|
|
239
|
-
right_mat[valid_idx] = np.stack([right_vecs[i] for i in valid_idx])
|
|
240
|
-
|
|
241
|
-
return pd.Series(self._masked_cosine_distance(left_mat, right_mat, row_valid), index=index, dtype=np.float64)
|
|
242
|
-
|
|
243
|
-
def calculate_binary(self, left: pd.Series, right: pd.Series) -> pd.Series:
|
|
244
|
-
n = len(left)
|
|
245
|
-
if n == 0:
|
|
246
|
-
return pd.Series(np.full(n, np.nan), index=left.index, dtype=np.float64)
|
|
247
|
-
|
|
248
|
-
row_present = left.notna().to_numpy() & right.notna().to_numpy()
|
|
249
|
-
left_arr = left.to_numpy()
|
|
250
|
-
right_arr = right.to_numpy()
|
|
251
|
-
|
|
252
|
-
fast_results = self._try_fast_distance(left_arr, right_arr, row_present, n)
|
|
253
|
-
if fast_results is not None:
|
|
254
|
-
return pd.Series(fast_results, index=left.index, dtype=np.float64)
|
|
255
|
-
|
|
256
|
-
return self._calculate_fallback_distance(left_arr, right_arr, n, left.index)
|
|
160
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
161
|
+
left = left.as_series()
|
|
162
|
+
right = right.as_series()
|
|
163
|
+
return pd.Series(
|
|
164
|
+
1 - self.__dot(left, right) / (self.__norm(left) * self.__norm(right)), index=left.index
|
|
165
|
+
).astype(np.float64)
|
|
166
|
+
|
|
167
|
+
# row-wise dot product, handling None values
|
|
168
|
+
def __dot(self, left: pd.Series, right: pd.Series) -> pd.Series:
|
|
169
|
+
left = left.apply(lambda x: np.array(x).astype(np.float64))
|
|
170
|
+
right = right.apply(lambda x: np.array(x).astype(np.float64))
|
|
171
|
+
res = (left.dropna() * right.dropna()).apply(np.sum)
|
|
172
|
+
res = res.reindex(left.index.union(right.index))
|
|
173
|
+
return res
|
|
174
|
+
|
|
175
|
+
# Calculate the norm of a vector, handling None values
|
|
176
|
+
def __norm(self, vector: pd.Series) -> pd.Series:
|
|
177
|
+
vector = vector.fillna(np.nan)
|
|
178
|
+
return np.sqrt(self.__dot(vector, vector))
|
|
257
179
|
|
|
258
180
|
|
|
259
181
|
# Left for backward compatibility
|
|
@@ -264,12 +186,14 @@ class Sim(Distance):
|
|
|
264
186
|
is_symmetrical: bool = True
|
|
265
187
|
has_symmetry_importance: bool = True
|
|
266
188
|
|
|
267
|
-
def calculate_binary(self, left:
|
|
189
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
268
190
|
return 1 - super().calculate_binary(left, right)
|
|
269
191
|
|
|
270
192
|
|
|
271
193
|
class StringSim(PandasOperator, abc.ABC):
|
|
272
|
-
def calculate_binary(self, left:
|
|
194
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
195
|
+
left = left.as_series()
|
|
196
|
+
right = right.as_series()
|
|
273
197
|
sims = []
|
|
274
198
|
for i in left.index:
|
|
275
199
|
left_i = self._prepare_value(left.get(i))
|
|
@@ -7,6 +7,7 @@ import pandas as pd
|
|
|
7
7
|
from pandas.core.arrays.timedeltas import TimedeltaArray
|
|
8
8
|
from pydantic import BaseModel, __version__ as pydantic_version
|
|
9
9
|
|
|
10
|
+
from upgini.autofe.operand import OperandValue
|
|
10
11
|
from upgini.autofe.operator import PandasOperator, ParametrizedOperator
|
|
11
12
|
from upgini.autofe.utils import bin_index, bin_index_many, bin_index_vectorized, pydantic_validator
|
|
12
13
|
|
|
@@ -64,7 +65,9 @@ class DateDiff(PandasOperator, DateDiffMixin):
|
|
|
64
65
|
)
|
|
65
66
|
return res
|
|
66
67
|
|
|
67
|
-
def calculate_binary(self, left:
|
|
68
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
69
|
+
left = left.as_series()
|
|
70
|
+
right = right.as_series()
|
|
68
71
|
if left.isna().all() or right.isna().all():
|
|
69
72
|
return pd.Series([None] * len(left), index=left.index, dtype=np.float64)
|
|
70
73
|
|
|
@@ -95,7 +98,9 @@ class DateDiffType2(PandasOperator, DateDiffMixin):
|
|
|
95
98
|
)
|
|
96
99
|
return res
|
|
97
100
|
|
|
98
|
-
def calculate_binary(self, left:
|
|
101
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
102
|
+
left = left.as_series()
|
|
103
|
+
right = right.as_series()
|
|
99
104
|
left = self._convert_to_date(left, self.left_unit)
|
|
100
105
|
right = self._convert_to_date(right, self.right_unit)
|
|
101
106
|
future = right + (left.dt.year - right.dt.year).apply(
|
|
@@ -187,7 +192,9 @@ class DateListDiffLists(PandasOperator, DateDiffMixin, ParametrizedOperator):
|
|
|
187
192
|
diffs = diffs[diffs > 0]
|
|
188
193
|
return np.atleast_1d(np.asarray(diffs, dtype=np.float64)).tolist()
|
|
189
194
|
|
|
190
|
-
def calculate_binary(self, left:
|
|
195
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
196
|
+
left = left.as_series()
|
|
197
|
+
right = right.as_series()
|
|
191
198
|
if left.isna().all() or right.isna().all():
|
|
192
199
|
return pd.Series([None] * len(left), index=left.index, dtype=object)
|
|
193
200
|
|
|
@@ -271,7 +278,8 @@ class DateListDiffAggWithinBounds(PandasOperator, ParametrizedOperator):
|
|
|
271
278
|
return agg_res / orig_len
|
|
272
279
|
return agg_res
|
|
273
280
|
|
|
274
|
-
def calculate_unary(self, data:
|
|
281
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
282
|
+
data = data.as_series()
|
|
275
283
|
results = np.empty(len(data), dtype=np.float64)
|
|
276
284
|
results[:] = np.nan
|
|
277
285
|
for i, diffs in enumerate(data.to_numpy()):
|
|
@@ -332,13 +340,15 @@ class DateListDiff(PandasOperator, DateDiffMixin, ParametrizedOperator):
|
|
|
332
340
|
return pd.Series([None] * len(left), index=left.index, dtype=np.float64)
|
|
333
341
|
|
|
334
342
|
right_mask = DateListDiffLists._non_empty_list_mask(right)
|
|
335
|
-
diff_lists = self._lists_op().
|
|
336
|
-
result = self._agg_op().
|
|
343
|
+
diff_lists = self._lists_op().calculate(left=left, right=right)
|
|
344
|
+
result = self._agg_op().calculate(data=diff_lists)
|
|
337
345
|
if self.aggregation in _count_aggregations:
|
|
338
346
|
result[~right_mask] = 0.0
|
|
339
347
|
return result.astype(np.float64)
|
|
340
348
|
|
|
341
|
-
def calculate_binary(self, left:
|
|
349
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
350
|
+
left = left.as_series()
|
|
351
|
+
right = right.as_series()
|
|
342
352
|
return self._compose_list_diff(left, right)
|
|
343
353
|
|
|
344
354
|
|
|
@@ -404,7 +414,9 @@ class DatePercentileBase(PandasOperator, abc.ABC):
|
|
|
404
414
|
|
|
405
415
|
date_unit: Optional[str] = None
|
|
406
416
|
|
|
407
|
-
def calculate_binary(self, left:
|
|
417
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
418
|
+
left = left.as_series()
|
|
419
|
+
right = right.as_series()
|
|
408
420
|
# Assuming that left is a date column, right is a feature column
|
|
409
421
|
left = pd.to_datetime(left, unit=self.date_unit)
|
|
410
422
|
|
|
@@ -8,13 +8,14 @@ import pandas as pd
|
|
|
8
8
|
from pandas._typing import DtypeObj
|
|
9
9
|
|
|
10
10
|
from upgini.autofe.all_operators import find_op
|
|
11
|
+
from upgini.autofe.operand import CalculationContext, OperandValue, wrap_operand
|
|
11
12
|
from upgini.autofe.operator import Operator, PandasOperator
|
|
12
13
|
from upgini.autofe.timeseries.base import TimeSeriesBase
|
|
13
14
|
from upgini.autofe.utils import pydantic_dump_method, pydantic_parse_method
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class Column:
|
|
17
|
-
def __init__(self, name: str, data: Optional[
|
|
18
|
+
def __init__(self, name: str, data: Optional[OperandValue] = None, calculate_all=False):
|
|
18
19
|
self.name = name
|
|
19
20
|
self.data = data
|
|
20
21
|
self.calculate_all = calculate_all
|
|
@@ -61,8 +62,12 @@ class Column:
|
|
|
61
62
|
def infer_type(self, data: pd.DataFrame) -> DtypeObj:
|
|
62
63
|
return data[self.name].dtype
|
|
63
64
|
|
|
64
|
-
def calculate(self, data: pd.DataFrame) -> pd.Series:
|
|
65
|
-
|
|
65
|
+
def calculate(self, data: Union[pd.DataFrame, CalculationContext]) -> pd.Series:
|
|
66
|
+
ctx = data if isinstance(data, CalculationContext) else CalculationContext.from_dataframe(data)
|
|
67
|
+
return self._eval(ctx).as_series()
|
|
68
|
+
|
|
69
|
+
def _eval(self, ctx: CalculationContext) -> OperandValue:
|
|
70
|
+
self.data = ctx.resolve_operand(self.name)
|
|
66
71
|
return self.data
|
|
67
72
|
|
|
68
73
|
def to_formula(self, **kwargs) -> str:
|
|
@@ -236,27 +241,41 @@ class Feature:
|
|
|
236
241
|
# either a symmetrical operator or group by
|
|
237
242
|
return self.children[0].infer_type(data)
|
|
238
243
|
|
|
239
|
-
def calculate(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
new_data = self.op.calculate(data=d1, left=d1, right=d2)
|
|
248
|
-
else:
|
|
249
|
-
raise NotImplementedError(f"Unrecognized operator {self.op.name}.")
|
|
250
|
-
|
|
251
|
-
if (str(new_data.dtype) == "category") | (str(new_data.dtype) == "object"):
|
|
252
|
-
pass
|
|
253
|
-
else:
|
|
254
|
-
new_data = new_data.replace([-np.inf, np.inf], np.nan)
|
|
244
|
+
def calculate(
|
|
245
|
+
self,
|
|
246
|
+
data: Union[pd.DataFrame, CalculationContext],
|
|
247
|
+
is_root: bool = False,
|
|
248
|
+
) -> Union[pd.Series, pd.DataFrame]:
|
|
249
|
+
ctx = data if isinstance(data, CalculationContext) else CalculationContext.from_dataframe(data)
|
|
250
|
+
result = self._eval(ctx)
|
|
251
|
+
new_data = self._operand_to_output(result)
|
|
255
252
|
|
|
256
253
|
if is_root:
|
|
257
254
|
self.data = new_data
|
|
258
255
|
return new_data
|
|
259
256
|
|
|
257
|
+
def _eval(self, ctx: CalculationContext) -> OperandValue:
|
|
258
|
+
if isinstance(self.op, PandasOperator):
|
|
259
|
+
if self.op.is_vector:
|
|
260
|
+
operands = [child._eval(ctx) for child in self.children]
|
|
261
|
+
result = self.op.calculate(data=operands)
|
|
262
|
+
return wrap_operand(result)
|
|
263
|
+
left = self.children[0]._eval(ctx)
|
|
264
|
+
if len(self.children) < 2:
|
|
265
|
+
result = self.op.calculate(data=left)
|
|
266
|
+
return wrap_operand(result, source=left.source)
|
|
267
|
+
right = self.children[1]._eval(ctx)
|
|
268
|
+
result = self.op.calculate(left=left, right=right)
|
|
269
|
+
return wrap_operand(result, source=left.source)
|
|
270
|
+
raise NotImplementedError(f"Unrecognized operator {self.op.name}.")
|
|
271
|
+
|
|
272
|
+
@staticmethod
|
|
273
|
+
def _operand_to_output(operand: OperandValue) -> Union[pd.Series, pd.DataFrame]:
|
|
274
|
+
new_data = operand.as_series()
|
|
275
|
+
if (str(new_data.dtype) == "category") | (str(new_data.dtype) == "object"):
|
|
276
|
+
return new_data
|
|
277
|
+
return new_data.replace([-np.inf, np.inf], np.nan)
|
|
278
|
+
|
|
260
279
|
@staticmethod
|
|
261
280
|
def check_xor(left: Union[Column, "Feature"], right: Union[Column, "Feature"]) -> bool:
|
|
262
281
|
def _get_all_columns(feature: Union[Column, "Feature"]) -> List[str]:
|
|
@@ -2,6 +2,7 @@ from typing import Optional
|
|
|
2
2
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
|
|
5
|
+
from upgini.autofe.operand import OperandValue
|
|
5
6
|
from upgini.autofe.operator import PandasOperator, ParametrizedOperator, VectorizableMixin
|
|
6
7
|
|
|
7
8
|
|
|
@@ -27,7 +28,9 @@ class GroupByThenAgg(
|
|
|
27
28
|
return None
|
|
28
29
|
return cls(agg=agg)
|
|
29
30
|
|
|
30
|
-
def calculate_binary(self, left:
|
|
31
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
32
|
+
left = left.as_series()
|
|
33
|
+
right = right.as_series()
|
|
31
34
|
temp = left.groupby(right).agg(self.agg.lower())
|
|
32
35
|
return self._loc(right, temp)
|
|
33
36
|
|
|
@@ -46,7 +49,9 @@ class GroupByThenRank(PandasOperator, VectorizableMixin):
|
|
|
46
49
|
output_type: Optional[str] = "float"
|
|
47
50
|
is_distribution_dependent: bool = True
|
|
48
51
|
|
|
49
|
-
def calculate_binary(self, left:
|
|
52
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
53
|
+
left = left.as_series()
|
|
54
|
+
right = right.as_series()
|
|
50
55
|
temp = pd.DataFrame(left[~right.isna()].groupby(right).rank(ascending=True, pct=True)).reset_index()
|
|
51
56
|
return temp.merge(pd.DataFrame(right).reset_index(), how="right", on=["index"])[left.name]
|
|
52
57
|
|
|
@@ -66,7 +71,9 @@ class GroupByThenNUnique(PandasOperator, VectorizableMixin):
|
|
|
66
71
|
is_distribution_dependent: bool = True
|
|
67
72
|
input_type: Optional[str] = "discrete"
|
|
68
73
|
|
|
69
|
-
def calculate_binary(self, left:
|
|
74
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
75
|
+
left = left.as_series()
|
|
76
|
+
right = right.as_series()
|
|
70
77
|
nunique = left.groupby(right).nunique()
|
|
71
78
|
return self._loc(right, nunique)
|
|
72
79
|
|
|
@@ -85,7 +92,9 @@ class GroupByThenFreq(PandasOperator):
|
|
|
85
92
|
is_distribution_dependent: bool = True
|
|
86
93
|
input_type: Optional[str] = "discrete"
|
|
87
94
|
|
|
88
|
-
def calculate_binary(self, left:
|
|
95
|
+
def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
|
|
96
|
+
left = left.as_series()
|
|
97
|
+
right = right.as_series()
|
|
89
98
|
def _f(x):
|
|
90
99
|
value_counts = x.value_counts(normalize=True)
|
|
91
100
|
return self._loc(x, value_counts)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OperandKind(str, Enum):
|
|
12
|
+
SERIES = "series"
|
|
13
|
+
MATRIX = "matrix"
|
|
14
|
+
ARRAY = "array"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class OperandValue:
|
|
19
|
+
kind: OperandKind
|
|
20
|
+
data: Union[pd.Series, np.ndarray]
|
|
21
|
+
index: pd.Index
|
|
22
|
+
source: Optional[str] = None
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_series(cls, series: pd.Series, source: Optional[str] = None) -> "OperandValue":
|
|
26
|
+
return cls(kind=OperandKind.SERIES, data=series, index=series.index, source=source)
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def from_matrix(cls, matrix: np.ndarray, index: pd.Index, source: Optional[str] = None) -> "OperandValue":
|
|
30
|
+
return cls(kind=OperandKind.MATRIX, data=matrix, index=index, source=source)
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def from_array(cls, array: np.ndarray, index: pd.Index, source: Optional[str] = None) -> "OperandValue":
|
|
34
|
+
return cls(kind=OperandKind.ARRAY, data=array, index=index, source=source)
|
|
35
|
+
|
|
36
|
+
def as_series(self) -> pd.Series:
|
|
37
|
+
if self.kind == OperandKind.SERIES:
|
|
38
|
+
return self.data # type: ignore[return-value]
|
|
39
|
+
if self.kind == OperandKind.ARRAY:
|
|
40
|
+
return pd.Series(self.data, index=self.index, dtype=np.float64)
|
|
41
|
+
if self.kind == OperandKind.MATRIX:
|
|
42
|
+
return pd.Series(list(self.as_matrix()), index=self.index)
|
|
43
|
+
raise TypeError(f"Cannot convert operand kind {self.kind.value} to series")
|
|
44
|
+
|
|
45
|
+
def as_matrix(self) -> np.ndarray:
|
|
46
|
+
if self.kind != OperandKind.MATRIX:
|
|
47
|
+
raise TypeError(f"Cannot convert operand kind {self.kind.value} to matrix")
|
|
48
|
+
return self.data # type: ignore[return-value]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class CalculationContext:
|
|
53
|
+
data: pd.DataFrame
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def index(self) -> pd.Index:
|
|
57
|
+
return self.data.index
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def from_dataframe(cls, data: pd.DataFrame) -> "CalculationContext":
|
|
61
|
+
return cls(data=data)
|
|
62
|
+
|
|
63
|
+
def resolve_operand(self, name: str) -> OperandValue:
|
|
64
|
+
return OperandValue.from_series(self.data[name], source=name)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def wrap_operand(
|
|
68
|
+
result: Union[pd.Series, "OperandValue"],
|
|
69
|
+
source: Optional[str] = None,
|
|
70
|
+
) -> "OperandValue":
|
|
71
|
+
if isinstance(result, OperandValue):
|
|
72
|
+
return result
|
|
73
|
+
return OperandValue.from_series(result, source=source)
|
|
@@ -6,6 +6,8 @@ import numpy as np
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
from pydantic import BaseModel, PrivateAttr
|
|
8
8
|
|
|
9
|
+
from upgini.autofe.operand import OperandValue
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
class OperatorRegistry(type(BaseModel)):
|
|
11
13
|
_registry = {}
|
|
@@ -132,21 +134,32 @@ MAIN_COLUMN = "main_column"
|
|
|
132
134
|
|
|
133
135
|
|
|
134
136
|
class PandasOperator(Operator, abc.ABC):
|
|
135
|
-
|
|
137
|
+
@staticmethod
|
|
138
|
+
def _operand(value: Union[pd.Series, OperandValue]) -> OperandValue:
|
|
139
|
+
if isinstance(value, OperandValue):
|
|
140
|
+
return value
|
|
141
|
+
return OperandValue.from_series(value)
|
|
142
|
+
|
|
143
|
+
def calculate(self, **kwargs) -> Union[pd.Series, OperandValue]:
|
|
136
144
|
if self.is_unary:
|
|
137
|
-
return self.calculate_unary(kwargs["data"])
|
|
145
|
+
return self.calculate_unary(self._operand(kwargs["data"]))
|
|
138
146
|
elif self.is_binary or self.is_grouping:
|
|
139
|
-
return self.calculate_binary(
|
|
147
|
+
return self.calculate_binary(
|
|
148
|
+
self._operand(kwargs["left"]),
|
|
149
|
+
self._operand(kwargs["right"]),
|
|
150
|
+
)
|
|
140
151
|
else:
|
|
141
|
-
return self.calculate_vector(kwargs["data"])
|
|
152
|
+
return self.calculate_vector([self._operand(value) for value in kwargs["data"]])
|
|
142
153
|
|
|
143
|
-
def calculate_unary(self, data:
|
|
154
|
+
def calculate_unary(self, data: OperandValue) -> Union[pd.Series, OperandValue]:
|
|
144
155
|
pass
|
|
145
156
|
|
|
146
|
-
def calculate_binary(
|
|
157
|
+
def calculate_binary(
|
|
158
|
+
self, left: OperandValue, right: OperandValue
|
|
159
|
+
) -> Union[pd.Series, OperandValue]:
|
|
147
160
|
pass
|
|
148
161
|
|
|
149
|
-
def calculate_vector(self, data: List[
|
|
162
|
+
def calculate_vector(self, data: List[OperandValue]) -> Union[pd.Series, OperandValue]:
|
|
150
163
|
pass
|
|
151
164
|
|
|
152
165
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -2,6 +2,7 @@ import abc
|
|
|
2
2
|
from typing import Dict, List, Optional, Tuple
|
|
3
3
|
|
|
4
4
|
import pandas as pd
|
|
5
|
+
from upgini.autofe.operand import OperandValue
|
|
5
6
|
from upgini.autofe.operator import PandasOperator
|
|
6
7
|
|
|
7
8
|
|
|
@@ -22,7 +23,8 @@ class TimeSeriesBase(PandasOperator, abc.ABC):
|
|
|
22
23
|
)
|
|
23
24
|
return res
|
|
24
25
|
|
|
25
|
-
def calculate_vector(self, data: List[
|
|
26
|
+
def calculate_vector(self, data: List[OperandValue]) -> pd.Series:
|
|
27
|
+
data = [operand.as_series() for operand in data]
|
|
26
28
|
# assuming first is date, last is value, rest is group columns
|
|
27
29
|
date = pd.to_datetime(data[0], unit=self.date_unit, errors="coerce")
|
|
28
30
|
ts = pd.concat([date] + data[1:], axis=1)
|
|
@@ -5,6 +5,7 @@ import numpy as np
|
|
|
5
5
|
import pandas as pd
|
|
6
6
|
|
|
7
7
|
from upgini.autofe.all_operators import find_op
|
|
8
|
+
from upgini.autofe.operand import OperandValue
|
|
8
9
|
from upgini.autofe.operator import PandasOperator, ParametrizedOperator
|
|
9
10
|
from upgini.autofe.timeseries.base import TimeSeriesBase
|
|
10
11
|
from upgini.autofe.utils import pydantic_validator
|
|
@@ -117,14 +118,15 @@ class CrossSeriesInteraction(TimeSeriesBase, ParametrizedOperator):
|
|
|
117
118
|
def _get_interaction_op_name(self) -> str:
|
|
118
119
|
return self.interaction_op.alias or self.interaction_op.to_formula()
|
|
119
120
|
|
|
120
|
-
def calculate_vector(self, data: List[
|
|
121
|
+
def calculate_vector(self, data: List[OperandValue]) -> pd.Series:
|
|
122
|
+
data = [operand.as_series() for operand in data]
|
|
121
123
|
left_mask = self._get_mask(data, self.left_descriptor)
|
|
122
124
|
left = self._extract_series(data, left_mask)
|
|
123
125
|
|
|
124
126
|
right_mask = self._get_mask(data, self.right_descriptor)
|
|
125
127
|
right = self._extract_series(data, right_mask)
|
|
126
128
|
|
|
127
|
-
interaction: pd.Series = self.interaction_op.
|
|
129
|
+
interaction: pd.Series = self.interaction_op.calculate(left=left, right=right)
|
|
128
130
|
interaction = interaction.reindex(self._get_index(data))
|
|
129
131
|
res = pd.Series(np.nan, index=data[-1].index, name=data[-1].name)
|
|
130
132
|
res.loc[left_mask] = interaction[left_mask].values
|
|
@@ -137,7 +139,7 @@ class CrossSeriesInteraction(TimeSeriesBase, ParametrizedOperator):
|
|
|
137
139
|
|
|
138
140
|
def _extract_series(self, data: List[pd.Series], mask: pd.Series) -> pd.Series:
|
|
139
141
|
masked_data = [d[mask] for d in data]
|
|
140
|
-
shifted = super().calculate_vector(masked_data)
|
|
142
|
+
shifted = super().calculate_vector([OperandValue.from_series(d) for d in masked_data])
|
|
141
143
|
shifted.index = self._get_index(masked_data)
|
|
142
144
|
return shifted
|
|
143
145
|
|
|
@@ -3,6 +3,7 @@ from typing import Dict, List, Optional
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
import pandas as pd
|
|
5
5
|
|
|
6
|
+
from upgini.autofe.operand import OperandValue
|
|
6
7
|
from upgini.autofe.operator import PandasOperator, ParametrizedOperator, VectorizableMixin
|
|
7
8
|
from upgini.autofe.utils import bin_index, bin_index_vectorized, pydantic_validator
|
|
8
9
|
|
|
@@ -13,7 +14,8 @@ class Abs(PandasOperator, VectorizableMixin):
|
|
|
13
14
|
is_vectorizable: bool = True
|
|
14
15
|
group_index: int = 0
|
|
15
16
|
|
|
16
|
-
def calculate_unary(self, data:
|
|
17
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
18
|
+
data = data.as_series()
|
|
17
19
|
return data.astype(np.float64).abs()
|
|
18
20
|
|
|
19
21
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -27,7 +29,8 @@ class Log(PandasOperator, VectorizableMixin):
|
|
|
27
29
|
output_type: Optional[str] = "float"
|
|
28
30
|
group_index: int = 0
|
|
29
31
|
|
|
30
|
-
def calculate_unary(self, data:
|
|
32
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
33
|
+
data = data.as_series()
|
|
31
34
|
return self._round_value(np.log(np.abs(data.replace(0, np.nan))), 10)
|
|
32
35
|
|
|
33
36
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -41,7 +44,8 @@ class Sqrt(PandasOperator, VectorizableMixin):
|
|
|
41
44
|
output_type: Optional[str] = "float"
|
|
42
45
|
group_index: int = 0
|
|
43
46
|
|
|
44
|
-
def calculate_unary(self, data:
|
|
47
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
48
|
+
data = data.as_series()
|
|
45
49
|
return self._round_value(np.sqrt(np.abs(data)))
|
|
46
50
|
|
|
47
51
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -54,7 +58,8 @@ class Square(PandasOperator, VectorizableMixin):
|
|
|
54
58
|
is_vectorizable: bool = True
|
|
55
59
|
group_index: int = 0
|
|
56
60
|
|
|
57
|
-
def calculate_unary(self, data:
|
|
61
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
62
|
+
data = data.as_series()
|
|
58
63
|
return np.square(data)
|
|
59
64
|
|
|
60
65
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -68,7 +73,8 @@ class Sigmoid(PandasOperator, VectorizableMixin):
|
|
|
68
73
|
output_type: Optional[str] = "float"
|
|
69
74
|
group_index: int = 0
|
|
70
75
|
|
|
71
|
-
def calculate_unary(self, data:
|
|
76
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
77
|
+
data = data.as_series()
|
|
72
78
|
return self._round_value(1 / (1 + np.exp(-data)))
|
|
73
79
|
|
|
74
80
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -83,7 +89,8 @@ class Floor(PandasOperator, VectorizableMixin):
|
|
|
83
89
|
input_type: Optional[str] = "continuous"
|
|
84
90
|
group_index: int = 0
|
|
85
91
|
|
|
86
|
-
def calculate_unary(self, data:
|
|
92
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
93
|
+
data = data.as_series()
|
|
87
94
|
return np.floor(data)
|
|
88
95
|
|
|
89
96
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -97,7 +104,8 @@ class Residual(PandasOperator, VectorizableMixin):
|
|
|
97
104
|
input_type: Optional[str] = "continuous"
|
|
98
105
|
group_index: int = 0
|
|
99
106
|
|
|
100
|
-
def calculate_unary(self, data:
|
|
107
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
108
|
+
data = data.as_series()
|
|
101
109
|
return data - np.floor(data)
|
|
102
110
|
|
|
103
111
|
def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
|
@@ -111,7 +119,8 @@ class Freq(PandasOperator):
|
|
|
111
119
|
is_distribution_dependent: bool = True
|
|
112
120
|
input_type: Optional[str] = "discrete"
|
|
113
121
|
|
|
114
|
-
def calculate_unary(self, data:
|
|
122
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
123
|
+
data = data.as_series()
|
|
115
124
|
value_counts = data.value_counts(normalize=True)
|
|
116
125
|
return self._loc(data, value_counts)
|
|
117
126
|
|
|
@@ -122,7 +131,8 @@ class Norm(PandasOperator):
|
|
|
122
131
|
output_type: Optional[str] = "float"
|
|
123
132
|
norm: Optional[float] = None
|
|
124
133
|
|
|
125
|
-
def calculate_unary(self, data:
|
|
134
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
135
|
+
data = data.as_series()
|
|
126
136
|
data_dropna = data.dropna()
|
|
127
137
|
if data_dropna.empty:
|
|
128
138
|
return data
|
|
@@ -162,7 +172,8 @@ class Bin(PandasOperator):
|
|
|
162
172
|
bin_bounds: List[int] = []
|
|
163
173
|
is_categorical: bool = True
|
|
164
174
|
|
|
165
|
-
def calculate_unary(self, data:
|
|
175
|
+
def calculate_unary(self, data: OperandValue) -> pd.Series:
|
|
176
|
+
data = data.as_series()
|
|
166
177
|
bounds_arr = np.asarray(self.bin_bounds, dtype=np.float64)
|
|
167
178
|
values = pd.to_numeric(data, errors="coerce").to_numpy(dtype=np.float64, copy=False)
|
|
168
179
|
result = bin_index_vectorized(values, bounds_arr)
|
|
@@ -2,6 +2,7 @@ from typing import List, Optional
|
|
|
2
2
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
|
|
5
|
+
from upgini.autofe.operand import OperandValue
|
|
5
6
|
from upgini.autofe.operator import OperatorRegistry, PandasOperator, ParametrizedOperator, VectorizableMixin
|
|
6
7
|
|
|
7
8
|
|
|
@@ -11,8 +12,9 @@ class Mean(PandasOperator, VectorizableMixin):
|
|
|
11
12
|
is_vector: bool = True
|
|
12
13
|
group_index: int = 0
|
|
13
14
|
|
|
14
|
-
def calculate_vector(self, data: List[
|
|
15
|
-
|
|
15
|
+
def calculate_vector(self, data: List[OperandValue]) -> pd.Series:
|
|
16
|
+
series = [operand.as_series() for operand in data]
|
|
17
|
+
return pd.DataFrame(series).T.fillna(0).mean(axis=1)
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
class Sum(PandasOperator, VectorizableMixin):
|
|
@@ -20,8 +22,9 @@ class Sum(PandasOperator, VectorizableMixin):
|
|
|
20
22
|
is_vector: bool = True
|
|
21
23
|
group_index: int = 0
|
|
22
24
|
|
|
23
|
-
def calculate_vector(self, data: List[
|
|
24
|
-
|
|
25
|
+
def calculate_vector(self, data: List[OperandValue]) -> pd.Series:
|
|
26
|
+
series = [operand.as_series() for operand in data]
|
|
27
|
+
return pd.DataFrame(series).T.fillna(0).sum(axis=1)
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
class Vectorize(PandasOperator, VectorizableMixin):
|
|
@@ -29,8 +32,9 @@ class Vectorize(PandasOperator, VectorizableMixin):
|
|
|
29
32
|
is_vector: bool = True
|
|
30
33
|
group_index: int = 0
|
|
31
34
|
|
|
32
|
-
def calculate_vector(self, data: List[
|
|
33
|
-
|
|
35
|
+
def calculate_vector(self, data: List[OperandValue]) -> pd.Series:
|
|
36
|
+
series = [operand.as_series() for operand in data]
|
|
37
|
+
return pd.DataFrame(series).T.apply(lambda x: x.to_list(), axis=1)
|
|
34
38
|
|
|
35
39
|
|
|
36
40
|
class OnnxModel(PandasOperator, ParametrizedOperator, metaclass=OperatorRegistry):
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.2.156.dev5"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{upgini-1.2.156.dev5 → upgini-1.2.157.dev1}/src/upgini/resource_bundle/strings_widget.properties
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|