Skip to content

Commit e2ee879

Browse files
authored
test: cover datetime unit inference (#905)
* test: cover datetime unit inference * test: add datetime unit variants * test: expand datetime unit coverage * docs: note datetime unit tests
1 parent 6a272f1 commit e2ee879

5 files changed

Lines changed: 114 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
88
## [Development]
99
<!-- Do Not Erase This Section - Used for tracking unreleased changes -->
1010

11+
### Tests
12+
- **Temporal**: Added datetime unit parity coverage (ms/us/ns) for ring layouts, GFQL time ring layouts, and temporal comparison predicates; relaxed honeypot hypergraph datetime unit expectations.
13+
1114
## [0.50.5 - 2026-01-25]
1215

1316
### Fixed

graphistry/tests/compute/gfql/layouts/test_call_layout_radial.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import numpy as np
12
import pandas as pd
23
import pytest
34

@@ -56,6 +57,24 @@ def _graph_with_time(self):
5657
.nodes(nodes)\
5758
.bind(source='source', destination='target', node='node')
5859

60+
def _graph_with_time_unit(self, unit: str):
61+
edges = pd.DataFrame({
62+
'source': [0, 1, 2],
63+
'target': [1, 2, 0]
64+
})
65+
nodes = pd.DataFrame({
66+
'node': [0, 1, 2],
67+
'ts': pd.to_datetime([
68+
'2024-01-01T00:00:00',
69+
'2024-01-01T00:01:00',
70+
'2024-01-01T00:02:00'
71+
]).astype(f'datetime64[{unit}]')
72+
})
73+
return CGFull()\
74+
.edges(edges)\
75+
.nodes(nodes)\
76+
.bind(source='source', destination='target', node='node')
77+
5978
def test_continuous_mode_basic(self):
6079
"""Continuous mode should bind coordinates from a numeric column."""
6180
g = self._graph_with_numeric()
@@ -132,6 +151,39 @@ def test_time_mode_iso_strings(self):
132151

133152
assert {'x', 'y', 'r'} <= set(result._nodes.columns)
134153

154+
@pytest.mark.parametrize('unit', ['us', 'ms'])
155+
def test_time_mode_unit_parity(self, unit):
156+
g_ns = self._graph_with_time_unit('ns')
157+
g_unit = self._graph_with_time_unit(unit)
158+
159+
result_ns = execute_call(
160+
g_ns,
161+
'time_ring_layout',
162+
{
163+
'time_col': 'ts',
164+
'time_start': '2024-01-01T00:00:00',
165+
'time_end': '2024-01-01T00:02:00',
166+
'num_rings': 3
167+
},
168+
Engine.PANDAS
169+
)
170+
result_unit = execute_call(
171+
g_unit,
172+
'time_ring_layout',
173+
{
174+
'time_col': 'ts',
175+
'time_start': '2024-01-01T00:00:00',
176+
'time_end': '2024-01-01T00:02:00',
177+
'num_rings': 3
178+
},
179+
Engine.PANDAS
180+
)
181+
182+
np.testing.assert_allclose(
183+
result_ns._nodes['r'].to_numpy(),
184+
result_unit._nodes['r'].to_numpy()
185+
)
186+
135187
def test_time_mode_invalid_strings_raise(self):
136188
"""Time mode should raise when time bounds are invalid strings."""
137189
g = self._graph_with_time()

graphistry/tests/compute/predicates/test_temporal_values.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from graphistry.compute.ast_temporal import (
66
DateTimeValue, DateValue, TimeValue, temporal_value_from_json
77
)
8-
from graphistry.compute.predicates.comparison import gt
8+
from graphistry.compute.predicates.comparison import gt, lt, le, ge, eq, ne
99
from graphistry.embed_utils import check_cudf
1010

1111

@@ -127,6 +127,34 @@ def test_gt_localizes_naive_series(self):
127127
expected = pd.Series([False, True])
128128
pd.testing.assert_series_equal(result, expected)
129129

130+
@pytest.mark.parametrize("unit", ["us", "ms"])
131+
def test_gt_naive_series_unit_variants(self, unit):
132+
s = pd.Series(pd.to_datetime(["2024-01-01 05:00:00", "2024-01-01 08:00:00"]))
133+
s = s.astype(f"datetime64[{unit}]")
134+
predicate = gt(DateTimeValue("2024-01-01T06:00:00", "UTC"))
135+
result = predicate(s)
136+
expected = pd.Series([False, True])
137+
pd.testing.assert_series_equal(result, expected)
138+
139+
@pytest.mark.parametrize("unit", ["us", "ms"])
140+
@pytest.mark.parametrize(
141+
"predicate_factory, expected",
142+
[
143+
(lt, [True, False]),
144+
(le, [True, False]),
145+
(ge, [False, True]),
146+
(eq, [False, False]),
147+
(ne, [True, True]),
148+
],
149+
)
150+
def test_comparison_unit_variants(self, unit, predicate_factory, expected):
151+
s = pd.Series(pd.to_datetime(["2024-01-01 05:00:00", "2024-01-01 08:00:00"]))
152+
s = s.astype(f"datetime64[{unit}]")
153+
predicate = predicate_factory(DateTimeValue("2024-01-01T06:00:00", "UTC"))
154+
result = predicate(s)
155+
expected_series = pd.Series(expected)
156+
pd.testing.assert_series_equal(result, expected_series)
157+
130158
def test_gt_converts_timezone_aware_series(self):
131159
s = pd.Series(pd.to_datetime(["2024-01-01 12:00:00", "2024-01-01 14:00:00"], utc=True))
132160
predicate = gt(DateTimeValue("2024-01-01T08:00:00", "US/Eastern"))

graphistry/tests/layout/ring/test_time.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,21 @@ def test_ring_pd_us_ns_equivalence(self):
292292
})
293293
nodes_us = nodes.copy()
294294
nodes_us['t'] = nodes_us['t'].astype('datetime64[us]')
295+
nodes_ms = nodes.copy()
296+
nodes_ms['t'] = nodes_ms['t'].astype('datetime64[ms]')
295297

296298
g_ns = lg.edges(edges, 's', 'd').nodes(nodes).time_ring_layout('t')
297299
g_us = lg.edges(edges, 's', 'd').nodes(nodes_us).time_ring_layout('t')
300+
g_ms = lg.edges(edges, 's', 'd').nodes(nodes_ms).time_ring_layout('t')
298301

299302
np.testing.assert_allclose(
300303
g_ns._nodes['r'].to_numpy(),
301304
g_us._nodes['r'].to_numpy()
302305
)
306+
np.testing.assert_allclose(
307+
g_ns._nodes['r'].to_numpy(),
308+
g_ms._nodes['r'].to_numpy()
309+
)
303310

304311

305312
def test_ring_pd_axis_positions(self):

graphistry/tests/test_hyper_dask.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22

33
import datetime as dt, logging, numpy as np, os, pandas as pd, pyarrow as pa, pytest
4+
from pandas.api.types import is_datetime64_any_dtype
45

56
from graphistry.pygraphistry import PyGraphistry
67
from graphistry.Engine import Engine, DataframeLike
@@ -61,19 +62,18 @@ def honeypot_pdf() -> pd.DataFrame:
6162
"time(max)": "float64",
6263
"time(min)": "float64",
6364
}
64-
base_dtypes = {
65-
**base_csv_dtypes,
66-
"time(max)": "datetime64[ns]",
67-
"time(min)": "datetime64[ns]",
68-
}
6965
df = pd.read_csv(
7066
"graphistry/tests/data/honeypot.5.csv",
7167
#'graphistry/tests/data/honeypot.csv',
7268
dtype=base_csv_dtypes,
7369
parse_dates=["time(max)", "time(min)"],
7470
date_parser=lambda v: pd.to_datetime(int(float(v))),
7571
)
76-
assert df.dtypes.to_dict() == base_dtypes
72+
dtypes = df.dtypes.to_dict()
73+
for col, dtype in base_csv_dtypes.items():
74+
assert dtypes[col] == dtype
75+
for col in ("time(max)", "time(min)"):
76+
assert is_datetime64_any_dtype(dtypes[col])
7777
assert len(df) == HONEYPOT_ROWS
7878
return df
7979

@@ -537,6 +537,23 @@ def test_hyper_to_pa_mixed2(self):
537537
nodes_arr = pa.Table.from_pandas(hg.graph._nodes)
538538
assert len(nodes_arr) == HONEYPOT_NODES
539539

540+
@pytest.mark.parametrize("unit", ["ms", "us"])
541+
def test_hyper_to_pa_mixed2_unit_variants(self, unit):
542+
df = honeypot_pdf().copy()
543+
for col in ("time(max)", "time(min)"):
544+
df[col] = df[col].astype(f"datetime64[{unit}]")
545+
546+
hg = hypergraph(**honeypot_hyperparams(df))
547+
548+
for col in ("time(max)", "time(min)"):
549+
assert is_datetime64_any_dtype(hg.graph._edges.dtypes[col])
550+
assert is_datetime64_any_dtype(hg.graph._nodes.dtypes[col])
551+
552+
edges_arr = pa.Table.from_pandas(hg.graph._edges)
553+
assert len(edges_arr) == HONEYPOT_EDGES
554+
nodes_arr = pa.Table.from_pandas(hg.graph._nodes)
555+
assert len(nodes_arr) == HONEYPOT_NODES
556+
540557
def test_hyper_to_pa_na(self):
541558

542559
df = pd.DataFrame({"x": ["a", None, "c"], "y": [1, 2, None]})

0 commit comments

Comments
 (0)