235 lines
8.0 KiB
Python
235 lines
8.0 KiB
Python
import math
|
|
import random
|
|
import sys
|
|
import time
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
import pytest
|
|
|
|
import ray
|
|
from ray.data.tests.conftest import * # noqa
|
|
from ray.data.tests.conftest import (
|
|
CoreExecutionMetrics,
|
|
assert_core_execution_metrics_equals,
|
|
)
|
|
from ray.tests.conftest import * # noqa
|
|
|
|
|
|
def test_count(ray_start_regular):
|
|
ds = ray.data.range(100, override_num_blocks=10)
|
|
# We do not kick off the read task by default.
|
|
assert ds.count() == 100
|
|
# Getting number of rows should not trigger execution of any read tasks
|
|
# for ray.data.range(), as the number of rows is known beforehand.
|
|
|
|
assert_core_execution_metrics_equals(CoreExecutionMetrics(task_count={}))
|
|
|
|
|
|
def test_count_edge_case(ray_start_regular):
|
|
# Test this edge case: https://github.com/ray-project/ray/issues/44509.
|
|
ds = ray.data.range(10)
|
|
ds.count()
|
|
|
|
actual_count = ds.filter(fn=lambda row: row["id"] % 2 == 0).count()
|
|
|
|
assert actual_count == 5
|
|
|
|
|
|
def test_count_after_caching_after_execution(ray_start_regular):
|
|
SCALE_FACTOR = 5
|
|
FILE_ROW_COUNT = 150
|
|
DS_ROW_COUNT = FILE_ROW_COUNT * SCALE_FACTOR
|
|
paths = ["example://iris.csv"] * SCALE_FACTOR
|
|
ds = ray.data.read_csv(paths)
|
|
# Row count should be unknown before execution.
|
|
assert "num_rows=?" in str(ds)
|
|
# After iterating over bundles and completing execution, row count should be known.
|
|
list(ds.iter_internal_ref_bundles())
|
|
assert ds.count() == DS_ROW_COUNT
|
|
assert ds._cache._num_rows == DS_ROW_COUNT
|
|
|
|
|
|
@pytest.mark.parametrize("num_parts", [1, 30])
|
|
@pytest.mark.parametrize("ds_format", ["arrow", "pandas"])
|
|
def test_global_tabular_min(ray_start_regular_shared_2_cpus, ds_format, num_parts):
|
|
seed = int(time.time())
|
|
print(f"Seeding RNG for test_global_arrow_min with: {seed}")
|
|
random.seed(seed)
|
|
xs = list(range(100))
|
|
random.shuffle(xs)
|
|
|
|
def _to_pandas(ds):
|
|
return ds.map_batches(lambda x: x, batch_size=None, batch_format="pandas")
|
|
|
|
# Test built-in global min aggregation
|
|
ds = ray.data.from_items([{"A": x} for x in xs]).repartition(num_parts)
|
|
if ds_format == "pandas":
|
|
ds = _to_pandas(ds)
|
|
assert ds.min("A") == 0
|
|
|
|
# Test empty dataset
|
|
# Note: we explicitly set parallelism here to ensure there are no empty
|
|
# input blocks.
|
|
ds = ray.data.range(10, override_num_blocks=10)
|
|
if ds_format == "pandas":
|
|
ds = _to_pandas(ds)
|
|
assert ds.filter(lambda r: r["id"] > 10).min("id") is None
|
|
|
|
# Test built-in global min aggregation with nans
|
|
nan_ds = ray.data.from_items([{"A": x} for x in xs] + [{"A": None}]).repartition(
|
|
num_parts
|
|
)
|
|
if ds_format == "pandas":
|
|
nan_ds = _to_pandas(nan_ds)
|
|
assert nan_ds.min("A") == 0
|
|
# Test ignore_nulls=False
|
|
assert pd.isnull(nan_ds.min("A", ignore_nulls=False))
|
|
# Test all nans
|
|
nan_ds = ray.data.from_items([{"A": None}] * len(xs)).repartition(num_parts)
|
|
if ds_format == "pandas":
|
|
nan_ds = _to_pandas(nan_ds)
|
|
assert pd.isnull(nan_ds.min("A"))
|
|
assert pd.isnull(nan_ds.min("A", ignore_nulls=False))
|
|
|
|
|
|
@pytest.mark.parametrize("num_parts", [1, 30])
|
|
@pytest.mark.parametrize("ds_format", ["arrow", "pandas"])
|
|
def test_global_tabular_max(ray_start_regular_shared_2_cpus, ds_format, num_parts):
|
|
seed = int(time.time())
|
|
print(f"Seeding RNG for test_global_arrow_max with: {seed}")
|
|
random.seed(seed)
|
|
xs = list(range(100))
|
|
random.shuffle(xs)
|
|
|
|
def _to_pandas(ds):
|
|
return ds.map_batches(lambda x: x, batch_size=None, batch_format="pandas")
|
|
|
|
# Test built-in global max aggregation
|
|
ds = ray.data.from_items([{"A": x} for x in xs]).repartition(num_parts)
|
|
if ds_format == "pandas":
|
|
ds = _to_pandas(ds)
|
|
assert ds.max("A") == 99
|
|
|
|
# Test empty dataset
|
|
# Note: we explicitly set parallelism here to ensure there are no empty
|
|
# input blocks.
|
|
ds = ray.data.range(10, override_num_blocks=10)
|
|
if ds_format == "pandas":
|
|
ds = _to_pandas(ds)
|
|
assert ds.filter(lambda r: r["id"] > 10).max("id") is None
|
|
|
|
# Test built-in global max aggregation with nans
|
|
nan_ds = ray.data.from_items([{"A": x} for x in xs] + [{"A": None}]).repartition(
|
|
num_parts
|
|
)
|
|
if ds_format == "pandas":
|
|
nan_ds = _to_pandas(nan_ds)
|
|
assert nan_ds.max("A") == 99
|
|
# Test ignore_nulls=False
|
|
assert pd.isnull(nan_ds.max("A", ignore_nulls=False))
|
|
# Test all nans
|
|
nan_ds = ray.data.from_items([{"A": None}] * len(xs)).repartition(num_parts)
|
|
if ds_format == "pandas":
|
|
nan_ds = _to_pandas(nan_ds)
|
|
assert pd.isnull(nan_ds.max("A"))
|
|
assert pd.isnull(nan_ds.max("A", ignore_nulls=False))
|
|
|
|
|
|
@pytest.mark.parametrize("num_parts", [1, 30])
|
|
@pytest.mark.parametrize("ds_format", ["arrow", "pandas"])
|
|
def test_global_tabular_mean(ray_start_regular_shared_2_cpus, ds_format, num_parts):
|
|
seed = int(time.time())
|
|
print(f"Seeding RNG for test_global_arrow_mean with: {seed}")
|
|
random.seed(seed)
|
|
xs = list(range(100))
|
|
random.shuffle(xs)
|
|
|
|
def _to_pandas(ds):
|
|
return ds.map_batches(lambda x: x, batch_size=None, batch_format="pandas")
|
|
|
|
# Test built-in global mean aggregation
|
|
ds = ray.data.from_items([{"A": x} for x in xs]).repartition(num_parts)
|
|
if ds_format == "pandas":
|
|
ds = _to_pandas(ds)
|
|
assert ds.mean("A") == 49.5
|
|
|
|
# Test empty dataset
|
|
# Note: we explicitly set parallelism here to ensure there are no empty
|
|
# input blocks.
|
|
ds = ray.data.range(10, override_num_blocks=10)
|
|
if ds_format == "pandas":
|
|
ds = _to_pandas(ds)
|
|
assert ds.filter(lambda r: r["id"] > 10).mean("id") is None
|
|
|
|
# Test built-in global mean aggregation with nans
|
|
nan_ds = ray.data.from_items([{"A": x} for x in xs] + [{"A": None}]).repartition(
|
|
num_parts
|
|
)
|
|
if ds_format == "pandas":
|
|
nan_ds = _to_pandas(nan_ds)
|
|
assert nan_ds.mean("A") == 49.5
|
|
# Test ignore_nulls=False
|
|
assert pd.isnull(nan_ds.mean("A", ignore_nulls=False))
|
|
# Test all nans
|
|
nan_ds = ray.data.from_items([{"A": None}] * len(xs)).repartition(num_parts)
|
|
if ds_format == "pandas":
|
|
nan_ds = _to_pandas(nan_ds)
|
|
assert pd.isnull(nan_ds.mean("A"))
|
|
assert pd.isnull(nan_ds.mean("A", ignore_nulls=False))
|
|
|
|
|
|
@pytest.mark.parametrize("num_parts", [1, 30])
|
|
@pytest.mark.parametrize("ds_format", ["arrow", "pandas"])
|
|
def test_global_tabular_std(ray_start_regular_shared_2_cpus, ds_format, num_parts):
|
|
# NOTE: Do not change the seed
|
|
seed = 1740035705
|
|
|
|
random.seed(seed)
|
|
xs = list(range(100))
|
|
random.shuffle(xs)
|
|
|
|
def _to_arrow(ds):
|
|
return ds.map_batches(lambda x: x, batch_size=None, batch_format="pyarrow")
|
|
|
|
def _to_pandas(ds):
|
|
return ds.map_batches(lambda x: x, batch_size=None, batch_format="pandas")
|
|
|
|
# Test built-in global max aggregation
|
|
df = pd.DataFrame({"A": xs})
|
|
ds = ray.data.from_pandas(df).repartition(num_parts)
|
|
if ds_format == "arrow":
|
|
ds = _to_arrow(ds)
|
|
assert math.isclose(ds.std("A"), df["A"].std())
|
|
assert math.isclose(ds.std("A", ddof=0), df["A"].std(ddof=0))
|
|
|
|
# Test empty dataset
|
|
ds = ray.data.from_pandas(pd.DataFrame({"A": []}))
|
|
if ds_format == "arrow":
|
|
ds = _to_arrow(ds)
|
|
assert pd.isnull(ds.std("A"))
|
|
# Test edge cases
|
|
ds = ray.data.from_pandas(pd.DataFrame({"A": [3]}))
|
|
if ds_format == "arrow":
|
|
ds = _to_arrow(ds)
|
|
assert np.isnan(ds.std("A"))
|
|
|
|
# Test built-in global std aggregation with nans
|
|
nan_df = pd.DataFrame({"A": xs + [None]})
|
|
nan_ds = ray.data.from_pandas(nan_df).repartition(num_parts)
|
|
if ds_format == "arrow":
|
|
nan_ds = _to_arrow(nan_ds)
|
|
assert math.isclose(nan_ds.std("A"), nan_df["A"].std())
|
|
# Test ignore_nulls=False
|
|
assert pd.isnull(nan_ds.std("A", ignore_nulls=False))
|
|
# Test all nans
|
|
nan_ds = ray.data.from_items([{"A": None}] * len(xs)).repartition(num_parts)
|
|
if ds_format == "pandas":
|
|
nan_ds = _to_pandas(nan_ds)
|
|
assert pd.isnull(nan_ds.std("A"))
|
|
assert pd.isnull(nan_ds.std("A", ignore_nulls=False))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(pytest.main(["-v", __file__]))
|