147 lines
4.7 KiB
Python
147 lines
4.7 KiB
Python
import numpy as np
|
|
import pandas as pd
|
|
import pytest
|
|
|
|
import ray
|
|
from ray.data.preprocessors import PowerTransformer
|
|
|
|
|
|
def test_power_transformer():
|
|
"""Tests basic PowerTransformer functionality."""
|
|
|
|
# yeo-johnson
|
|
col_a = [-1, 0]
|
|
col_b = [0, 1]
|
|
in_df = pd.DataFrame.from_dict({"A": col_a, "B": col_b})
|
|
ds = ray.data.from_pandas(in_df)
|
|
|
|
# yeo-johnson power=0
|
|
transformer = PowerTransformer(["A", "B"], power=0)
|
|
transformed = transformer.transform(ds)
|
|
out_df = transformed.to_pandas()
|
|
|
|
processed_col_a = [-1.5, 0]
|
|
processed_col_b = [0, np.log(2)]
|
|
expected_df = pd.DataFrame.from_dict(
|
|
{"A": processed_col_a, "B": processed_col_b}
|
|
).astype(out_df.dtypes.to_dict())
|
|
|
|
pd.testing.assert_frame_equal(out_df, expected_df, check_like=True)
|
|
|
|
# yeo-johnson power=2
|
|
transformer = PowerTransformer(["A", "B"], power=2)
|
|
transformed = transformer.transform(ds)
|
|
out_df = transformed.to_pandas()
|
|
processed_col_a = [-np.log(2), 0]
|
|
processed_col_b = [0, 1.5]
|
|
expected_df = pd.DataFrame.from_dict(
|
|
{"A": processed_col_a, "B": processed_col_b}
|
|
).astype(out_df.dtypes.to_dict())
|
|
|
|
pd.testing.assert_frame_equal(out_df, expected_df, check_like=True)
|
|
|
|
# box-cox
|
|
col_a = [1, 2]
|
|
col_b = [3, 4]
|
|
in_df = pd.DataFrame.from_dict({"A": col_a, "B": col_b})
|
|
ds = ray.data.from_pandas(in_df)
|
|
|
|
# box-cox power=0
|
|
transformer = PowerTransformer(["A", "B"], power=0, method="box-cox")
|
|
transformed = transformer.transform(ds)
|
|
out_df = transformed.to_pandas()
|
|
|
|
processed_col_a = [0, np.log(2)]
|
|
processed_col_b = [np.log(3), np.log(4)]
|
|
expected_df = pd.DataFrame.from_dict(
|
|
{"A": processed_col_a, "B": processed_col_b}
|
|
).astype(out_df.dtypes.to_dict())
|
|
|
|
pd.testing.assert_frame_equal(out_df, expected_df, check_like=True)
|
|
|
|
# box-cox power=2
|
|
transformer = PowerTransformer(["A", "B"], power=2, method="box-cox")
|
|
transformed = transformer.transform(ds)
|
|
out_df = transformed.to_pandas()
|
|
processed_col_a = [0, 1.5]
|
|
processed_col_b = [4, 7.5]
|
|
expected_df = pd.DataFrame.from_dict(
|
|
{"A": processed_col_a, "B": processed_col_b}
|
|
).astype(out_df.dtypes.to_dict())
|
|
|
|
pd.testing.assert_frame_equal(out_df, expected_df, check_like=True)
|
|
|
|
# Test append mode
|
|
# First test that providing wrong number of output columns raises error
|
|
with pytest.raises(
|
|
ValueError, match="The length of columns and output_columns must match."
|
|
):
|
|
PowerTransformer(columns=["A", "B"], power=2, output_columns=["A_transformed"])
|
|
|
|
# Test append mode with correct output columns
|
|
transformer = PowerTransformer(
|
|
columns=["A", "B"],
|
|
power=2,
|
|
method="box-cox",
|
|
output_columns=["A_transformed", "B_transformed"],
|
|
)
|
|
transformed = transformer.transform(ds)
|
|
out_df = transformed.to_pandas()
|
|
|
|
# Transformed columns should have the expected values
|
|
processed_col_a = [0, 1.5]
|
|
processed_col_b = [4, 7.5]
|
|
|
|
expected_df = pd.DataFrame(
|
|
{
|
|
"A": col_a,
|
|
"B": col_b,
|
|
"A_transformed": processed_col_a,
|
|
"B_transformed": processed_col_b,
|
|
}
|
|
).astype(out_df.dtypes.to_dict())
|
|
|
|
pd.testing.assert_frame_equal(out_df, expected_df, check_like=True)
|
|
|
|
|
|
def test_power_transformer_serialization():
|
|
"""Test PowerTransformer serialization and deserialization functionality."""
|
|
from ray.data.preprocessor import SerializablePreprocessorBase
|
|
|
|
# Create transformer with test data
|
|
transformer = PowerTransformer(columns=["A", "B"], power=2.0, method="yeo-johnson")
|
|
|
|
# Serialize using CloudPickle
|
|
serialized = transformer.serialize()
|
|
|
|
# Verify it's binary CloudPickle format
|
|
assert isinstance(serialized, bytes)
|
|
assert serialized.startswith(SerializablePreprocessorBase.MAGIC_CLOUDPICKLE)
|
|
|
|
# Deserialize
|
|
deserialized = PowerTransformer.deserialize(serialized)
|
|
|
|
# Verify type and field values
|
|
assert isinstance(deserialized, PowerTransformer)
|
|
assert deserialized.columns == ["A", "B"]
|
|
assert deserialized.power == 2.0
|
|
assert deserialized.method == "yeo-johnson"
|
|
assert deserialized.output_columns == ["A", "B"]
|
|
|
|
# Verify it works correctly
|
|
df = pd.DataFrame({"A": [1.0, 2.0, 3.0], "B": [4.0, 5.0, 6.0]})
|
|
result = deserialized.transform_batch(df.copy())
|
|
|
|
# Verify transformation was applied
|
|
# For power=2, yeo-johnson on positive values: ((x+1)^2 - 1) / 2
|
|
expected_a_0 = ((1.0 + 1) ** 2.0 - 1) / 2.0
|
|
assert abs(result["A"][0] - expected_a_0) < 1e-10
|
|
assert "A" in result.columns
|
|
assert "B" in result.columns
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
sys.exit(pytest.main(["-sv", __file__]))
|