upgini 1.2.157.dev1__tar.gz → 1.2.157.dev3__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.
Files changed (85) hide show
  1. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/PKG-INFO +1 -1
  2. upgini-1.2.157.dev3/src/upgini/__about__.py +1 -0
  3. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/binary.py +18 -3
  4. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/date.py +1 -1
  5. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/feature.py +14 -18
  6. upgini-1.2.157.dev3/src/upgini/autofe/operand.py +133 -0
  7. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/operator.py +17 -14
  8. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/timeseries/cross.py +1 -1
  9. upgini-1.2.157.dev1/src/upgini/__about__.py +0 -1
  10. upgini-1.2.157.dev1/src/upgini/autofe/operand.py +0 -73
  11. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/.gitignore +0 -0
  12. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/LICENSE +0 -0
  13. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/README.md +0 -0
  14. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/pyproject.toml +0 -0
  15. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/__init__.py +0 -0
  16. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/ads.py +0 -0
  17. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/ads_management/__init__.py +0 -0
  18. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/ads_management/ads_manager.py +0 -0
  19. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/__init__.py +0 -0
  20. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/all_operators.py +0 -0
  21. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/groupby.py +0 -0
  22. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/timeseries/__init__.py +0 -0
  23. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/timeseries/base.py +0 -0
  24. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/timeseries/delta.py +0 -0
  25. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/timeseries/lag.py +0 -0
  26. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/timeseries/roll.py +0 -0
  27. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/timeseries/trend.py +0 -0
  28. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/timeseries/volatility.py +0 -0
  29. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/unary.py +0 -0
  30. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/utils.py +0 -0
  31. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/autofe/vector.py +0 -0
  32. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/data_source/__init__.py +0 -0
  33. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/data_source/data_source_publisher.py +0 -0
  34. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/dataset.py +0 -0
  35. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/errors.py +0 -0
  36. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/features_enricher.py +0 -0
  37. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/http.py +0 -0
  38. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/mdc/__init__.py +0 -0
  39. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/mdc/context.py +0 -0
  40. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/metadata.py +0 -0
  41. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/metrics.py +0 -0
  42. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/normalizer/__init__.py +0 -0
  43. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/normalizer/normalize_utils.py +0 -0
  44. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/resource_bundle/__init__.py +0 -0
  45. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/resource_bundle/exceptions.py +0 -0
  46. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/resource_bundle/strings.properties +0 -0
  47. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/resource_bundle/strings_widget.properties +0 -0
  48. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/sampler/__init__.py +0 -0
  49. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/sampler/base.py +0 -0
  50. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/sampler/random_under_sampler.py +0 -0
  51. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/sampler/utils.py +0 -0
  52. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/search_task.py +0 -0
  53. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/spinner.py +0 -0
  54. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/Roboto-Regular.ttf +0 -0
  55. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/__init__.py +0 -0
  56. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/base_search_key_detector.py +0 -0
  57. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/blocked_time_series.py +0 -0
  58. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/config.py +0 -0
  59. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/country_utils.py +0 -0
  60. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/custom_loss_utils.py +0 -0
  61. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/cv_utils.py +0 -0
  62. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/datetime_utils.py +0 -0
  63. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/deduplicate_utils.py +0 -0
  64. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/display_utils.py +0 -0
  65. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/email_utils.py +0 -0
  66. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/fallback_progress_bar.py +0 -0
  67. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/feature_info.py +0 -0
  68. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/features_validator.py +0 -0
  69. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/format.py +0 -0
  70. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/hash_utils.py +0 -0
  71. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/ip_utils.py +0 -0
  72. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/mstats.py +0 -0
  73. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/one_hot_encoder.py +0 -0
  74. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/phone_utils.py +0 -0
  75. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/postal_code_utils.py +0 -0
  76. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/progress_bar.py +0 -0
  77. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/psi.py +0 -0
  78. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/sample_utils.py +0 -0
  79. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/sklearn_ext.py +0 -0
  80. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/sort.py +0 -0
  81. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/target_utils.py +0 -0
  82. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/track_info.py +0 -0
  83. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/ts_utils.py +0 -0
  84. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/utils/warning_counter.py +0 -0
  85. {upgini-1.2.157.dev1 → upgini-1.2.157.dev3}/src/upgini/version_validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: upgini
3
- Version: 1.2.157.dev1
3
+ Version: 1.2.157.dev3
4
4
  Summary: Intelligent data search & enrichment for Machine Learning
5
5
  Project-URL: Bug Reports, https://github.com/upgini/upgini/issues
6
6
  Project-URL: Homepage, https://upgini.com/
@@ -0,0 +1 @@
1
+ __version__ = "1.2.157.dev3"
@@ -6,7 +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
+ from upgini.autofe.operand import OperandKind, OperandValue
10
10
  from upgini.autofe.operator import PandasOperator, VectorizableMixin
11
11
 
12
12
 
@@ -158,8 +158,23 @@ class Distance(PandasOperator):
158
158
  has_symmetry_importance: bool = True
159
159
 
160
160
  def calculate_binary(self, left: OperandValue, right: OperandValue) -> pd.Series:
161
- left = left.as_series()
162
- right = right.as_series()
161
+ if left.kind == OperandKind.MATRIX and right.kind == OperandKind.MATRIX:
162
+ return self._calculate_binary_matrices(left, right)
163
+ return self._calculate_binary_series(left.as_series(), right.as_series())
164
+
165
+ def _calculate_binary_matrices(self, left: OperandValue, right: OperandValue) -> pd.Series:
166
+ left_mat = left.as_matrix()
167
+ right_mat = right.as_matrix()
168
+ dot = np.sum(left_mat * right_mat, axis=1)
169
+ left_norm = np.sqrt(np.sum(left_mat * left_mat, axis=1))
170
+ right_norm = np.sqrt(np.sum(right_mat * right_mat, axis=1))
171
+ denom = left_norm * right_norm
172
+ with np.errstate(invalid="ignore", divide="ignore"):
173
+ result = 1 - dot / denom
174
+ result[denom == 0] = np.nan
175
+ return pd.Series(result, index=left.index, dtype=np.float64)
176
+
177
+ def _calculate_binary_series(self, left: pd.Series, right: pd.Series) -> pd.Series:
163
178
  return pd.Series(
164
179
  1 - self.__dot(left, right) / (self.__norm(left) * self.__norm(right)), index=left.index
165
180
  ).astype(np.float64)
@@ -341,7 +341,7 @@ class DateListDiff(PandasOperator, DateDiffMixin, ParametrizedOperator):
341
341
 
342
342
  right_mask = DateListDiffLists._non_empty_list_mask(right)
343
343
  diff_lists = self._lists_op().calculate(left=left, right=right)
344
- result = self._agg_op().calculate(data=diff_lists)
344
+ result = self._agg_op().calculate(data=diff_lists).as_series()
345
345
  if self.aggregation in _count_aggregations:
346
346
  result[~right_mask] = 0.0
347
347
  return result.astype(np.float64)
@@ -8,7 +8,7 @@ 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
+ from upgini.autofe.operand import CalculationContext, OperandValue, operand_to_output
12
12
  from upgini.autofe.operator import Operator, PandasOperator
13
13
  from upgini.autofe.timeseries.base import TimeSeriesBase
14
14
  from upgini.autofe.utils import pydantic_dump_method, pydantic_parse_method
@@ -62,9 +62,14 @@ class Column:
62
62
  def infer_type(self, data: pd.DataFrame) -> DtypeObj:
63
63
  return data[self.name].dtype
64
64
 
65
- def calculate(self, data: Union[pd.DataFrame, CalculationContext]) -> pd.Series:
65
+ def calculate(
66
+ self,
67
+ data: Union[pd.DataFrame, CalculationContext],
68
+ preserve_kind: bool = False,
69
+ ) -> Union[pd.Series, OperandValue]:
66
70
  ctx = data if isinstance(data, CalculationContext) else CalculationContext.from_dataframe(data)
67
- return self._eval(ctx).as_series()
71
+ operand = self._eval(ctx)
72
+ return operand if preserve_kind else operand.as_series()
68
73
 
69
74
  def _eval(self, ctx: CalculationContext) -> OperandValue:
70
75
  self.data = ctx.resolve_operand(self.name)
@@ -245,10 +250,11 @@ class Feature:
245
250
  self,
246
251
  data: Union[pd.DataFrame, CalculationContext],
247
252
  is_root: bool = False,
248
- ) -> Union[pd.Series, pd.DataFrame]:
253
+ preserve_kind: bool = False,
254
+ ) -> Union[pd.Series, pd.DataFrame, OperandValue]:
249
255
  ctx = data if isinstance(data, CalculationContext) else CalculationContext.from_dataframe(data)
250
256
  result = self._eval(ctx)
251
- new_data = self._operand_to_output(result)
257
+ new_data = operand_to_output(result, preserve_kind=preserve_kind)
252
258
 
253
259
  if is_root:
254
260
  self.data = new_data
@@ -258,24 +264,14 @@ class Feature:
258
264
  if isinstance(self.op, PandasOperator):
259
265
  if self.op.is_vector:
260
266
  operands = [child._eval(ctx) for child in self.children]
261
- result = self.op.calculate(data=operands)
262
- return wrap_operand(result)
267
+ return self.op.calculate(data=operands)
263
268
  left = self.children[0]._eval(ctx)
264
269
  if len(self.children) < 2:
265
- result = self.op.calculate(data=left)
266
- return wrap_operand(result, source=left.source)
270
+ return self.op.calculate(data=left)
267
271
  right = self.children[1]._eval(ctx)
268
- result = self.op.calculate(left=left, right=right)
269
- return wrap_operand(result, source=left.source)
272
+ return self.op.calculate(left=left, right=right)
270
273
  raise NotImplementedError(f"Unrecognized operator {self.op.name}.")
271
274
 
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
-
279
275
  @staticmethod
280
276
  def check_xor(left: Union[Column, "Feature"], right: Union[Column, "Feature"]) -> bool:
281
277
  def _get_all_columns(feature: Union[Column, "Feature"]) -> List[str]:
@@ -0,0 +1,133 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from enum import Enum
5
+ from typing import Dict, 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: Optional[pd.DataFrame] = None
54
+ matrices: Dict[str, np.ndarray] = field(default_factory=dict)
55
+ row_index: Optional[pd.Index] = None
56
+
57
+ def __post_init__(self) -> None:
58
+ if self.data is None and not self.matrices:
59
+ raise ValueError("CalculationContext requires data and/or matrices")
60
+ if self.matrices:
61
+ lengths = {name: matrix.shape[0] for name, matrix in self.matrices.items()}
62
+ if len(set(lengths.values())) > 1:
63
+ raise ValueError(f"Matrix row counts mismatch: {lengths}")
64
+
65
+ @property
66
+ def index(self) -> pd.Index:
67
+ if self.data is not None:
68
+ return self.data.index
69
+ if self.row_index is not None:
70
+ return self.row_index
71
+ if self.matrices:
72
+ nrows = next(iter(self.matrices.values())).shape[0]
73
+ return pd.RangeIndex(nrows)
74
+ raise ValueError("CalculationContext has no index source")
75
+
76
+ @classmethod
77
+ def from_dataframe(cls, data: pd.DataFrame) -> "CalculationContext":
78
+ return cls(data=data)
79
+
80
+ @classmethod
81
+ def from_matrices(
82
+ cls,
83
+ matrices: Dict[str, np.ndarray],
84
+ index: Optional[pd.Index] = None,
85
+ ) -> "CalculationContext":
86
+ return cls(matrices=matrices, row_index=index)
87
+
88
+ def resolve_operand(self, name: str) -> OperandValue:
89
+ matrix = self.matrices.get(name)
90
+ if matrix is not None:
91
+ return OperandValue.from_matrix(matrix, index=self.index, source=name)
92
+ if self.data is not None and name in self.data:
93
+ return OperandValue.from_series(self.data[name], source=name)
94
+ raise KeyError(name)
95
+
96
+
97
+ def finalize_operand(operand: OperandValue) -> OperandValue:
98
+ if operand.kind == OperandKind.MATRIX:
99
+ matrix = np.where(np.isinf(operand.as_matrix()), np.nan, operand.as_matrix())
100
+ return OperandValue.from_matrix(matrix, index=operand.index, source=operand.source)
101
+ if operand.kind == OperandKind.ARRAY:
102
+ array = operand.data # type: ignore[assignment]
103
+ array = np.where(np.isinf(array), np.nan, array)
104
+ return OperandValue.from_array(array, index=operand.index, source=operand.source)
105
+ new_data = operand.as_series()
106
+ if (str(new_data.dtype) == "category") | (str(new_data.dtype) == "object"):
107
+ return OperandValue.from_series(new_data, source=operand.source)
108
+ return OperandValue.from_series(new_data.replace([-np.inf, np.inf], np.nan), source=operand.source)
109
+
110
+
111
+ def operand_to_output(operand: OperandValue, preserve_kind: bool = False) -> Union[pd.Series, OperandValue]:
112
+ finalized = finalize_operand(operand)
113
+ if preserve_kind:
114
+ return finalized
115
+ return finalized.as_series()
116
+
117
+
118
+ def wrap_operand(
119
+ result: Union[pd.Series, np.ndarray, "OperandValue"],
120
+ source: Optional[str] = None,
121
+ index: Optional[pd.Index] = None,
122
+ ) -> "OperandValue":
123
+ if isinstance(result, OperandValue):
124
+ return result
125
+ if isinstance(result, np.ndarray):
126
+ if index is None:
127
+ raise TypeError("index is required when wrapping ndarray result")
128
+ if result.ndim == 1:
129
+ return OperandValue.from_array(result, index=index, source=source)
130
+ if result.ndim == 2:
131
+ return OperandValue.from_matrix(result, index=index, source=source)
132
+ raise TypeError(f"Cannot wrap ndarray with {result.ndim} dimensions")
133
+ return OperandValue.from_series(result, source=source)
@@ -6,7 +6,7 @@ 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
9
+ from upgini.autofe.operand import OperandValue, wrap_operand
10
10
 
11
11
 
12
12
  class OperatorRegistry(type(BaseModel)):
@@ -140,26 +140,29 @@ class PandasOperator(Operator, abc.ABC):
140
140
  return value
141
141
  return OperandValue.from_series(value)
142
142
 
143
- def calculate(self, **kwargs) -> Union[pd.Series, OperandValue]:
143
+ def calculate(self, **kwargs) -> OperandValue:
144
144
  if self.is_unary:
145
- return self.calculate_unary(self._operand(kwargs["data"]))
146
- elif self.is_binary or self.is_grouping:
147
- return self.calculate_binary(
148
- self._operand(kwargs["left"]),
149
- self._operand(kwargs["right"]),
150
- )
151
- else:
152
- return self.calculate_vector([self._operand(value) for value in kwargs["data"]])
153
-
154
- def calculate_unary(self, data: OperandValue) -> Union[pd.Series, OperandValue]:
145
+ data = self._operand(kwargs["data"])
146
+ return wrap_operand(self.calculate_unary(data), source=data.source, index=data.index)
147
+ if self.is_binary or self.is_grouping:
148
+ left = self._operand(kwargs["left"])
149
+ right = self._operand(kwargs["right"])
150
+ return wrap_operand(self.calculate_binary(left, right), source=left.source, index=left.index)
151
+ operands = [self._operand(value) for value in kwargs["data"]]
152
+ index = operands[0].index if operands else None
153
+ return wrap_operand(self.calculate_vector(operands), index=index)
154
+
155
+ def calculate_unary(self, data: OperandValue) -> Union[pd.Series, np.ndarray, OperandValue]:
155
156
  pass
156
157
 
157
158
  def calculate_binary(
158
159
  self, left: OperandValue, right: OperandValue
159
- ) -> Union[pd.Series, OperandValue]:
160
+ ) -> Union[pd.Series, np.ndarray, OperandValue]:
160
161
  pass
161
162
 
162
- def calculate_vector(self, data: List[OperandValue]) -> Union[pd.Series, OperandValue]:
163
+ def calculate_vector(
164
+ self, data: List[OperandValue]
165
+ ) -> Union[pd.Series, np.ndarray, OperandValue]:
163
166
  pass
164
167
 
165
168
  def calculate_group(self, data: pd.DataFrame, **kwargs) -> pd.DataFrame:
@@ -126,7 +126,7 @@ class CrossSeriesInteraction(TimeSeriesBase, ParametrizedOperator):
126
126
  right_mask = self._get_mask(data, self.right_descriptor)
127
127
  right = self._extract_series(data, right_mask)
128
128
 
129
- interaction: pd.Series = self.interaction_op.calculate(left=left, right=right)
129
+ interaction: pd.Series = self.interaction_op.calculate(left=left, right=right).as_series()
130
130
  interaction = interaction.reindex(self._get_index(data))
131
131
  res = pd.Series(np.nan, index=data[-1].index, name=data[-1].name)
132
132
  res.loc[left_mask] = interaction[left_mask].values
@@ -1 +0,0 @@
1
- __version__ = "1.2.157.dev1"
@@ -1,73 +0,0 @@
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)
File without changes
File without changes
File without changes