Skip to content

Commit b4ffcde

Browse files
committed
GH-62: refactor(metadata): Rename ColumnProfile to ColumnInformation
The Pydantic model `ColumnProfile` and its corresponding field `column_profile` have been renamed to `ColumnInformation` and `columns` respectively. This change improves clarity and consistency. "Information" is a more accurate and general term for the data being stored (name, role, statistics) than "Profile". The field name `columns` is more concise and conventional. All related functions, variables, test fixtures, and test data have been updated to reflect this renaming. No functional changes are introduced. GH-62: refactor: Add support for text column statistics Introduces support for calculating and storing summary statistics for columns with the `text` role. This includes: - Adding a `ColumnStatisticsText` model. - Refactoring `ColumnStatistics` models to use a common `ColumnStatisticsBase` class, reducing code duplication. - Making statistical fields (like min, max, avg) optional to correctly handle columns with only NULL values. - Introducing a `ColumnType` enum to replace string literals for column types, improving type safety and readability. - Updating test fixtures and helpers to align with the new data models and support the `text` role. GH-62: ci: Optimize workflow triggers with path filtering Adds path filters to the pull_request and push triggers for the Python test workflow. This prevents the workflow from running on changes unrelated to the source code, tests, or project configuration (e.g., documentation updates). Additionally, this change adds a `push` trigger to run tests on merges to `main` and `develop`, ensuring the integrity of the primary branches. GH-62: refactor: Generalize Parquet serialization and statistics logic Extract the logic for serializing data to Parquet and calculating column statistics into a new, more generic `serialize_dataframe` function in `serialize/parquet.py`. This new function is now used for serializing both getML DataFrames/Views and prediction results. This refactoring improves code reuse and separation of concerns. Key changes: - `serialize_dataframe` accepts callables for saving the Parquet file and retrieving column roles, decoupling it from specific data structures like `getml.DataFrame`. - `serialize_predictions` now creates a `pyarrow.Table` directly from the numpy array and uses the new `serialize_dataframe` function, avoiding the overhead of creating an intermediate `getml.DataFrame`. - Moves shared logic out of `serialize_dataframe_or_view.py`, simplifying it significantly. GH-62: refactor(pydantic): Use `frozen=True` for immutable models Modernize Pydantic model definitions by replacing the `model_config` dictionary with the `frozen=True` class keyword argument. This change simplifies the code, makes it more readable, and aligns with current Pydantic v2 best practices for creating immutable models. No functional changes are introduced.
1 parent d8cdb40 commit b4ffcde

39 files changed

Lines changed: 1680 additions & 1506 deletions

.github/workflows/python-tests.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
name: Python Linting, Formatting, Testing, Coverage
22
on:
33
pull_request:
4-
branches: [main, develop]
4+
paths:
5+
- 'src/**'
6+
- 'tests/**'
7+
- '.github/workflows/**'
8+
- 'pyproject.toml'
9+
push:
10+
branches:
11+
- main
12+
- develop
13+
paths:
14+
- 'src/**'
15+
- 'tests/**'
16+
- '.github/workflows/**'
17+
- 'pyproject.toml'
518
jobs:
619
test:
720
runs-on: ubuntu-latest

src/getml_io/getml/feature_learning.py

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from collections.abc import Set as AbstractSet
4-
from typing import Annotated, ClassVar, Literal
4+
from typing import Annotated, Literal
55

66
from getml.feature_learning.aggregations.types import (
77
FastPropAggregations,
@@ -11,12 +11,10 @@
1111
CrossEntropyLossType,
1212
SquareLossType,
1313
)
14-
from pydantic import BaseModel, ConfigDict, Field
14+
from pydantic import BaseModel, Field
1515

1616

17-
class FastProp(BaseModel):
18-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
19-
17+
class FastProp(BaseModel, frozen=True):
2018
aggregation: AbstractSet[FastPropAggregations]
2119
delta_t: float
2220
loss_function: CrossEntropyLossType | SquareLossType | None
@@ -31,9 +29,7 @@ class FastProp(BaseModel):
3129
type: Literal["fast_prop"] = "fast_prop"
3230

3331

34-
class Fastboost(BaseModel):
35-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
36-
32+
class Fastboost(BaseModel, frozen=True):
3733
gamma: float
3834
loss_function: CrossEntropyLossType | SquareLossType | None
3935
max_depth: int
@@ -48,9 +44,7 @@ class Fastboost(BaseModel):
4844
type: Literal["fastboost"] = "fastboost"
4945

5046

51-
class Multirel(BaseModel):
52-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
53-
47+
class Multirel(BaseModel, frozen=True):
5448
aggregation: AbstractSet[MultirelAggregations]
5549
allow_sets: bool
5650
delta_t: float
@@ -75,9 +69,7 @@ class Multirel(BaseModel):
7569
type: Literal["multirel"] = "multirel"
7670

7771

78-
class Relboost(BaseModel):
79-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
80-
72+
class Relboost(BaseModel, frozen=True):
8173
allow_null_weights: bool
8274
delta_t: float
8375
gamma: float
@@ -98,9 +90,7 @@ class Relboost(BaseModel):
9890
type: Literal["relboost"] = "relboost"
9991

10092

101-
class RelMT(BaseModel):
102-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
103-
93+
class RelMT(BaseModel, frozen=True):
10494
allow_avg: bool
10595
delta_t: float
10696
gamma: float

src/getml_io/getml/features.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
from collections.abc import Mapping
2-
from typing import ClassVar
32

4-
from pydantic import BaseModel, ConfigDict
3+
from pydantic import BaseModel
54

65

7-
class Feature(BaseModel):
8-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
9-
6+
class Feature(BaseModel, frozen=True):
107
name: str
118
index: int
129
target: str

src/getml_io/getml/predictors.py

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,23 @@
11
from __future__ import annotations
22

3-
from typing import Annotated, ClassVar, Literal
3+
from typing import Annotated, Literal
44

5-
from pydantic import BaseModel, ConfigDict, Field
5+
from pydantic import BaseModel, Field
66

77

8-
class LinearRegression(BaseModel):
9-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
10-
8+
class LinearRegression(BaseModel, frozen=True):
119
learning_rate: float
1210
reg_lambda: float
1311
type: Literal["linear_regression"] = "linear_regression"
1412

1513

16-
class LogisticRegression(BaseModel):
17-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
18-
14+
class LogisticRegression(BaseModel, frozen=True):
1915
learning_rate: float
2016
reg_lambda: float
2117
type: Literal["logistic_regression"] = "logistic_regression"
2218

2319

24-
class ScaleGBMClassifier(BaseModel):
25-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
26-
20+
class ScaleGBMClassifier(BaseModel, frozen=True):
2721
colsample_bylevel: float
2822
colsample_bytree: float
2923
early_stopping_rounds: int
@@ -41,9 +35,7 @@ class ScaleGBMClassifier(BaseModel):
4135
type: Literal["scale_gbm_classifier"] = "scale_gbm_classifier"
4236

4337

44-
class ScaleGBMRegressor(BaseModel):
45-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
46-
38+
class ScaleGBMRegressor(BaseModel, frozen=True):
4739
colsample_bylevel: float
4840
colsample_bytree: float
4941
early_stopping_rounds: int
@@ -61,9 +53,7 @@ class ScaleGBMRegressor(BaseModel):
6153
type: Literal["scale_gbm_regressor"] = "scale_gbm_regressor"
6254

6355

64-
class XGBoostClassifier(BaseModel):
65-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
66-
56+
class XGBoostClassifier(BaseModel, frozen=True):
6757
booster: str
6858
colsample_bylevel: float
6959
colsample_bytree: float
@@ -90,9 +80,7 @@ class XGBoostClassifier(BaseModel):
9080
type: Literal["xgboost_classifier"] = "xgboost_classifier"
9181

9282

93-
class XGBoostRegressor(BaseModel):
94-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
95-
83+
class XGBoostRegressor(BaseModel, frozen=True):
9684
booster: str
9785
colsample_bylevel: float
9886
colsample_bytree: float

src/getml_io/getml/preprocessors.py

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,35 @@
11
from __future__ import annotations
22

33
from collections.abc import Set as AbstractSet
4-
from typing import Annotated, ClassVar, Literal
4+
from typing import Annotated, Literal
55

66
from getml.feature_learning.aggregations.types import MappingAggregations
7-
from pydantic import BaseModel, ConfigDict, Field
7+
from pydantic import BaseModel, Field
88

99

10-
class CategoryTrimmer(BaseModel):
11-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
12-
10+
class CategoryTrimmer(BaseModel, frozen=True):
1311
max_num_categories: int
1412
min_freq: int
1513
type: Literal["category_trimmer"] = "category_trimmer"
1614

1715

18-
class EmailDomain(BaseModel):
19-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
20-
16+
class EmailDomain(BaseModel, frozen=True):
2117
type: Literal["email_domain"] = "email_domain"
2218

2319

24-
class Imputation(BaseModel):
25-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
26-
20+
class Imputation(BaseModel, frozen=True):
2721
add_dummies: bool
2822
type: Literal["imputation"] = "imputation"
2923

3024

31-
class Mapping(BaseModel):
32-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
33-
25+
class Mapping(BaseModel, frozen=True):
3426
aggregation: AbstractSet[MappingAggregations]
3527
min_freq: int
3628
multithreading: bool
3729
type: Literal["mapping"] = "mapping"
3830

3931

40-
class Seasonal(BaseModel):
41-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
42-
32+
class Seasonal(BaseModel, frozen=True):
4333
disable_year: bool
4434
disable_month: bool
4535
disable_weekday: bool
@@ -48,18 +38,14 @@ class Seasonal(BaseModel):
4838
type: Literal["seasonal"] = "seasonal"
4939

5040

51-
class Substring(BaseModel):
52-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
53-
41+
class Substring(BaseModel, frozen=True):
5442
begin: int
5543
length: int
5644
unit: str
5745
type: Literal["substring"] = "substring"
5846

5947

60-
class TextFieldSplitter(BaseModel):
61-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
62-
48+
class TextFieldSplitter(BaseModel, frozen=True):
6349
type: Literal["text_field_splitter"] = "text_field_splitter"
6450

6551

src/getml_io/getml/project.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import logging
22
from collections.abc import Generator
33
from contextlib import contextmanager
4-
from typing import ClassVar
54

65
from getml.data import Container
76
from getml.pipeline import Pipeline
8-
from pydantic import BaseModel, ConfigDict
7+
from pydantic import BaseModel
98

109
from getml_io.getml.exception import (
1110
PipelineNotFoundError,
@@ -25,12 +24,7 @@
2524
logger: logging.Logger = logging.getLogger(__name__)
2625

2726

28-
class Project(BaseModel):
29-
model_config: ClassVar[ConfigDict] = ConfigDict(
30-
arbitrary_types_allowed=True,
31-
frozen=True,
32-
)
33-
27+
class Project(BaseModel, frozen=True, arbitrary_types_allowed=True):
3428
name: str
3529
pipeline: Pipeline
3630
container: Container
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
from typing import ClassVar
1+
from pydantic import BaseModel
22

3-
from pydantic import BaseModel, ConfigDict
4-
5-
6-
class ProjectInformation(BaseModel):
7-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
83

4+
class ProjectInformation(BaseModel, frozen=True):
95
project_name: str
106
pipeline_id: str
117
container_id: str

src/getml_io/getml/roles.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
from collections.abc import Sequence
44
from enum import Enum
5-
from typing import ClassVar
65

76
from getml.data import roles
8-
from pydantic import BaseModel, ConfigDict
7+
from pydantic import BaseModel
98

109

1110
class Role(str, Enum):
@@ -19,9 +18,7 @@ class Role(str, Enum):
1918
UNUSED_STRING = roles.unused_string
2019

2120

22-
class Roles(BaseModel):
23-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
24-
21+
class Roles(BaseModel, frozen=True):
2522
categorical: Sequence[str]
2623
join_key: Sequence[str]
2724
numerical: Sequence[str]

src/getml_io/getml/scores.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55
from pydantic import BaseModel, Field
66

77

8-
class _Score(BaseModel):
8+
class _Score(BaseModel, frozen=True):
99
date_time: datetime
1010
set_used: str
1111
target: str
1212

1313

14-
class ClassificationScore(_Score):
14+
class ClassificationScore(_Score, frozen=True):
1515
accuracy: float
1616
auc: float
1717
cross_entropy: float
1818
type: Literal["classification"] = "classification"
1919

2020

21-
class RegressionScore(_Score):
21+
class RegressionScore(_Score, frozen=True):
2222
mae: float
2323
rmse: float
2424
rsquared: float

src/getml_io/metadata/container_information.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
from __future__ import annotations
22

3-
from typing import ClassVar
4-
5-
from pydantic import BaseModel, ConfigDict
3+
from pydantic import BaseModel
64

75
from getml_io.metadata.dataframe_information import (
86
DataFrameInformation,
97
DataFrameInformationByName,
108
)
119

1210

13-
class ContainerInformation(BaseModel):
14-
model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True)
15-
11+
class ContainerInformation(BaseModel, frozen=True):
1612
id: str
1713
population: DataFrameInformation | None
1814
peripheral: DataFrameInformationByName

0 commit comments

Comments
 (0)