vtlengine 1.4.0rc2__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.
- vtlengine/API/_InternalApi.py +791 -0
- vtlengine/API/__init__.py +612 -0
- vtlengine/API/data/schema/external_routines_schema.json +34 -0
- vtlengine/API/data/schema/json_schema_2.1.json +116 -0
- vtlengine/API/data/schema/value_domain_schema.json +97 -0
- vtlengine/AST/ASTComment.py +57 -0
- vtlengine/AST/ASTConstructor.py +598 -0
- vtlengine/AST/ASTConstructorModules/Expr.py +1928 -0
- vtlengine/AST/ASTConstructorModules/ExprComponents.py +995 -0
- vtlengine/AST/ASTConstructorModules/Terminals.py +790 -0
- vtlengine/AST/ASTConstructorModules/__init__.py +50 -0
- vtlengine/AST/ASTDataExchange.py +10 -0
- vtlengine/AST/ASTEncoders.py +32 -0
- vtlengine/AST/ASTString.py +675 -0
- vtlengine/AST/ASTTemplate.py +558 -0
- vtlengine/AST/ASTVisitor.py +25 -0
- vtlengine/AST/DAG/__init__.py +479 -0
- vtlengine/AST/DAG/_words.py +10 -0
- vtlengine/AST/Grammar/Vtl.g4 +705 -0
- vtlengine/AST/Grammar/VtlTokens.g4 +409 -0
- vtlengine/AST/Grammar/__init__.py +0 -0
- vtlengine/AST/Grammar/lexer.py +2139 -0
- vtlengine/AST/Grammar/parser.py +16597 -0
- vtlengine/AST/Grammar/tokens.py +169 -0
- vtlengine/AST/VtlVisitor.py +824 -0
- vtlengine/AST/__init__.py +674 -0
- vtlengine/DataTypes/TimeHandling.py +562 -0
- vtlengine/DataTypes/__init__.py +863 -0
- vtlengine/DataTypes/_time_checking.py +135 -0
- vtlengine/Exceptions/__exception_file_generator.py +96 -0
- vtlengine/Exceptions/__init__.py +159 -0
- vtlengine/Exceptions/messages.py +1004 -0
- vtlengine/Interpreter/__init__.py +2048 -0
- vtlengine/Model/__init__.py +501 -0
- vtlengine/Operators/Aggregation.py +357 -0
- vtlengine/Operators/Analytic.py +455 -0
- vtlengine/Operators/Assignment.py +23 -0
- vtlengine/Operators/Boolean.py +106 -0
- vtlengine/Operators/CastOperator.py +451 -0
- vtlengine/Operators/Clause.py +366 -0
- vtlengine/Operators/Comparison.py +488 -0
- vtlengine/Operators/Conditional.py +495 -0
- vtlengine/Operators/General.py +191 -0
- vtlengine/Operators/HROperators.py +254 -0
- vtlengine/Operators/Join.py +447 -0
- vtlengine/Operators/Numeric.py +422 -0
- vtlengine/Operators/RoleSetter.py +77 -0
- vtlengine/Operators/Set.py +176 -0
- vtlengine/Operators/String.py +578 -0
- vtlengine/Operators/Time.py +1144 -0
- vtlengine/Operators/Validation.py +275 -0
- vtlengine/Operators/__init__.py +900 -0
- vtlengine/Utils/__Virtual_Assets.py +34 -0
- vtlengine/Utils/__init__.py +479 -0
- vtlengine/__extras_check.py +17 -0
- vtlengine/__init__.py +27 -0
- vtlengine/files/__init__.py +0 -0
- vtlengine/files/output/__init__.py +35 -0
- vtlengine/files/output/_time_period_representation.py +55 -0
- vtlengine/files/parser/__init__.py +240 -0
- vtlengine/files/parser/_rfc_dialect.py +22 -0
- vtlengine/py.typed +0 -0
- vtlengine-1.4.0rc2.dist-info/METADATA +89 -0
- vtlengine-1.4.0rc2.dist-info/RECORD +66 -0
- vtlengine-1.4.0rc2.dist-info/WHEEL +4 -0
- vtlengine-1.4.0rc2.dist-info/licenses/LICENSE.md +661 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
from copy import copy
|
|
2
|
+
from typing import Any, Optional, Type, Union
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
import vtlengine.Operators as Operator
|
|
7
|
+
from vtlengine.AST.Grammar.tokens import CAST
|
|
8
|
+
from vtlengine.DataTypes import (
|
|
9
|
+
COMP_NAME_MAPPING,
|
|
10
|
+
EXPLICIT_WITH_MASK_TYPE_PROMOTION_MAPPING,
|
|
11
|
+
EXPLICIT_WITHOUT_MASK_TYPE_PROMOTION_MAPPING,
|
|
12
|
+
IMPLICIT_TYPE_PROMOTION_MAPPING,
|
|
13
|
+
SCALAR_TYPES_CLASS_REVERSE,
|
|
14
|
+
Date,
|
|
15
|
+
Duration,
|
|
16
|
+
Number,
|
|
17
|
+
ScalarType,
|
|
18
|
+
String,
|
|
19
|
+
TimeInterval,
|
|
20
|
+
TimePeriod,
|
|
21
|
+
)
|
|
22
|
+
from vtlengine.DataTypes.TimeHandling import str_period_to_date
|
|
23
|
+
from vtlengine.Exceptions import SemanticError
|
|
24
|
+
from vtlengine.Model import Component, DataComponent, Dataset, Role, Scalar
|
|
25
|
+
from vtlengine.Utils.__Virtual_Assets import VirtualCounter
|
|
26
|
+
|
|
27
|
+
duration_mapping = {"A": 6, "S": 5, "Q": 4, "M": 3, "W": 2, "D": 1}
|
|
28
|
+
|
|
29
|
+
ALL_MODEL_DATA_TYPES = Union[Dataset, Scalar, DataComponent]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Cast(Operator.Unary):
|
|
33
|
+
op = CAST
|
|
34
|
+
|
|
35
|
+
# CASTS VALUES
|
|
36
|
+
# Converts the value from one type to another in a way that is according to the mask
|
|
37
|
+
@classmethod
|
|
38
|
+
def cast_string_to_number(cls, value: Any, mask: str) -> Any:
|
|
39
|
+
"""
|
|
40
|
+
This method casts a string to a number, according to the mask.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def cast_string_to_date(cls, value: Any, mask: str) -> Any:
|
|
48
|
+
"""
|
|
49
|
+
This method casts a string to a number, according to the mask.
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def cast_string_to_duration(cls, value: Any, mask: str) -> Any:
|
|
57
|
+
"""
|
|
58
|
+
This method casts a string to a duration, according to the mask.
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def cast_string_to_time_period(cls, value: Any, mask: str) -> Any:
|
|
66
|
+
"""
|
|
67
|
+
This method casts a string to a time period, according to the mask.
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def cast_string_to_time(cls, value: Any, mask: str) -> Any:
|
|
75
|
+
"""
|
|
76
|
+
This method casts a string to a time, according to the mask.
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
raise NotImplementedError("How this cast should be implemented is not yet defined.")
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# @classmethod
|
|
84
|
+
# def cast_date_to_string(cls, value: Any, mask: str) -> Any:
|
|
85
|
+
# """ """
|
|
86
|
+
# return NotImplementedError("How this cast should be implemented is not yet defined.")
|
|
87
|
+
#
|
|
88
|
+
# @classmethod
|
|
89
|
+
# def cast_duration_to_string(cls, value: Any, mask: str) -> Any:
|
|
90
|
+
# """ """
|
|
91
|
+
# return NotImplementedError("How this cast should be implemented is not yet defined.")
|
|
92
|
+
#
|
|
93
|
+
# @classmethod
|
|
94
|
+
# def cast_time_to_string(cls, value: Any, mask: str) -> Any:
|
|
95
|
+
# """ """
|
|
96
|
+
# return NotImplementedError("How this cast should be implemented is not yet defined.")
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def cast_time_period_to_date(cls, value: Any, mask_value: str) -> Any:
|
|
100
|
+
""" """
|
|
101
|
+
|
|
102
|
+
start = mask_value == "START"
|
|
103
|
+
return str_period_to_date(value, start)
|
|
104
|
+
|
|
105
|
+
invalid_mask_message = "At op {op}: Invalid mask to cast from type {type_1} to {type_2}."
|
|
106
|
+
|
|
107
|
+
@classmethod
|
|
108
|
+
def check_mask_value(
|
|
109
|
+
cls, from_type: Type[ScalarType], to_type: Type[ScalarType], mask_value: str
|
|
110
|
+
) -> None:
|
|
111
|
+
"""
|
|
112
|
+
This method checks if the mask value is valid for the cast operation.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
if from_type == TimeInterval and to_type == String:
|
|
116
|
+
return cls.check_mask_value_from_time_to_string(mask_value)
|
|
117
|
+
# from = Date
|
|
118
|
+
if from_type == Date and to_type == String:
|
|
119
|
+
return cls.check_mask_value_from_date_to_string(mask_value)
|
|
120
|
+
# from = Time_Period
|
|
121
|
+
if from_type == TimePeriod and to_type == Date:
|
|
122
|
+
return cls.check_mask_value_from_time_period_to_date(mask_value)
|
|
123
|
+
# from = String
|
|
124
|
+
if from_type == String and to_type == Number:
|
|
125
|
+
return cls.check_mask_value_from_string_to_number(mask_value)
|
|
126
|
+
if from_type == String and to_type == TimeInterval:
|
|
127
|
+
return cls.check_mask_value_from_string_to_time(mask_value)
|
|
128
|
+
if from_type == String and to_type == Date:
|
|
129
|
+
return cls.check_mask_value_from_string_to_date(mask_value)
|
|
130
|
+
if from_type == String and to_type == TimePeriod:
|
|
131
|
+
return cls.check_mask_value_from_string_to_time_period(mask_value)
|
|
132
|
+
if from_type == String and to_type == Duration:
|
|
133
|
+
return cls.check_mask_value_from_string_to_duration(mask_value)
|
|
134
|
+
# from = Duration
|
|
135
|
+
if from_type == Duration and to_type == String:
|
|
136
|
+
return cls.check_mask_value_from_duration_to_string(mask_value)
|
|
137
|
+
raise SemanticError(
|
|
138
|
+
"1-1-5-5",
|
|
139
|
+
op=cls.op,
|
|
140
|
+
type_1=SCALAR_TYPES_CLASS_REVERSE[from_type],
|
|
141
|
+
type_2=SCALAR_TYPES_CLASS_REVERSE[to_type],
|
|
142
|
+
mask_value=mask_value,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
@classmethod
|
|
146
|
+
def check_mask_value_from_time_period_to_date(cls, mask_value: str) -> None:
|
|
147
|
+
if mask_value not in ["START", "END"]:
|
|
148
|
+
raise SemanticError("1-1-5-4", op=cls.op, type_1="Time_Period", type_2="Date")
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def check_mask_value_from_time_to_string(cls, *args: Any) -> None:
|
|
152
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def check_mask_value_from_date_to_string(cls, *args: Any) -> None:
|
|
156
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def check_mask_value_from_string_to_number(cls, *args: Any) -> None:
|
|
160
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
161
|
+
|
|
162
|
+
@classmethod
|
|
163
|
+
def check_mask_value_from_string_to_time(cls, *args: Any) -> None:
|
|
164
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
165
|
+
|
|
166
|
+
@classmethod
|
|
167
|
+
def check_mask_value_from_string_to_date(cls, *args: Any) -> None:
|
|
168
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
169
|
+
|
|
170
|
+
@classmethod
|
|
171
|
+
def check_mask_value_from_string_to_time_period(cls, *args: Any) -> None:
|
|
172
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def check_mask_value_from_string_to_duration(cls, *args: Any) -> None:
|
|
176
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
177
|
+
|
|
178
|
+
@classmethod
|
|
179
|
+
def check_mask_value_from_duration_to_string(cls, *args: Any) -> None:
|
|
180
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
181
|
+
|
|
182
|
+
@classmethod
|
|
183
|
+
def check_cast(
|
|
184
|
+
cls,
|
|
185
|
+
from_type: Type[ScalarType],
|
|
186
|
+
to_type: Type[ScalarType],
|
|
187
|
+
mask_value: Optional[str],
|
|
188
|
+
) -> None:
|
|
189
|
+
if mask_value is not None:
|
|
190
|
+
cls.check_with_mask(from_type, to_type, mask_value)
|
|
191
|
+
else:
|
|
192
|
+
cls.check_without_mask(from_type, to_type)
|
|
193
|
+
|
|
194
|
+
@classmethod
|
|
195
|
+
def check_with_mask(
|
|
196
|
+
cls, from_type: Type[ScalarType], to_type: Type[ScalarType], mask_value: str
|
|
197
|
+
) -> None:
|
|
198
|
+
explicit_promotion = EXPLICIT_WITH_MASK_TYPE_PROMOTION_MAPPING[from_type]
|
|
199
|
+
if to_type.is_included(explicit_promotion):
|
|
200
|
+
return cls.check_mask_value(from_type, to_type, mask_value)
|
|
201
|
+
|
|
202
|
+
raise SemanticError(
|
|
203
|
+
"1-1-5-5",
|
|
204
|
+
op=cls.op,
|
|
205
|
+
type_1=SCALAR_TYPES_CLASS_REVERSE[from_type],
|
|
206
|
+
type_2=SCALAR_TYPES_CLASS_REVERSE[to_type],
|
|
207
|
+
mask_value=mask_value,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
@classmethod
|
|
211
|
+
def check_without_mask(cls, from_type: Type[ScalarType], to_type: Type[ScalarType]) -> None:
|
|
212
|
+
explicit_promotion = EXPLICIT_WITHOUT_MASK_TYPE_PROMOTION_MAPPING[from_type]
|
|
213
|
+
implicit_promotion = IMPLICIT_TYPE_PROMOTION_MAPPING[from_type]
|
|
214
|
+
if not (to_type.is_included(explicit_promotion) or to_type.is_included(implicit_promotion)):
|
|
215
|
+
explicit_with_mask = EXPLICIT_WITH_MASK_TYPE_PROMOTION_MAPPING[from_type]
|
|
216
|
+
if to_type.is_included(explicit_with_mask):
|
|
217
|
+
raise SemanticError(
|
|
218
|
+
"1-1-5-3",
|
|
219
|
+
op=cls.op,
|
|
220
|
+
type_1=SCALAR_TYPES_CLASS_REVERSE[from_type],
|
|
221
|
+
type_2=SCALAR_TYPES_CLASS_REVERSE[to_type],
|
|
222
|
+
)
|
|
223
|
+
raise SemanticError(
|
|
224
|
+
"1-1-5-4",
|
|
225
|
+
op=cls.op,
|
|
226
|
+
type_1=SCALAR_TYPES_CLASS_REVERSE[from_type],
|
|
227
|
+
type_2=SCALAR_TYPES_CLASS_REVERSE[to_type],
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
@classmethod
|
|
231
|
+
def cast_component(
|
|
232
|
+
cls, data: Any, from_type: Type[ScalarType], to_type: Type[ScalarType]
|
|
233
|
+
) -> Any:
|
|
234
|
+
"""
|
|
235
|
+
Cast the component to the type to_type without mask
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
if to_type.is_included(IMPLICIT_TYPE_PROMOTION_MAPPING[from_type]):
|
|
239
|
+
result = data.map(lambda x: to_type.implicit_cast(x, from_type), na_action="ignore")
|
|
240
|
+
else:
|
|
241
|
+
result = data.map(lambda x: to_type.explicit_cast(x, from_type), na_action="ignore")
|
|
242
|
+
return result
|
|
243
|
+
|
|
244
|
+
@classmethod
|
|
245
|
+
def cast_mask_component(cls, data: Any, from_type: Any, to_type: Any, mask: str) -> Any:
|
|
246
|
+
result = data.map(lambda x: cls.cast_value(x, from_type, to_type, mask), na_action="ignore")
|
|
247
|
+
return result
|
|
248
|
+
|
|
249
|
+
@classmethod
|
|
250
|
+
def cast_value(
|
|
251
|
+
cls,
|
|
252
|
+
value: Any,
|
|
253
|
+
provided_type: Type[ScalarType],
|
|
254
|
+
to_type: Type[ScalarType],
|
|
255
|
+
mask_value: str,
|
|
256
|
+
) -> Any:
|
|
257
|
+
if provided_type == String and to_type == Number:
|
|
258
|
+
return cls.cast_string_to_number(value, mask_value)
|
|
259
|
+
if provided_type == String and to_type == Date:
|
|
260
|
+
return cls.cast_string_to_date(value, mask_value)
|
|
261
|
+
if provided_type == String and to_type == Duration:
|
|
262
|
+
return cls.cast_string_to_duration(value, mask_value)
|
|
263
|
+
if provided_type == String and to_type == TimePeriod:
|
|
264
|
+
return cls.cast_string_to_time_period(value, mask_value)
|
|
265
|
+
if provided_type == String and to_type == TimeInterval:
|
|
266
|
+
return cls.cast_string_to_time(value, mask_value)
|
|
267
|
+
# if provided_type == Date and to_type == String:
|
|
268
|
+
# return cls.cast_date_to_string(value, mask_value)
|
|
269
|
+
# if provided_type == Duration and to_type == String:
|
|
270
|
+
# return cls.cast_duration_to_string(value, mask_value)
|
|
271
|
+
# if provided_type == TimeInterval and to_type == String:
|
|
272
|
+
# return cls.cast_time_to_string(value, mask_value)
|
|
273
|
+
if provided_type == TimePeriod and to_type == Date:
|
|
274
|
+
return cls.cast_time_period_to_date(value, mask_value)
|
|
275
|
+
|
|
276
|
+
raise SemanticError(
|
|
277
|
+
"2-1-5-1",
|
|
278
|
+
op=cls.op,
|
|
279
|
+
value=value,
|
|
280
|
+
type_1=SCALAR_TYPES_CLASS_REVERSE[provided_type],
|
|
281
|
+
type_2=SCALAR_TYPES_CLASS_REVERSE[to_type],
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
@classmethod
|
|
285
|
+
def validate( # type: ignore[override]
|
|
286
|
+
cls,
|
|
287
|
+
operand: ALL_MODEL_DATA_TYPES,
|
|
288
|
+
scalarType: Type[ScalarType],
|
|
289
|
+
mask: Optional[str] = None,
|
|
290
|
+
) -> Any:
|
|
291
|
+
if mask is not None and not isinstance(mask, str):
|
|
292
|
+
raise Exception(f"{cls.op} mask must be a string")
|
|
293
|
+
|
|
294
|
+
if isinstance(operand, Dataset):
|
|
295
|
+
return cls.dataset_validation(operand, scalarType, mask)
|
|
296
|
+
elif isinstance(operand, DataComponent):
|
|
297
|
+
return cls.component_validation(operand, scalarType, mask)
|
|
298
|
+
elif isinstance(operand, Scalar):
|
|
299
|
+
return cls.scalar_validation(operand, scalarType, mask)
|
|
300
|
+
|
|
301
|
+
@classmethod
|
|
302
|
+
def dataset_validation( # type: ignore[override]
|
|
303
|
+
cls,
|
|
304
|
+
operand: Dataset,
|
|
305
|
+
to_type: Type[ScalarType],
|
|
306
|
+
mask: Optional[str] = None,
|
|
307
|
+
) -> Dataset:
|
|
308
|
+
"""
|
|
309
|
+
This method validates the operation when the operand is a Dataset.
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
# monomeasure
|
|
313
|
+
if len(operand.get_measures()) != 1:
|
|
314
|
+
raise Exception(f"{cls.op} can only be applied to a Dataset with one measure")
|
|
315
|
+
measure = operand.get_measures()[0]
|
|
316
|
+
from_type = measure.data_type
|
|
317
|
+
|
|
318
|
+
cls.check_cast(from_type, to_type, mask)
|
|
319
|
+
result_components = {
|
|
320
|
+
comp_name: copy(comp)
|
|
321
|
+
for comp_name, comp in operand.components.items()
|
|
322
|
+
if comp.role != Role.MEASURE
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if not to_type.is_included(IMPLICIT_TYPE_PROMOTION_MAPPING[from_type]):
|
|
326
|
+
measure_name = COMP_NAME_MAPPING[to_type]
|
|
327
|
+
else:
|
|
328
|
+
measure_name = measure.name
|
|
329
|
+
result_components[measure_name] = Component(
|
|
330
|
+
name=measure_name,
|
|
331
|
+
data_type=to_type,
|
|
332
|
+
role=Role.MEASURE,
|
|
333
|
+
nullable=measure.nullable,
|
|
334
|
+
)
|
|
335
|
+
dataset_name = VirtualCounter._new_ds_name()
|
|
336
|
+
return Dataset(name=dataset_name, components=result_components, data=None)
|
|
337
|
+
|
|
338
|
+
@classmethod
|
|
339
|
+
def component_validation( # type: ignore[override]
|
|
340
|
+
cls,
|
|
341
|
+
operand: DataComponent,
|
|
342
|
+
to_type: Type[ScalarType],
|
|
343
|
+
mask: Optional[str] = None,
|
|
344
|
+
) -> DataComponent:
|
|
345
|
+
"""
|
|
346
|
+
This method validates the operation when the operand is a DataComponent.
|
|
347
|
+
"""
|
|
348
|
+
|
|
349
|
+
from_type = operand.data_type
|
|
350
|
+
cls.check_cast(from_type, to_type, mask)
|
|
351
|
+
comp_name = VirtualCounter._new_dc_name()
|
|
352
|
+
return DataComponent(
|
|
353
|
+
name=comp_name,
|
|
354
|
+
data=None,
|
|
355
|
+
data_type=to_type,
|
|
356
|
+
role=operand.role,
|
|
357
|
+
nullable=operand.nullable,
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
@classmethod
|
|
361
|
+
def scalar_validation( # type: ignore[override]
|
|
362
|
+
cls,
|
|
363
|
+
operand: Scalar,
|
|
364
|
+
to_type: Type[ScalarType],
|
|
365
|
+
mask: Optional[str] = None,
|
|
366
|
+
) -> Scalar:
|
|
367
|
+
"""
|
|
368
|
+
This method validates the operation when the operand is a DataComponent.
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
from_type = operand.data_type
|
|
372
|
+
cls.check_cast(from_type, to_type, mask)
|
|
373
|
+
return Scalar(name=operand.name, data_type=to_type, value=None)
|
|
374
|
+
|
|
375
|
+
@classmethod
|
|
376
|
+
def evaluate( # type: ignore[override]
|
|
377
|
+
cls,
|
|
378
|
+
operand: ALL_MODEL_DATA_TYPES,
|
|
379
|
+
scalarType: Type[ScalarType],
|
|
380
|
+
mask: Optional[str] = None,
|
|
381
|
+
) -> Any:
|
|
382
|
+
if isinstance(operand, Dataset):
|
|
383
|
+
return cls.dataset_evaluation(operand, scalarType, mask)
|
|
384
|
+
if isinstance(operand, Scalar):
|
|
385
|
+
return cls.scalar_evaluation(operand, scalarType, mask)
|
|
386
|
+
if isinstance(operand, DataComponent):
|
|
387
|
+
return cls.component_evaluation(operand, scalarType, mask)
|
|
388
|
+
|
|
389
|
+
@classmethod
|
|
390
|
+
def dataset_evaluation( # type: ignore[override]
|
|
391
|
+
cls,
|
|
392
|
+
operand: Dataset,
|
|
393
|
+
to_type: Type[ScalarType],
|
|
394
|
+
mask: Optional[str] = None,
|
|
395
|
+
) -> Dataset:
|
|
396
|
+
from_type = operand.get_measures()[0].data_type
|
|
397
|
+
original_measure = operand.get_measures()[0]
|
|
398
|
+
result_dataset = cls.dataset_validation(operand, to_type, mask)
|
|
399
|
+
new_measure = result_dataset.get_measures()[0]
|
|
400
|
+
result_dataset.data = operand.data.copy() if operand.data is not None else pd.DataFrame()
|
|
401
|
+
|
|
402
|
+
if original_measure.name != new_measure.name:
|
|
403
|
+
result_dataset.data.rename(
|
|
404
|
+
columns={original_measure.name: new_measure.name}, inplace=True
|
|
405
|
+
)
|
|
406
|
+
measure_data = result_dataset.data[new_measure.name]
|
|
407
|
+
if mask:
|
|
408
|
+
result_dataset.data[new_measure.name] = cls.cast_mask_component(
|
|
409
|
+
measure_data, from_type, to_type, mask
|
|
410
|
+
)
|
|
411
|
+
else:
|
|
412
|
+
result_dataset.data[new_measure.name] = cls.cast_component(
|
|
413
|
+
measure_data, from_type, to_type
|
|
414
|
+
)
|
|
415
|
+
return result_dataset
|
|
416
|
+
|
|
417
|
+
@classmethod
|
|
418
|
+
def scalar_evaluation( # type: ignore[override]
|
|
419
|
+
cls,
|
|
420
|
+
operand: Scalar,
|
|
421
|
+
to_type: Type[ScalarType],
|
|
422
|
+
mask: Optional[str] = None,
|
|
423
|
+
) -> Scalar:
|
|
424
|
+
from_type = operand.data_type
|
|
425
|
+
result_scalar = cls.scalar_validation(operand, to_type, mask)
|
|
426
|
+
if pd.isna(operand.value):
|
|
427
|
+
return Scalar(name=result_scalar.name, data_type=to_type, value=None)
|
|
428
|
+
if mask:
|
|
429
|
+
casted_data = cls.cast_value(operand.value, operand.data_type, to_type, mask)
|
|
430
|
+
else:
|
|
431
|
+
if to_type.is_included(IMPLICIT_TYPE_PROMOTION_MAPPING[from_type]):
|
|
432
|
+
casted_data = to_type.implicit_cast(operand.value, from_type)
|
|
433
|
+
else:
|
|
434
|
+
casted_data = to_type.explicit_cast(operand.value, from_type)
|
|
435
|
+
return Scalar(name=result_scalar.name, data_type=to_type, value=casted_data)
|
|
436
|
+
|
|
437
|
+
@classmethod
|
|
438
|
+
def component_evaluation( # type: ignore[override]
|
|
439
|
+
cls,
|
|
440
|
+
operand: DataComponent,
|
|
441
|
+
to_type: Type[ScalarType],
|
|
442
|
+
mask: Optional[str] = None,
|
|
443
|
+
) -> DataComponent:
|
|
444
|
+
from_type = operand.data_type
|
|
445
|
+
result_component = cls.component_validation(operand, to_type, mask)
|
|
446
|
+
if mask:
|
|
447
|
+
casted_data = cls.cast_mask_component(operand.data, from_type, to_type, mask)
|
|
448
|
+
else:
|
|
449
|
+
casted_data = cls.cast_component(operand.data, from_type, to_type)
|
|
450
|
+
result_component.data = casted_data
|
|
451
|
+
return result_component
|