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.
- pylegend/core/database/sql_to_string/db_extension.py +68 -6
- pylegend/core/language/legendql_api/legendql_api_custom_expressions.py +190 -5
- pylegend/core/language/pandas_api/pandas_api_series.py +3 -0
- pylegend/core/sql/metamodel.py +4 -1
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_distinct_function.py +53 -7
- pylegend/core/tds/legendql_api/frames/legendql_api_base_tds_frame.py +146 -4
- pylegend/core/tds/legendql_api/frames/legendql_api_tds_frame.py +33 -2
- pylegend/core/tds/pandas_api/frames/functions/assign_function.py +65 -23
- pylegend/core/tds/pandas_api/frames/functions/drop.py +3 -3
- pylegend/core/tds/pandas_api/frames/functions/dropna.py +167 -0
- pylegend/core/tds/pandas_api/frames/functions/fillna.py +162 -0
- pylegend/core/tds/pandas_api/frames/functions/filter.py +10 -5
- pylegend/core/tds/pandas_api/frames/functions/truncate_function.py +151 -120
- pylegend/core/tds/pandas_api/frames/pandas_api_applied_function_tds_frame.py +7 -3
- pylegend/core/tds/pandas_api/frames/pandas_api_base_tds_frame.py +300 -34
- pylegend/core/tds/pandas_api/frames/pandas_api_tds_frame.py +78 -9
- pylegend/extensions/tds/pandas_api/frames/pandas_api_legend_function_input_frame.py +9 -4
- pylegend/extensions/tds/pandas_api/frames/pandas_api_legend_service_input_frame.py +12 -5
- pylegend/extensions/tds/pandas_api/frames/pandas_api_table_spec_input_frame.py +12 -4
- {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/METADATA +1 -1
- {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/RECORD +25 -23
- {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/WHEEL +0 -0
- {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/licenses/LICENSE +0 -0
- {pylegend-0.11.0.dist-info → pylegend-0.12.0.dist-info}/licenses/LICENSE.spdx +0 -0
- {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
|
|
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
|
-
|
|
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
|
|
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] =
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
|
809
|
+
def get_super_type(self) -> PyLegendType[PyLegendTdsFrame]:
|
|
552
810
|
pass # pragma: no cover
|
|
553
811
|
|
|
554
|
-
|
|
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
|
-
|
|
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] =
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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(
|
|
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(
|
|
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
|