apache-hamilton 1.90.0.dev0__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.
- apache_hamilton-1.90.0.dev0.dist-info/METADATA +407 -0
- apache_hamilton-1.90.0.dev0.dist-info/RECORD +151 -0
- apache_hamilton-1.90.0.dev0.dist-info/WHEEL +4 -0
- apache_hamilton-1.90.0.dev0.dist-info/entry_points.txt +9 -0
- apache_hamilton-1.90.0.dev0.dist-info/licenses/DISCLAIMER +10 -0
- apache_hamilton-1.90.0.dev0.dist-info/licenses/LICENSE +228 -0
- apache_hamilton-1.90.0.dev0.dist-info/licenses/NOTICE +5 -0
- hamilton/__init__.py +24 -0
- hamilton/ad_hoc_utils.py +132 -0
- hamilton/async_driver.py +465 -0
- hamilton/base.py +466 -0
- hamilton/caching/__init__.py +16 -0
- hamilton/caching/adapter.py +1475 -0
- hamilton/caching/cache_key.py +70 -0
- hamilton/caching/fingerprinting.py +287 -0
- hamilton/caching/stores/__init__.py +16 -0
- hamilton/caching/stores/base.py +242 -0
- hamilton/caching/stores/file.py +140 -0
- hamilton/caching/stores/memory.py +297 -0
- hamilton/caching/stores/sqlite.py +282 -0
- hamilton/caching/stores/utils.py +40 -0
- hamilton/cli/__init__.py +16 -0
- hamilton/cli/__main__.py +328 -0
- hamilton/cli/commands.py +126 -0
- hamilton/cli/logic.py +338 -0
- hamilton/common/__init__.py +76 -0
- hamilton/contrib/__init__.py +41 -0
- hamilton/data_quality/__init__.py +16 -0
- hamilton/data_quality/base.py +198 -0
- hamilton/data_quality/default_validators.py +560 -0
- hamilton/data_quality/pandera_validators.py +121 -0
- hamilton/dataflows/__init__.py +726 -0
- hamilton/dataflows/template/README.md +25 -0
- hamilton/dataflows/template/__init__.py +54 -0
- hamilton/dataflows/template/author.md +28 -0
- hamilton/dataflows/template/requirements.txt +0 -0
- hamilton/dataflows/template/tags.json +7 -0
- hamilton/dataflows/template/valid_configs.jsonl +1 -0
- hamilton/dev_utils/__init__.py +16 -0
- hamilton/dev_utils/deprecation.py +204 -0
- hamilton/driver.py +2112 -0
- hamilton/execution/__init__.py +16 -0
- hamilton/execution/debugging_utils.py +56 -0
- hamilton/execution/executors.py +502 -0
- hamilton/execution/graph_functions.py +421 -0
- hamilton/execution/grouping.py +430 -0
- hamilton/execution/state.py +539 -0
- hamilton/experimental/__init__.py +27 -0
- hamilton/experimental/databackend.py +61 -0
- hamilton/experimental/decorators/__init__.py +16 -0
- hamilton/experimental/decorators/parameterize_frame.py +233 -0
- hamilton/experimental/h_async.py +29 -0
- hamilton/experimental/h_cache.py +413 -0
- hamilton/experimental/h_dask.py +28 -0
- hamilton/experimental/h_databackends.py +174 -0
- hamilton/experimental/h_ray.py +28 -0
- hamilton/experimental/h_spark.py +32 -0
- hamilton/function_modifiers/README +40 -0
- hamilton/function_modifiers/__init__.py +121 -0
- hamilton/function_modifiers/adapters.py +900 -0
- hamilton/function_modifiers/base.py +859 -0
- hamilton/function_modifiers/configuration.py +310 -0
- hamilton/function_modifiers/delayed.py +202 -0
- hamilton/function_modifiers/dependencies.py +246 -0
- hamilton/function_modifiers/expanders.py +1230 -0
- hamilton/function_modifiers/macros.py +1634 -0
- hamilton/function_modifiers/metadata.py +434 -0
- hamilton/function_modifiers/recursive.py +908 -0
- hamilton/function_modifiers/validation.py +289 -0
- hamilton/function_modifiers_base.py +31 -0
- hamilton/graph.py +1153 -0
- hamilton/graph_types.py +264 -0
- hamilton/graph_utils.py +41 -0
- hamilton/htypes.py +450 -0
- hamilton/io/__init__.py +32 -0
- hamilton/io/data_adapters.py +216 -0
- hamilton/io/default_data_loaders.py +224 -0
- hamilton/io/materialization.py +500 -0
- hamilton/io/utils.py +158 -0
- hamilton/lifecycle/__init__.py +67 -0
- hamilton/lifecycle/api.py +833 -0
- hamilton/lifecycle/base.py +1130 -0
- hamilton/lifecycle/default.py +802 -0
- hamilton/log_setup.py +47 -0
- hamilton/models.py +92 -0
- hamilton/node.py +449 -0
- hamilton/plugins/README.md +48 -0
- hamilton/plugins/__init__.py +16 -0
- hamilton/plugins/dask_extensions.py +47 -0
- hamilton/plugins/dlt_extensions.py +161 -0
- hamilton/plugins/geopandas_extensions.py +49 -0
- hamilton/plugins/h_dask.py +331 -0
- hamilton/plugins/h_ddog.py +522 -0
- hamilton/plugins/h_diskcache.py +163 -0
- hamilton/plugins/h_experiments/__init__.py +22 -0
- hamilton/plugins/h_experiments/__main__.py +62 -0
- hamilton/plugins/h_experiments/cache.py +39 -0
- hamilton/plugins/h_experiments/data_model.py +68 -0
- hamilton/plugins/h_experiments/hook.py +219 -0
- hamilton/plugins/h_experiments/server.py +375 -0
- hamilton/plugins/h_kedro.py +152 -0
- hamilton/plugins/h_logging.py +454 -0
- hamilton/plugins/h_mcp/__init__.py +28 -0
- hamilton/plugins/h_mcp/__main__.py +33 -0
- hamilton/plugins/h_mcp/_helpers.py +129 -0
- hamilton/plugins/h_mcp/_templates.py +417 -0
- hamilton/plugins/h_mcp/server.py +328 -0
- hamilton/plugins/h_mlflow.py +335 -0
- hamilton/plugins/h_narwhals.py +134 -0
- hamilton/plugins/h_openlineage.py +400 -0
- hamilton/plugins/h_opentelemetry.py +167 -0
- hamilton/plugins/h_pandas.py +257 -0
- hamilton/plugins/h_pandera.py +117 -0
- hamilton/plugins/h_polars.py +304 -0
- hamilton/plugins/h_polars_lazyframe.py +282 -0
- hamilton/plugins/h_pyarrow.py +56 -0
- hamilton/plugins/h_pydantic.py +127 -0
- hamilton/plugins/h_ray.py +242 -0
- hamilton/plugins/h_rich.py +142 -0
- hamilton/plugins/h_schema.py +493 -0
- hamilton/plugins/h_slack.py +100 -0
- hamilton/plugins/h_spark.py +1380 -0
- hamilton/plugins/h_threadpool.py +125 -0
- hamilton/plugins/h_tqdm.py +122 -0
- hamilton/plugins/h_vaex.py +129 -0
- hamilton/plugins/huggingface_extensions.py +236 -0
- hamilton/plugins/ibis_extensions.py +93 -0
- hamilton/plugins/jupyter_magic.py +622 -0
- hamilton/plugins/kedro_extensions.py +117 -0
- hamilton/plugins/lightgbm_extensions.py +99 -0
- hamilton/plugins/matplotlib_extensions.py +108 -0
- hamilton/plugins/mlflow_extensions.py +216 -0
- hamilton/plugins/numpy_extensions.py +105 -0
- hamilton/plugins/pandas_extensions.py +1763 -0
- hamilton/plugins/plotly_extensions.py +150 -0
- hamilton/plugins/polars_extensions.py +71 -0
- hamilton/plugins/polars_implementations.py +25 -0
- hamilton/plugins/polars_lazyframe_extensions.py +302 -0
- hamilton/plugins/polars_post_1_0_0_extensions.py +877 -0
- hamilton/plugins/polars_pre_1_0_0_extension.py +836 -0
- hamilton/plugins/pydantic_extensions.py +98 -0
- hamilton/plugins/pyspark_pandas_extensions.py +47 -0
- hamilton/plugins/sklearn_plot_extensions.py +129 -0
- hamilton/plugins/spark_extensions.py +105 -0
- hamilton/plugins/vaex_extensions.py +51 -0
- hamilton/plugins/xgboost_extensions.py +91 -0
- hamilton/plugins/yaml_extensions.py +89 -0
- hamilton/registry.py +254 -0
- hamilton/settings.py +18 -0
- hamilton/telemetry.py +50 -0
- hamilton/version.py +18 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from hamilton.data_quality import base, default_validators
|
|
21
|
+
from hamilton.htypes import custom_subclass_check
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import pydantic # noqa: F401
|
|
25
|
+
except ModuleNotFoundError as e:
|
|
26
|
+
raise NotImplementedError(
|
|
27
|
+
"Cannot import `pydantic` from `pydantic_validators`. Run pip install 'sf-hamilton[pydantic]' if needed."
|
|
28
|
+
) from e
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
from pydantic import BaseModel, TypeAdapter, ValidationError
|
|
32
|
+
except ImportError as e:
|
|
33
|
+
raise NotImplementedError(
|
|
34
|
+
"`pydantic>=2.0` required to use `pydantic_validators`. Run pip install 'sf-hamilton[pydantic]' if needed."
|
|
35
|
+
) from e
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
COLUMN_FRIENDLY_DF_TYPE = False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class PydanticModelValidator(base.BaseDefaultValidator):
|
|
42
|
+
"""Pydantic model compatibility validator (requires ``pydantic>=2.0``)
|
|
43
|
+
|
|
44
|
+
Note that this validator uses pydantic's strict mode, which does not allow for
|
|
45
|
+
coercion of data. This means that if an object does not exactly match the reference
|
|
46
|
+
type, it will fail validation, regardless of whether it could be coerced into the
|
|
47
|
+
correct type.
|
|
48
|
+
|
|
49
|
+
:param model: Pydantic model to validate against
|
|
50
|
+
:param importance: Importance of the validator, possible values "warn" and "fail"
|
|
51
|
+
:param arbitrary_types_allowed: Whether arbitrary types are allowed in the model
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, model: type[BaseModel], importance: str):
|
|
55
|
+
super(PydanticModelValidator, self).__init__(importance)
|
|
56
|
+
self.model = model
|
|
57
|
+
self._model_adapter = TypeAdapter(model)
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def applies_to(cls, datatype: type[type]) -> bool:
|
|
61
|
+
# In addition to checking for a subclass of BaseModel, we also check for dict
|
|
62
|
+
# as this is the standard 'de-serialized' format of pydantic models in python
|
|
63
|
+
return custom_subclass_check(datatype, BaseModel) or custom_subclass_check(datatype, dict)
|
|
64
|
+
|
|
65
|
+
def description(self) -> str:
|
|
66
|
+
return "Validates that the returned object is compatible with the specified pydantic model"
|
|
67
|
+
|
|
68
|
+
def validate(self, data: Any) -> base.ValidationResult:
|
|
69
|
+
try:
|
|
70
|
+
# Currently, validate can not alter the output data, so we must use
|
|
71
|
+
# strict=True. The downside to this is that data that could be coerced
|
|
72
|
+
# into the correct type will fail validation.
|
|
73
|
+
self._model_adapter.validate_python(data, strict=True)
|
|
74
|
+
except ValidationError as e:
|
|
75
|
+
return base.ValidationResult(
|
|
76
|
+
passes=False, message=str(e), diagnostics={"model_errors": e.errors()}
|
|
77
|
+
)
|
|
78
|
+
return base.ValidationResult(
|
|
79
|
+
passes=True,
|
|
80
|
+
message=f"Data passes pydantic check for model {str(self.model)}",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def arg(cls) -> str:
|
|
85
|
+
return "model"
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def name(cls) -> str:
|
|
89
|
+
return "pydantic_validator"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def register_validators():
|
|
93
|
+
"""Utility method to append pydantic validators as needed"""
|
|
94
|
+
validators = [PydanticModelValidator]
|
|
95
|
+
default_validators.AVAILABLE_DEFAULT_VALIDATORS.extend(validators)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
register_validators()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
import pyspark.pandas as ps
|
|
22
|
+
except ImportError as e:
|
|
23
|
+
raise NotImplementedError("Pyspark is not installed.") from e
|
|
24
|
+
|
|
25
|
+
from hamilton import registry
|
|
26
|
+
|
|
27
|
+
DATAFRAME_TYPE = ps.DataFrame
|
|
28
|
+
COLUMN_TYPE = ps.Series
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@registry.get_column.register(ps.DataFrame)
|
|
32
|
+
def get_column_pyspark_pandas(df: ps.DataFrame, column_name: str) -> ps.Series:
|
|
33
|
+
return df[column_name]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@registry.fill_with_scalar.register(ps.DataFrame)
|
|
37
|
+
def fill_with_scalar_pyspark_pandas(df: ps.DataFrame, column_name: str, value: Any) -> ps.DataFrame:
|
|
38
|
+
df[column_name] = value
|
|
39
|
+
return df
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def register_types():
|
|
43
|
+
"""Function to register the types for this extension."""
|
|
44
|
+
registry.register_types("pyspark_pandas", DATAFRAME_TYPE, COLUMN_TYPE)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
register_types()
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
import dataclasses
|
|
19
|
+
from collections.abc import Collection
|
|
20
|
+
from os import PathLike
|
|
21
|
+
from typing import Any, Union
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import sklearn.inspection
|
|
25
|
+
import sklearn.metrics
|
|
26
|
+
import sklearn.model_selection
|
|
27
|
+
from matplotlib import pyplot
|
|
28
|
+
except ImportError as e:
|
|
29
|
+
raise NotImplementedError("scikit-learn is not installed.") from e
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
from hamilton import registry
|
|
33
|
+
from hamilton.io import utils
|
|
34
|
+
from hamilton.io.data_adapters import DataSaver
|
|
35
|
+
|
|
36
|
+
display_classes = [
|
|
37
|
+
"ConfusionMatrixDisplay",
|
|
38
|
+
"DetCurveDisplay",
|
|
39
|
+
"PrecisionRecallDisplay",
|
|
40
|
+
"PredictionErrorDisplay",
|
|
41
|
+
"RocCurveDisplay",
|
|
42
|
+
"CalibrationDisplay",
|
|
43
|
+
"DecisionBoundaryDisplay",
|
|
44
|
+
"LearningCurveDisplay",
|
|
45
|
+
"PartialDependenceDisplay",
|
|
46
|
+
"ValidationCurveDisplay",
|
|
47
|
+
]
|
|
48
|
+
SKLEARN_PLOT_TYPES = []
|
|
49
|
+
for class_name in display_classes:
|
|
50
|
+
# get the attribute via string from sklearn.metrics; if not found return None
|
|
51
|
+
class_metrics = getattr(sklearn.metrics, class_name, None)
|
|
52
|
+
class_inspection = getattr(sklearn.inspection, class_name, None)
|
|
53
|
+
class_model_selection = getattr(sklearn.model_selection, class_name, None)
|
|
54
|
+
if class_metrics:
|
|
55
|
+
SKLEARN_PLOT_TYPES.append(class_metrics)
|
|
56
|
+
if class_inspection:
|
|
57
|
+
SKLEARN_PLOT_TYPES.append(class_inspection)
|
|
58
|
+
if class_model_selection:
|
|
59
|
+
SKLEARN_PLOT_TYPES.append(class_model_selection)
|
|
60
|
+
|
|
61
|
+
SKLEARN_PLOT_TYPES.append(pyplot.Figure)
|
|
62
|
+
SKLEARN_PLOT_TYPES_ANNOTATION = Union[tuple(SKLEARN_PLOT_TYPES)]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclasses.dataclass
|
|
66
|
+
class SklearnPlotSaver(DataSaver):
|
|
67
|
+
path: str | PathLike
|
|
68
|
+
# kwargs
|
|
69
|
+
dpi: float = 200
|
|
70
|
+
format: str = "png"
|
|
71
|
+
metadata: dict | None = None
|
|
72
|
+
bbox_inches: str = None
|
|
73
|
+
pad_inches: float = 0.1
|
|
74
|
+
backend: str | None = None
|
|
75
|
+
papertype: str = None
|
|
76
|
+
transparent: bool = None
|
|
77
|
+
bbox_extra_artists: list | None = None
|
|
78
|
+
pil_kwargs: dict | None = None
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def applicable_types(cls) -> Collection[type]:
|
|
82
|
+
return SKLEARN_PLOT_TYPES
|
|
83
|
+
|
|
84
|
+
def _get_saving_kwargs(self) -> dict[str, Any]:
|
|
85
|
+
kwargs = {}
|
|
86
|
+
if self.dpi is not None:
|
|
87
|
+
kwargs["dpi"] = self.dpi
|
|
88
|
+
if self.format is not None:
|
|
89
|
+
kwargs["format"] = self.format
|
|
90
|
+
if self.metadata is not None:
|
|
91
|
+
kwargs["metadata"] = self.metadata
|
|
92
|
+
if self.bbox_inches is not None:
|
|
93
|
+
kwargs["bbox_inches"] = self.bbox_inches
|
|
94
|
+
if self.pad_inches is not None:
|
|
95
|
+
kwargs["pad_inches"] = self.pad_inches
|
|
96
|
+
if self.backend is not None:
|
|
97
|
+
kwargs["backend"] = self.backend
|
|
98
|
+
if self.papertype is not None:
|
|
99
|
+
kwargs["papertype"] = self.papertype
|
|
100
|
+
if self.transparent is not None:
|
|
101
|
+
kwargs["transparent"] = self.transparent
|
|
102
|
+
if self.bbox_extra_artists is not None:
|
|
103
|
+
kwargs["bbox_extra_artists"] = self.bbox_extra_artists
|
|
104
|
+
if self.pil_kwargs is not None:
|
|
105
|
+
kwargs["pil_kwargs"] = self.pil_kwargs
|
|
106
|
+
return kwargs
|
|
107
|
+
|
|
108
|
+
def save_data(self, data: SKLEARN_PLOT_TYPES_ANNOTATION) -> dict[str, Any]:
|
|
109
|
+
if isinstance(data, pyplot.Figure):
|
|
110
|
+
figure = data
|
|
111
|
+
else:
|
|
112
|
+
data.plot()
|
|
113
|
+
figure = data.figure_
|
|
114
|
+
figure.savefig(self.path, **self._get_saving_kwargs())
|
|
115
|
+
return utils.get_file_metadata(self.path)
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def name(cls) -> str:
|
|
119
|
+
return "png"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def register_data_loaders():
|
|
123
|
+
"""Function to register the data loaders for this extension."""
|
|
124
|
+
registry.register_adapter(SklearnPlotSaver)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
register_data_loaders()
|
|
128
|
+
|
|
129
|
+
COLUMN_FRIENDLY_DF_TYPE = False
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
import abc
|
|
19
|
+
import dataclasses
|
|
20
|
+
from collections.abc import Collection
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import pyspark.sql as ps
|
|
25
|
+
except ImportError as e:
|
|
26
|
+
raise NotImplementedError("Pyspark is not installed.") from e
|
|
27
|
+
|
|
28
|
+
from pandas import DataFrame
|
|
29
|
+
from pyspark.sql import SparkSession
|
|
30
|
+
|
|
31
|
+
from hamilton import registry
|
|
32
|
+
from hamilton.io import utils
|
|
33
|
+
from hamilton.io.data_adapters import DataLoader
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclasses.dataclass
|
|
37
|
+
class SparkDataFrameDataLoader(DataLoader):
|
|
38
|
+
"""Base class for data loaders that load pyspark dataframes.
|
|
39
|
+
We are not yet including data savers, but that will be added to this most likely..
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
spark: SparkSession
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def applicable_types(cls) -> Collection[type]:
|
|
46
|
+
return [ps.DataFrame]
|
|
47
|
+
|
|
48
|
+
@abc.abstractmethod
|
|
49
|
+
def load_data(self, type_: type[DataFrame]) -> tuple[ps.DataFrame, dict[str, Any]]:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclasses.dataclass
|
|
54
|
+
class CSVDataLoader(SparkDataFrameDataLoader):
|
|
55
|
+
path: str # It supports multiple but for now we're going to have a single one
|
|
56
|
+
# We can always make that a list of strings, or make a multiple reader (.multicsv)
|
|
57
|
+
header: bool = True
|
|
58
|
+
sep: str = ","
|
|
59
|
+
|
|
60
|
+
def load_data(self, type_: type[DataFrame]) -> tuple[ps.DataFrame, dict[str, Any]]:
|
|
61
|
+
return (
|
|
62
|
+
self.spark.read.csv(self.path, header=self.header, sep=self.sep, inferSchema=True),
|
|
63
|
+
utils.get_file_metadata(self.path),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def name(cls) -> str:
|
|
68
|
+
return "csv"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclasses.dataclass
|
|
72
|
+
class ParquetDataLoader(SparkDataFrameDataLoader):
|
|
73
|
+
path: str # It supports multiple but for now we're going to have a single one
|
|
74
|
+
|
|
75
|
+
# We can always make that a list of strings, or make a multiple reader (.multicsv)
|
|
76
|
+
|
|
77
|
+
def load_data(self, type_: type[DataFrame]) -> tuple[ps.DataFrame, dict[str, Any]]:
|
|
78
|
+
return self.spark.read.parquet(self.path), utils.get_file_metadata(self.path)
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def name(cls) -> str:
|
|
82
|
+
return "parquet"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def register_data_loaders():
|
|
86
|
+
"""Function to register the data loaders for this extension."""
|
|
87
|
+
for loader in [CSVDataLoader, ParquetDataLoader]:
|
|
88
|
+
registry.register_adapter(loader)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
COLUMN_FRIENDLY_DF_TYPE = False
|
|
92
|
+
|
|
93
|
+
register_data_loaders()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
DATAFRAME_TYPE = ps.DataFrame
|
|
97
|
+
COLUMN_TYPE = None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def register_types():
|
|
101
|
+
"""Function to register the types for this extension."""
|
|
102
|
+
registry.register_types("spark", DATAFRAME_TYPE, COLUMN_TYPE)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
register_types()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
from hamilton import registry
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
import vaex
|
|
26
|
+
except ImportError as e:
|
|
27
|
+
raise NotImplementedError("Vaex is not installed.") from e
|
|
28
|
+
|
|
29
|
+
DATAFRAME_TYPE = vaex.dataframe.DataFrame
|
|
30
|
+
COLUMN_TYPE = vaex.expression.Expression
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@registry.get_column.register(vaex.dataframe.DataFrame)
|
|
34
|
+
def get_column_vaex(df: vaex.dataframe.DataFrame, column_name: str) -> vaex.expression.Expression:
|
|
35
|
+
return df[column_name]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@registry.fill_with_scalar.register(vaex.dataframe.DataFrame)
|
|
39
|
+
def fill_with_scalar_vaex(
|
|
40
|
+
df: vaex.dataframe.DataFrame, column_name: str, scalar_value: Any
|
|
41
|
+
) -> vaex.dataframe.DataFrame:
|
|
42
|
+
df[column_name] = np.full((df.shape[0],), scalar_value)
|
|
43
|
+
return df
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def register_types():
|
|
47
|
+
"""Function to register the types for this extension."""
|
|
48
|
+
registry.register_types("vaex", DATAFRAME_TYPE, COLUMN_TYPE)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
register_types()
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
import dataclasses
|
|
19
|
+
from collections.abc import Collection
|
|
20
|
+
from os import PathLike
|
|
21
|
+
from typing import Any, Union
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import xgboost
|
|
25
|
+
except ImportError as e:
|
|
26
|
+
raise NotImplementedError("XGBoost is not installed.") from e
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
from hamilton import registry
|
|
30
|
+
from hamilton.io import utils
|
|
31
|
+
from hamilton.io.data_adapters import DataLoader, DataSaver
|
|
32
|
+
|
|
33
|
+
XGBOOST_MODEL_TYPES = [xgboost.XGBModel, xgboost.Booster]
|
|
34
|
+
XGBOOST_MODEL_TYPES_ANNOTATION = Union[xgboost.XGBModel, xgboost.Booster]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclasses.dataclass
|
|
38
|
+
class XGBoostJsonWriter(DataSaver):
|
|
39
|
+
"""Write XGBoost models and boosters to json format
|
|
40
|
+
See differences with pickle format: https://xgboost.readthedocs.io/en/stable/tutorials/saving_model.html
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
path: str | PathLike
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def applicable_types(cls) -> Collection[type]:
|
|
47
|
+
return XGBOOST_MODEL_TYPES
|
|
48
|
+
|
|
49
|
+
def save_data(self, data: XGBOOST_MODEL_TYPES_ANNOTATION) -> dict[str, Any]:
|
|
50
|
+
data.save_model(self.path)
|
|
51
|
+
return utils.get_file_metadata(self.path)
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def name(cls) -> str:
|
|
55
|
+
return "json"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclasses.dataclass
|
|
59
|
+
class XGBoostJsonReader(DataLoader):
|
|
60
|
+
"""Load XGBoost models and boosters to json format
|
|
61
|
+
See differences with pickle format: https://xgboost.readthedocs.io/en/stable/tutorials/saving_model.html
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
path: str | bytearray | PathLike
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def applicable_types(cls) -> Collection[type]:
|
|
68
|
+
return XGBOOST_MODEL_TYPES
|
|
69
|
+
|
|
70
|
+
def load_data(self, type_: type) -> tuple[XGBOOST_MODEL_TYPES_ANNOTATION, dict[str, Any]]:
|
|
71
|
+
model = type_()
|
|
72
|
+
model.load_model(self.path)
|
|
73
|
+
metadata = utils.get_file_metadata(self.path)
|
|
74
|
+
return model, metadata
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def name(cls) -> str:
|
|
78
|
+
return "json"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def register_data_loaders():
|
|
82
|
+
for loader in [
|
|
83
|
+
XGBoostJsonReader,
|
|
84
|
+
XGBoostJsonWriter,
|
|
85
|
+
]:
|
|
86
|
+
registry.register_adapter(loader)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
register_data_loaders()
|
|
90
|
+
|
|
91
|
+
COLUMN_FRIENDLY_DF_TYPE = False
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import yaml
|
|
20
|
+
except ImportError as e:
|
|
21
|
+
raise NotImplementedError("yaml is not installed and is needed for yaml hamilton plugin") from e
|
|
22
|
+
|
|
23
|
+
import dataclasses
|
|
24
|
+
import pathlib
|
|
25
|
+
from collections.abc import Collection
|
|
26
|
+
from typing import Any, Union
|
|
27
|
+
|
|
28
|
+
from hamilton import registry
|
|
29
|
+
from hamilton.io.data_adapters import DataLoader, DataSaver
|
|
30
|
+
from hamilton.io.utils import get_file_metadata
|
|
31
|
+
|
|
32
|
+
PrimitiveTypes = str, int, float, bool, dict, list
|
|
33
|
+
AcceptedTypes = Union[PrimitiveTypes]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclasses.dataclass
|
|
37
|
+
class YAMLDataLoader(DataLoader):
|
|
38
|
+
path: str | pathlib.Path
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def applicable_types(cls) -> Collection[type]:
|
|
42
|
+
return [*PrimitiveTypes]
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def name(cls) -> str:
|
|
46
|
+
return "yaml"
|
|
47
|
+
|
|
48
|
+
def load_data(self, type_: type) -> tuple[AcceptedTypes, dict[str, Any]]:
|
|
49
|
+
path = self.path
|
|
50
|
+
if isinstance(self.path, str):
|
|
51
|
+
path = pathlib.Path(self.path)
|
|
52
|
+
|
|
53
|
+
with path.open(mode="r") as f:
|
|
54
|
+
return yaml.safe_load(f), get_file_metadata(path)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclasses.dataclass
|
|
58
|
+
class YAMLDataSaver(DataSaver):
|
|
59
|
+
path: str | pathlib.Path
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def applicable_types(cls) -> Collection[type]:
|
|
63
|
+
return [*PrimitiveTypes]
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def name(cls) -> str:
|
|
67
|
+
return "yaml"
|
|
68
|
+
|
|
69
|
+
def save_data(self, data: AcceptedTypes) -> dict[str, Any]:
|
|
70
|
+
path = self.path
|
|
71
|
+
if isinstance(path, str):
|
|
72
|
+
path = pathlib.Path(path)
|
|
73
|
+
with path.open("w") as f:
|
|
74
|
+
yaml.dump(data, f)
|
|
75
|
+
return get_file_metadata(self.path)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
COLUMN_FRIENDLY_DF_TYPE = False
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def register_data_loaders():
|
|
82
|
+
for materializer in [
|
|
83
|
+
YAMLDataLoader,
|
|
84
|
+
YAMLDataSaver,
|
|
85
|
+
]:
|
|
86
|
+
registry.register_adapter(materializer)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
register_data_loaders()
|