pylegend 0.11.0__py3-none-any.whl → 0.12.0__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.
Files changed (25) hide show
  1. pylegend/core/database/sql_to_string/db_extension.py +68 -6
  2. pylegend/core/language/legendql_api/legendql_api_custom_expressions.py +190 -5
  3. pylegend/core/language/pandas_api/pandas_api_series.py +3 -0
  4. pylegend/core/sql/metamodel.py +4 -1
  5. pylegend/core/tds/legendql_api/frames/functions/legendql_api_distinct_function.py +53 -7
  6. pylegend/core/tds/legendql_api/frames/legendql_api_base_tds_frame.py +146 -4
  7. pylegend/core/tds/legendql_api/frames/legendql_api_tds_frame.py +33 -2
  8. pylegend/core/tds/pandas_api/frames/functions/assign_function.py +65 -23
  9. pylegend/core/tds/pandas_api/frames/functions/drop.py +3 -3
  10. pylegend/core/tds/pandas_api/frames/functions/dropna.py +167 -0
  11. pylegend/core/tds/pandas_api/frames/functions/fillna.py +162 -0
  12. pylegend/core/tds/pandas_api/frames/functions/filter.py +10 -5
  13. pylegend/core/tds/pandas_api/frames/functions/truncate_function.py +151 -120
  14. pylegend/core/tds/pandas_api/frames/pandas_api_applied_function_tds_frame.py +7 -3
  15. pylegend/core/tds/pandas_api/frames/pandas_api_base_tds_frame.py +300 -34
  16. pylegend/core/tds/pandas_api/frames/pandas_api_tds_frame.py +78 -9
  17. pylegend/extensions/tds/pandas_api/frames/pandas_api_legend_function_input_frame.py +9 -4
  18. pylegend/extensions/tds/pandas_api/frames/pandas_api_legend_service_input_frame.py +12 -5
  19. pylegend/extensions/tds/pandas_api/frames/pandas_api_table_spec_input_frame.py +12 -4
  20. {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/METADATA +1 -1
  21. {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/RECORD +25 -23
  22. {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/WHEEL +0 -0
  23. {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/licenses/LICENSE +0 -0
  24. {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/licenses/LICENSE.spdx +0 -0
  25. {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/licenses/NOTICE +0 -0
@@ -12,15 +12,24 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ import copy
15
16
  from abc import ABCMeta, abstractmethod
16
17
  from datetime import date, datetime
17
18
  from typing import TYPE_CHECKING
18
19
 
20
+ from typing_extensions import Concatenate
21
+
22
+ try:
23
+ from typing import ParamSpec
24
+ except Exception:
25
+ from typing_extensions import ParamSpec # type: ignore
26
+
19
27
  import pandas as pd
20
28
 
21
29
  from pylegend._typing import (
22
30
  PyLegendSequence,
23
31
  PyLegendTypeVar,
32
+ PyLegendType,
24
33
  PyLegendList,
25
34
  PyLegendTuple,
26
35
  PyLegendSet,
@@ -33,7 +42,11 @@ from pylegend.core.database.sql_to_string import (
33
42
  SqlToStringConfig,
34
43
  SqlToStringFormat
35
44
  )
36
- from pylegend.core.language import PyLegendPrimitive, PyLegendInteger, PyLegendBoolean
45
+ from pylegend.core.language import (
46
+ PyLegendPrimitive,
47
+ PyLegendInteger,
48
+ PyLegendBoolean,
49
+ )
37
50
  from pylegend.core.language.pandas_api.pandas_api_aggregate_specification import PyLegendAggInput
38
51
  from pylegend.core.language.pandas_api.pandas_api_tds_row import PandasApiTdsRow
39
52
  from pylegend.core.language.shared.primitives.primitive import PyLegendPrimitiveOrPythonPrimitive
@@ -47,7 +60,7 @@ from pylegend.core.tds.result_handler import (
47
60
  )
48
61
  from pylegend.core.tds.tds_column import TdsColumn
49
62
  from pylegend.core.tds.tds_frame import FrameToPureConfig
50
- from pylegend.core.tds.tds_frame import FrameToSqlConfig
63
+ from pylegend.core.tds.tds_frame import FrameToSqlConfig, PyLegendTdsFrame
51
64
  from pylegend.extensions.tds.result_handler import (
52
65
  ToPandasDfResultHandler,
53
66
  PandasDfReadConfig,
@@ -62,6 +75,7 @@ __all__: PyLegendSequence[str] = [
62
75
  ]
63
76
 
64
77
  R = PyLegendTypeVar('R')
78
+ P = ParamSpec("P")
65
79
 
66
80
 
67
81
  class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
@@ -73,9 +87,12 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
73
87
  cols = "[" + ", ".join([str(c) for c in columns]) + "]"
74
88
  raise ValueError(f"TdsFrame cannot have duplicated column names. Passed columns: {cols}")
75
89
  self.__columns = [c.copy() for c in columns]
90
+ self._transformed_frame = None
76
91
 
77
92
  def columns(self) -> PyLegendSequence[TdsColumn]:
78
- return [c.copy() for c in self.__columns]
93
+ if self._transformed_frame is None:
94
+ return [c.copy() for c in self.__columns]
95
+ return self._transformed_frame.columns()
79
96
 
80
97
  def __getitem__(
81
98
  self,
@@ -97,7 +114,8 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
97
114
  if col.get_name() == key:
98
115
  col_type = col.get_type()
99
116
  if col_type == "Boolean":
100
- from pylegend.core.language.pandas_api.pandas_api_series import BooleanSeries # pragma: no cover
117
+ from pylegend.core.language.pandas_api.pandas_api_series import \
118
+ BooleanSeries # pragma: no cover
101
119
  return BooleanSeries(self, key) # pragma: no cover (Boolean column not supported in PURE)
102
120
  elif col_type == "String":
103
121
  from pylegend.core.language.pandas_api.pandas_api_series import StringSeries
@@ -130,6 +148,41 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
130
148
  else:
131
149
  raise TypeError(f"Invalid key type: {type(key)}. Expected str, list, or boolean expression")
132
150
 
151
+ def __setitem__(self, key: str, value: PyLegendUnion["Series", PyLegendPrimitiveOrPythonPrimitive]) -> None:
152
+ """
153
+ Pandas-like column assignment with replace semantics:
154
+ - If column exists, drop it first.
155
+ - Then assign the new value (Series or constant).
156
+ """
157
+ from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
158
+ PandasApiAppliedFunctionTdsFrame
159
+ )
160
+ from pylegend.core.tds.pandas_api.frames.functions.assign_function import AssignFunction
161
+ from pylegend.core.language.pandas_api.pandas_api_series import Series
162
+
163
+ # Type Check
164
+ if not isinstance(key, str):
165
+ raise TypeError(f"Column name must be a string, got: {type(key)}")
166
+
167
+ # Reject cross-frame assignment
168
+ if isinstance(value, Series):
169
+ origin = value.get_base_frame()
170
+ if origin is not None and origin is not self:
171
+ raise ValueError("Assignment from a different frame is not allowed")
172
+
173
+ # Normalize the assignment value
174
+ col_def = {}
175
+ if callable(value):
176
+ col_def[key] = value
177
+ else:
178
+ col_def[key] = lambda row: value
179
+
180
+ working_frame = copy.deepcopy(self)
181
+ assign_applied = PandasApiAppliedFunctionTdsFrame(AssignFunction(working_frame, col_definitions=col_def))
182
+
183
+ self._transformed_frame = assign_applied # type: ignore
184
+ self.__columns = assign_applied.columns()
185
+
133
186
  def assign(
134
187
  self,
135
188
  **kwargs: PyLegendCallable[
@@ -217,7 +270,7 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
217
270
  index: PyLegendOptional[PyLegendUnion[str, PyLegendSequence[str], PyLegendSet[str]]] = None,
218
271
  columns: PyLegendOptional[PyLegendUnion[str, PyLegendSequence[str], PyLegendSet[str]]] = None,
219
272
  level: PyLegendOptional[PyLegendUnion[int, PyLegendInteger, str]] = None,
220
- inplace: PyLegendUnion[bool, PyLegendBoolean] = True,
273
+ inplace: PyLegendUnion[bool, PyLegendBoolean] = False,
221
274
  errors: str = "raise",
222
275
  ) -> "PandasApiTdsFrame":
223
276
  from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import \
@@ -238,11 +291,11 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
238
291
  )
239
292
 
240
293
  def aggregate(
241
- self,
242
- func: PyLegendAggInput,
243
- axis: PyLegendUnion[int, str] = 0,
244
- *args: PyLegendPrimitiveOrPythonPrimitive,
245
- **kwargs: PyLegendPrimitiveOrPythonPrimitive
294
+ self,
295
+ func: PyLegendAggInput,
296
+ axis: PyLegendUnion[int, str] = 0,
297
+ *args: PyLegendPrimitiveOrPythonPrimitive,
298
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
246
299
  ) -> "PandasApiTdsFrame":
247
300
  from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
248
301
  PandasApiAppliedFunctionTdsFrame
@@ -257,11 +310,11 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
257
310
  ))
258
311
 
259
312
  def agg(
260
- self,
261
- func: PyLegendAggInput,
262
- axis: PyLegendUnion[int, str] = 0,
263
- *args: PyLegendPrimitiveOrPythonPrimitive,
264
- **kwargs: PyLegendPrimitiveOrPythonPrimitive
313
+ self,
314
+ func: PyLegendAggInput,
315
+ axis: PyLegendUnion[int, str] = 0,
316
+ *args: PyLegendPrimitiveOrPythonPrimitive,
317
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
265
318
  ) -> "PandasApiTdsFrame":
266
319
  from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
267
320
  PandasApiAppliedFunctionTdsFrame
@@ -293,7 +346,8 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
293
346
  if min_count != 0:
294
347
  raise NotImplementedError(f"min_count must be 0 in sum function, but got: {min_count}")
295
348
  if len(kwargs) > 0:
296
- raise NotImplementedError(f"Additional keyword arguments not supported in sum function: {list(kwargs.keys())}")
349
+ raise NotImplementedError(
350
+ f"Additional keyword arguments not supported in sum function: {list(kwargs.keys())}")
297
351
  return self.aggregate("sum", 0)
298
352
 
299
353
  def mean(
@@ -310,7 +364,8 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
310
364
  if numeric_only is not False:
311
365
  raise NotImplementedError("numeric_only=True is not currently supported in mean function.")
312
366
  if len(kwargs) > 0:
313
- raise NotImplementedError(f"Additional keyword arguments not supported in mean function: {list(kwargs.keys())}")
367
+ raise NotImplementedError(
368
+ f"Additional keyword arguments not supported in mean function: {list(kwargs.keys())}")
314
369
  return self.aggregate("mean", 0)
315
370
 
316
371
  def min(
@@ -327,7 +382,8 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
327
382
  if numeric_only is not False:
328
383
  raise NotImplementedError("numeric_only=True is not currently supported in min function.")
329
384
  if len(kwargs) > 0:
330
- raise NotImplementedError(f"Additional keyword arguments not supported in min function: {list(kwargs.keys())}")
385
+ raise NotImplementedError(
386
+ f"Additional keyword arguments not supported in min function: {list(kwargs.keys())}")
331
387
  return self.aggregate("min", 0)
332
388
 
333
389
  def max(
@@ -344,7 +400,8 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
344
400
  if numeric_only is not False:
345
401
  raise NotImplementedError("numeric_only=True is not currently supported in max function.")
346
402
  if len(kwargs) > 0:
347
- raise NotImplementedError(f"Additional keyword arguments not supported in max function: {list(kwargs.keys())}")
403
+ raise NotImplementedError(
404
+ f"Additional keyword arguments not supported in max function: {list(kwargs.keys())}")
348
405
  return self.aggregate("max", 0)
349
406
 
350
407
  def std(
@@ -360,11 +417,13 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
360
417
  if skipna is not True:
361
418
  raise NotImplementedError("skipna=False is not currently supported in std function.")
362
419
  if ddof != 1:
363
- raise NotImplementedError(f"Only ddof=1 (Sample Standard Deviation) is supported in std function, but got: {ddof}")
420
+ raise NotImplementedError(
421
+ f"Only ddof=1 (Sample Standard Deviation) is supported in std function, but got: {ddof}")
364
422
  if numeric_only is not False:
365
423
  raise NotImplementedError("numeric_only=True is not currently supported in std function.")
366
424
  if len(kwargs) > 0:
367
- raise NotImplementedError(f"Additional keyword arguments not supported in std function: {list(kwargs.keys())}")
425
+ raise NotImplementedError(
426
+ f"Additional keyword arguments not supported in std function: {list(kwargs.keys())}")
368
427
  return self.aggregate("std", 0)
369
428
 
370
429
  def var(
@@ -384,7 +443,8 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
384
443
  if numeric_only is not False:
385
444
  raise NotImplementedError("numeric_only=True is not currently supported in var function.")
386
445
  if len(kwargs) > 0:
387
- raise NotImplementedError(f"Additional keyword arguments not supported in var function: {list(kwargs.keys())}")
446
+ raise NotImplementedError(
447
+ f"Additional keyword arguments not supported in var function: {list(kwargs.keys())}")
388
448
  return self.aggregate("var", 0)
389
449
 
390
450
  def count(
@@ -398,18 +458,19 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
398
458
  if numeric_only is not False:
399
459
  raise NotImplementedError("numeric_only=True is not currently supported in count function.")
400
460
  if len(kwargs) > 0:
401
- raise NotImplementedError(f"Additional keyword arguments not supported in count function: {list(kwargs.keys())}")
461
+ raise NotImplementedError(
462
+ f"Additional keyword arguments not supported in count function: {list(kwargs.keys())}")
402
463
  return self.aggregate("count", 0)
403
464
 
404
465
  def groupby(
405
- self,
406
- by: PyLegendUnion[str, PyLegendList[str]],
407
- level: PyLegendOptional[PyLegendUnion[str, int, PyLegendList[str]]] = None,
408
- as_index: bool = False,
409
- sort: bool = True,
410
- group_keys: bool = False,
411
- observed: bool = False,
412
- dropna: bool = False,
466
+ self,
467
+ by: PyLegendUnion[str, PyLegendList[str]],
468
+ level: PyLegendOptional[PyLegendUnion[str, int, PyLegendList[str]]] = None,
469
+ as_index: bool = False,
470
+ sort: bool = True,
471
+ group_keys: bool = False,
472
+ observed: bool = False,
473
+ dropna: bool = False,
413
474
  ) -> "PandasApiGroupbyTdsFrame":
414
475
  from pylegend.core.tds.pandas_api.frames.pandas_api_groupby_tds_frame import (
415
476
  PandasApiGroupbyTdsFrame
@@ -547,13 +608,218 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
547
608
  )
548
609
  )
549
610
 
611
+ def apply(
612
+ self,
613
+ func: PyLegendUnion[
614
+ PyLegendCallable[Concatenate["Series", P], PyLegendPrimitiveOrPythonPrimitive],
615
+ str
616
+ ],
617
+ axis: PyLegendUnion[int, str] = 0,
618
+ raw: bool = False,
619
+ result_type: PyLegendOptional[str] = None,
620
+ args: PyLegendTuple[PyLegendPrimitiveOrPythonPrimitive, ...] = (),
621
+ by_row: PyLegendUnion[bool, str] = "compat",
622
+ engine: str = "python",
623
+ engine_kwargs: PyLegendOptional[PyLegendDict[str, PyLegendPrimitiveOrPythonPrimitive]] = None,
624
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
625
+ ) -> "PandasApiTdsFrame":
626
+ """
627
+ Pandas-like apply (columns-only):
628
+ - Supports callable func applied to each column (axis=0 or 'index')
629
+ - Internally delegates to assign by constructing lambdas per column
630
+ - Unsupported params raise NotImplementedError
631
+ """
632
+
633
+ from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
634
+ PandasApiAppliedFunctionTdsFrame
635
+ )
636
+ from pylegend.core.tds.pandas_api.frames.functions.assign_function import AssignFunction
637
+ from pylegend.core.language.pandas_api.pandas_api_series import Series
638
+
639
+ # Validation
640
+ if axis not in (0, "index"):
641
+ raise ValueError("Only column-wise apply is supported. Use axis=0 or 'index'")
642
+ if raw:
643
+ raise NotImplementedError("raw=True is not supported. Use raw=False")
644
+ if result_type is not None:
645
+ raise NotImplementedError("result_type is not supported")
646
+ if by_row not in (False, "compat"):
647
+ raise NotImplementedError("by_row must be False or 'compat'")
648
+ if engine != "python":
649
+ raise NotImplementedError("Only engine='python' is supported")
650
+ if engine_kwargs is not None:
651
+ raise NotImplementedError("engine_kwargs are not supported")
652
+ if isinstance(func, str):
653
+ raise NotImplementedError("String-based apply is not supported")
654
+ if not callable(func):
655
+ raise TypeError("Function must be a callable")
656
+
657
+ # Build assign column definitions: apply func to each column Series
658
+ col_definitions = {}
659
+ for c in self.columns():
660
+ col_name = c.get_name()
661
+ series = self[col_name]
662
+
663
+ # Compute row callable via func on the Series
664
+ def _row_callable(
665
+ _row: PandasApiTdsRow,
666
+ _s: Series = series, # type: ignore
667
+ _a: PyLegendTuple[PyLegendPrimitiveOrPythonPrimitive, ...] = args,
668
+ _k: PyLegendPrimitiveOrPythonPrimitive = kwargs # type: ignore
669
+ ) -> PyLegendPrimitiveOrPythonPrimitive:
670
+ return func(_s, *_a, **_k) # type: ignore
671
+
672
+ col_definitions[col_name] = _row_callable
673
+
674
+ return PandasApiAppliedFunctionTdsFrame(
675
+ AssignFunction(self, col_definitions=col_definitions) # type: ignore
676
+ )
677
+
678
+ def head(self, n: int = 5) -> "PandasApiTdsFrame":
679
+ """
680
+ Return the first `n` rows by calling truncate on rows.
681
+ Negative `n` is not supported.
682
+ """
683
+ if not isinstance(n, int):
684
+ raise TypeError(f"n must be an int, got {type(n)}")
685
+ if n < 0:
686
+ raise NotImplementedError("Negative n is not supported yet in Pandas API head")
687
+
688
+ return self.truncate(before=None, after=max(n - 1, -1), axis=0, copy=True)
689
+
690
+ @property
691
+ def shape(self) -> PyLegendTuple[int, int]:
692
+ """
693
+ Return a tuple representing the dimensionality of the TdsFrame
694
+ as (number of rows, number of columns).
695
+ """
696
+
697
+ col_name = self.columns()[0].get_name()
698
+ newframe = self.aggregate(func={col_name: "count"}, axis=0)
699
+
700
+ df = newframe.execute_frame_to_pandas_df()
701
+
702
+ total_rows = df.iloc[0, 0]
703
+ total_cols = len(self.columns())
704
+
705
+ return (total_rows, total_cols) # type: ignore
706
+
707
+ def dropna(
708
+ self,
709
+ axis: PyLegendUnion[int, str] = 0,
710
+ how: str = "any",
711
+ thresh: PyLegendOptional[int] = None,
712
+ subset: PyLegendOptional[PyLegendUnion[str, PyLegendSequence[str]]] = None,
713
+ inplace: bool = False,
714
+ ignore_index: bool = False
715
+ ) -> "PandasApiTdsFrame":
716
+ """
717
+ Remove missing values.
718
+
719
+ Parameters
720
+ ----------
721
+ axis : {0 or 'index'}, default 0
722
+ Determine if rows or columns which contain missing values are removed.
723
+ * 0, or 'index' : Drop rows which contain missing values.
724
+ Currently, only `axis=0` is supported.
725
+ how : {'any', 'all'}, default 'any'
726
+ Determine if row is removed from TdsFrame, when we have at least one NA or all NA.
727
+ * 'any' : If any NA values are present, drop that row.
728
+ * 'all' : If all values are NA, drop that row.
729
+ thresh : int, optional
730
+ Not implemented yet.
731
+ subset : list-like, optional
732
+ Labels along other axis to consider, e.g. if you are dropping rows
733
+ these would be a list of columns to include.
734
+ inplace : bool, default False
735
+ Not implemented yet.
736
+ ignore_index : bool, default False
737
+ Not implemented yet.
738
+
739
+ Returns
740
+ -------
741
+ PandasApiTdsFrame
742
+ TdsFrame with NA entries dropped.
743
+ """
744
+ from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
745
+ PandasApiAppliedFunctionTdsFrame
746
+ )
747
+ from pylegend.core.tds.pandas_api.frames.functions.dropna import PandasApiDropnaFunction
748
+ return PandasApiAppliedFunctionTdsFrame(
749
+ PandasApiDropnaFunction(
750
+ base_frame=self,
751
+ axis=axis,
752
+ how=how,
753
+ thresh=thresh,
754
+ subset=subset,
755
+ inplace=inplace,
756
+ ignore_index=ignore_index
757
+ )
758
+ )
759
+
760
+ def fillna(
761
+ self,
762
+ value: PyLegendUnion[
763
+ int, float, str, bool, date, datetime,
764
+ PyLegendDict[str, PyLegendUnion[int, float, str, bool, date, datetime]]
765
+ ] = None, # type: ignore
766
+ axis: PyLegendOptional[PyLegendUnion[int, str]] = 0,
767
+ inplace: bool = False,
768
+ limit: PyLegendOptional[int] = None
769
+ ) -> "PandasApiTdsFrame":
770
+ """
771
+ Fill missing values.
772
+
773
+ Parameters
774
+ ----------
775
+ base_frame : PandasApiBaseTdsFrame
776
+ The base frame to apply fillna on.
777
+ value : scalar, dict, default None
778
+ Value to use to fill holes (e.g. 0), alternately a dict of values specifying
779
+ which value to use for each column of TdsFrame.
780
+ axis : {0 or 'index'}, default 0
781
+ Axis along which to fill missing values.
782
+ * 0, or 'index' : Fill missing values for each column.
783
+ Currently, only `axis=0` is supported.
784
+ inplace : bool, default False
785
+ Not implemented yet.
786
+ limit : int, optional
787
+ Not implemented yet.
788
+
789
+ Returns
790
+ -------
791
+ PandasApiTdsFrame
792
+ TdsFrame with NA entries filled.
793
+ """
794
+ from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
795
+ PandasApiAppliedFunctionTdsFrame
796
+ )
797
+ from pylegend.core.tds.pandas_api.frames.functions.fillna import PandasApiFillnaFunction
798
+ return PandasApiAppliedFunctionTdsFrame(
799
+ PandasApiFillnaFunction(
800
+ base_frame=self,
801
+ value=value,
802
+ axis=axis,
803
+ inplace=inplace,
804
+ limit=limit
805
+ )
806
+ )
807
+
550
808
  @abstractmethod
551
- def to_sql_query_object(self, config: FrameToSqlConfig) -> QuerySpecification:
809
+ def get_super_type(self) -> PyLegendType[PyLegendTdsFrame]:
552
810
  pass # pragma: no cover
553
811
 
554
- @abstractmethod
812
+ def to_sql_query_object(self, config: FrameToSqlConfig) -> QuerySpecification:
813
+ if self._transformed_frame is None:
814
+ return self.get_super_type().to_sql_query_object(self, config) # type: ignore
815
+ else:
816
+ return self._transformed_frame.to_sql_query_object(config)
817
+
555
818
  def to_pure(self, config: FrameToPureConfig) -> str:
556
- pass # pragma: no cover
819
+ if self._transformed_frame is None:
820
+ return self.get_super_type().to_pure(self, config) # type: ignore
821
+ else:
822
+ return self._transformed_frame.to_pure(config)
557
823
 
558
824
  def to_pure_query(self, config: FrameToPureConfig = FrameToPureConfig()) -> str:
559
825
  return self.to_pure(config)
@@ -16,6 +16,13 @@ from abc import abstractmethod
16
16
  from datetime import date, datetime
17
17
  from typing import TYPE_CHECKING
18
18
 
19
+ from typing_extensions import Concatenate
20
+
21
+ try:
22
+ from typing import ParamSpec
23
+ except Exception:
24
+ from typing_extensions import ParamSpec # type: ignore
25
+
19
26
  from pylegend._typing import (
20
27
  PyLegendCallable,
21
28
  PyLegendSequence,
@@ -45,6 +52,8 @@ __all__: PyLegendSequence[str] = [
45
52
  "PandasApiTdsFrame"
46
53
  ]
47
54
 
55
+ P = ParamSpec("P")
56
+
48
57
 
49
58
  class PandasApiTdsFrame(PyLegendTdsFrame):
50
59
 
@@ -55,6 +64,14 @@ class PandasApiTdsFrame(PyLegendTdsFrame):
55
64
  ) -> PyLegendUnion["PandasApiTdsFrame", "Series"]:
56
65
  pass # pragma: no cover
57
66
 
67
+ @abstractmethod
68
+ def __setitem__(
69
+ self,
70
+ key: str,
71
+ value: PyLegendUnion["Series", PyLegendPrimitiveOrPythonPrimitive]
72
+ ) -> None:
73
+ pass # pragma: no cover
74
+
58
75
  @abstractmethod
59
76
  def assign(
60
77
  self,
@@ -107,7 +124,7 @@ class PandasApiTdsFrame(PyLegendTdsFrame):
107
124
  index: PyLegendOptional[PyLegendUnion[str, PyLegendSequence[str], PyLegendSet[str]]] = None,
108
125
  columns: PyLegendOptional[PyLegendUnion[str, PyLegendSequence[str], PyLegendSet[str]]] = None,
109
126
  level: PyLegendOptional[PyLegendUnion[int, PyLegendInteger, str]] = None,
110
- inplace: PyLegendUnion[bool, PyLegendBoolean] = True,
127
+ inplace: PyLegendUnion[bool, PyLegendBoolean] = False,
111
128
  errors: str = "raise",
112
129
  ) -> "PandasApiTdsFrame":
113
130
  pass # pragma: no cover
@@ -183,14 +200,14 @@ class PandasApiTdsFrame(PyLegendTdsFrame):
183
200
 
184
201
  @abstractmethod
185
202
  def groupby(
186
- self,
187
- by: PyLegendUnion[str, PyLegendList[str]],
188
- level: PyLegendOptional[PyLegendUnion[str, int, PyLegendList[str]]] = None,
189
- as_index: bool = False,
190
- sort: bool = True,
191
- group_keys: bool = False,
192
- observed: bool = False,
193
- dropna: bool = False,
203
+ self,
204
+ by: PyLegendUnion[str, PyLegendList[str]],
205
+ level: PyLegendOptional[PyLegendUnion[str, int, PyLegendList[str]]] = None,
206
+ as_index: bool = False,
207
+ sort: bool = True,
208
+ group_keys: bool = False,
209
+ observed: bool = False,
210
+ dropna: bool = False,
194
211
  ) -> "PandasApiGroupbyTdsFrame":
195
212
  pass # pragma: no cover
196
213
 
@@ -265,3 +282,55 @@ class PandasApiTdsFrame(PyLegendTdsFrame):
265
282
  **kwargs: PyLegendPrimitiveOrPythonPrimitive
266
283
  ) -> "PandasApiTdsFrame":
267
284
  pass # pragma: no cover
285
+
286
+ @abstractmethod
287
+ def apply(
288
+ self,
289
+ func: PyLegendUnion[
290
+ PyLegendCallable[Concatenate["Series", P], PyLegendPrimitiveOrPythonPrimitive],
291
+ str
292
+ ],
293
+ axis: PyLegendUnion[int, str] = 0,
294
+ raw: bool = False,
295
+ result_type: PyLegendOptional[str] = None,
296
+ args: PyLegendTuple[PyLegendPrimitiveOrPythonPrimitive, ...] = (),
297
+ by_row: PyLegendUnion[bool, str] = "compat",
298
+ engine: str = "python",
299
+ engine_kwargs: PyLegendOptional[PyLegendDict[str, PyLegendPrimitiveOrPythonPrimitive]] = None,
300
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
301
+ ) -> "PandasApiTdsFrame":
302
+ pass # pragma: no cover
303
+
304
+ @abstractmethod
305
+ def head(self, n: int = 5) -> "PandasApiTdsFrame":
306
+ pass # pragma: no cover
307
+
308
+ @property
309
+ @abstractmethod
310
+ def shape(self) -> PyLegendTuple[int, int]:
311
+ pass # pragma: no cover
312
+
313
+ @abstractmethod
314
+ def dropna(
315
+ self,
316
+ axis: PyLegendUnion[int, str] = 0,
317
+ how: str = "any",
318
+ thresh: PyLegendOptional[int] = None,
319
+ subset: PyLegendOptional[PyLegendUnion[str, PyLegendSequence[str]]] = None,
320
+ inplace: bool = False,
321
+ ignore_index: bool = False
322
+ ) -> "PandasApiTdsFrame":
323
+ pass # pragma: no cover
324
+
325
+ @abstractmethod
326
+ def fillna(
327
+ self,
328
+ value: PyLegendUnion[
329
+ int, float, str, bool, date, datetime,
330
+ PyLegendDict[str, PyLegendUnion[int, float, str, bool, date, datetime]]
331
+ ] = None, # type: ignore
332
+ axis: PyLegendOptional[PyLegendUnion[int, str]] = 0,
333
+ inplace: bool = False,
334
+ limit: PyLegendOptional[int] = None
335
+ ) -> "PandasApiTdsFrame":
336
+ pass # pragma: no cover
@@ -13,20 +13,21 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from pylegend._typing import (
16
- PyLegendSequence
16
+ PyLegendSequence,
17
+ PyLegendType
17
18
  )
18
- from pylegend.core.tds.pandas_api.frames.pandas_api_input_tds_frame import PandasApiExecutableInputTdsFrame
19
19
  from pylegend.core.project_cooridnates import ProjectCoordinates
20
20
  from pylegend.core.request.legend_client import LegendClient
21
+ from pylegend.core.tds.pandas_api.frames.pandas_api_input_tds_frame import PandasApiExecutableInputTdsFrame
22
+ from pylegend.core.tds.tds_frame import PyLegendTdsFrame
21
23
  from pylegend.extensions.tds.abstract.legend_function_input_frame import LegendFunctionInputFrameAbstract
22
24
 
23
-
24
25
  __all__: PyLegendSequence[str] = [
25
26
  "PandasApiLegendFunctionInputFrame"
26
27
  ]
27
28
 
28
29
 
29
- class PandasApiLegendFunctionInputFrame(LegendFunctionInputFrameAbstract, PandasApiExecutableInputTdsFrame):
30
+ class PandasApiLegendFunctionInputFrame(PandasApiExecutableInputTdsFrame, LegendFunctionInputFrameAbstract):
30
31
 
31
32
  def __init__(
32
33
  self,
@@ -35,6 +36,7 @@ class PandasApiLegendFunctionInputFrame(LegendFunctionInputFrameAbstract, Pandas
35
36
  legend_client: LegendClient,
36
37
  ) -> None:
37
38
  LegendFunctionInputFrameAbstract.__init__(self, path=path, project_coordinates=project_coordinates)
39
+ self._transformed_frame = None
38
40
  PandasApiExecutableInputTdsFrame.__init__(
39
41
  self,
40
42
  legend_client=legend_client,
@@ -44,3 +46,6 @@ class PandasApiLegendFunctionInputFrame(LegendFunctionInputFrameAbstract, Pandas
44
46
 
45
47
  def __str__(self) -> str:
46
48
  return f"PandasApiLegendFunctionInputFrame({'.'.join(self.get_path())})"
49
+
50
+ def get_super_type(self) -> PyLegendType[PyLegendTdsFrame]:
51
+ return LegendFunctionInputFrameAbstract
@@ -13,20 +13,23 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from pylegend._typing import (
16
- PyLegendSequence
16
+ PyLegendSequence,
17
+ PyLegendType
17
18
  )
18
- from pylegend.core.tds.pandas_api.frames.pandas_api_input_tds_frame import PandasApiExecutableInputTdsFrame
19
19
  from pylegend.core.project_cooridnates import ProjectCoordinates
20
20
  from pylegend.core.request.legend_client import LegendClient
21
+ from pylegend.core.tds.pandas_api.frames.pandas_api_input_tds_frame import PandasApiExecutableInputTdsFrame
22
+ from pylegend.core.tds.tds_frame import (
23
+ PyLegendTdsFrame
24
+ )
21
25
  from pylegend.extensions.tds.abstract.legend_service_input_frame import LegendServiceInputFrameAbstract
22
26
 
23
-
24
27
  __all__: PyLegendSequence[str] = [
25
28
  "PandasApiLegendServiceInputFrame"
26
29
  ]
27
30
 
28
31
 
29
- class PandasApiLegendServiceInputFrame(LegendServiceInputFrameAbstract, PandasApiExecutableInputTdsFrame):
32
+ class PandasApiLegendServiceInputFrame(PandasApiExecutableInputTdsFrame, LegendServiceInputFrameAbstract):
30
33
 
31
34
  def __init__(
32
35
  self,
@@ -35,6 +38,7 @@ class PandasApiLegendServiceInputFrame(LegendServiceInputFrameAbstract, PandasAp
35
38
  legend_client: LegendClient,
36
39
  ) -> None:
37
40
  LegendServiceInputFrameAbstract.__init__(self, pattern=pattern, project_coordinates=project_coordinates)
41
+ self._transformed_frame = None
38
42
  PandasApiExecutableInputTdsFrame.__init__(
39
43
  self,
40
44
  legend_client=legend_client,
@@ -43,4 +47,7 @@ class PandasApiLegendServiceInputFrame(LegendServiceInputFrameAbstract, PandasAp
43
47
  LegendServiceInputFrameAbstract.set_initialized(self, True)
44
48
 
45
49
  def __str__(self) -> str:
46
- return f"PandasApiLegendServiceInputFrame({'.'.join(self.get_pattern())})"
50
+ return f"PandasApiLegendServiceInputFrame({'.'.join(self.get_pattern())})" # pragma: no cover
51
+
52
+ def get_super_type(self) -> PyLegendType[PyLegendTdsFrame]:
53
+ return LegendServiceInputFrameAbstract