Skip to content

Commit 5fae432

Browse files
committed
Support coercing pandas datetime DataFrames
1 parent 050dd8a commit 5fae432

File tree

3 files changed

+78
-1
lines changed

3 files changed

+78
-1
lines changed

Diff for: packages/python/plotly/_plotly_utils/basevalidators.py

+7
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ def copy_to_readonly_numpy_array(v, kind=None, force_numeric=False):
103103
else:
104104
# DatetimeIndex
105105
v = v.to_pydatetime()
106+
elif pd and isinstance(v, pd.DataFrame) and len(set(v.dtypes)) == 1:
107+
dtype = v.dtypes[0]
108+
if dtype.kind in numeric_kinds:
109+
v = v.values
110+
elif dtype.kind == "M":
111+
v = [row.dt.to_pydatetime().tolist() for i, row in v.iterrows()]
112+
106113
if not isinstance(v, np.ndarray):
107114
# v has its own logic on how to convert itself into a numpy array
108115
if is_numpy_convertable(v):

Diff for: packages/python/plotly/_plotly_utils/tests/validators/test_pandas_series_input.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def test_color_validator_categorical(color_validator, color_categorical_pandas):
173173
np.testing.assert_array_equal(res, np.array(color_categorical_pandas))
174174

175175

176-
def test_data_array_validator_dates(data_array_validator, datetime_pandas, dates_array):
176+
def test_data_array_validator_dates_series(data_array_validator, datetime_pandas, dates_array):
177177

178178
res = data_array_validator.validate_coerce(datetime_pandas)
179179

@@ -185,3 +185,18 @@ def test_data_array_validator_dates(data_array_validator, datetime_pandas, dates
185185

186186
# Check values
187187
np.testing.assert_array_equal(res, dates_array)
188+
189+
190+
def test_data_array_validator_dates_dataframe(data_array_validator, datetime_pandas, dates_array):
191+
192+
df = pd.DataFrame({"d": datetime_pandas})
193+
res = data_array_validator.validate_coerce(df)
194+
195+
# Check type
196+
assert isinstance(res, np.ndarray)
197+
198+
# Check dtype
199+
assert res.dtype == "object"
200+
201+
# Check values
202+
np.testing.assert_array_equal(res, dates_array.reshape(len(dates_array), 1))

Diff for: packages/python/plotly/plotly/tests/test_optional/test_utils/test_utils.py

+55
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,61 @@ def test_pandas_json_encoding(self):
257257
j6 = _json.dumps(ts.index, cls=utils.PlotlyJSONEncoder)
258258
assert j6 == '["2011-01-01T00:00:00", "2011-01-01T01:00:00"]'
259259

260+
def test_encode_customdata_datetime_series(self):
261+
df = pd.DataFrame(dict(t=pd.to_datetime(["2010-01-01", "2010-01-02"])))
262+
263+
# 1D customdata
264+
fig = Figure(
265+
Scatter(x=df["t"], customdata=df["t"]), layout=dict(template="none")
266+
)
267+
fig_json = _json.dumps(
268+
fig, cls=utils.PlotlyJSONEncoder, separators=(",", ":"), sort_keys=True
269+
)
270+
self.assertTrue(
271+
fig_json.startswith(
272+
'{"data":[{"customdata":["2010-01-01T00:00:00","2010-01-02T00:00:00"]'
273+
)
274+
)
275+
276+
def test_encode_customdata_datetime_homogenous_dataframe(self):
277+
df = pd.DataFrame(dict(
278+
t1=pd.to_datetime(["2010-01-01", "2010-01-02"]),
279+
t2=pd.to_datetime(["2011-01-01", "2011-01-02"]),
280+
))
281+
# 2D customdata
282+
fig = Figure(
283+
Scatter(x=df["t1"], customdata=df[["t1", "t2"]]), layout=dict(template="none")
284+
)
285+
fig_json = _json.dumps(
286+
fig, cls=utils.PlotlyJSONEncoder, separators=(",", ":"), sort_keys=True
287+
)
288+
self.assertTrue(
289+
fig_json.startswith(
290+
'{"data":[{"customdata":'
291+
'[["2010-01-01T00:00:00","2011-01-01T00:00:00"],'
292+
'["2010-01-02T00:00:00","2011-01-02T00:00:00"]'
293+
)
294+
)
295+
296+
def test_encode_customdata_datetime_inhomogenous_dataframe(self):
297+
df = pd.DataFrame(dict(
298+
t=pd.to_datetime(["2010-01-01", "2010-01-02"]),
299+
v=np.arange(2),
300+
))
301+
# 2D customdata
302+
fig = Figure(
303+
Scatter(x=df["t"], customdata=df[["t", "v"]]), layout=dict(template="none")
304+
)
305+
fig_json = _json.dumps(
306+
fig, cls=utils.PlotlyJSONEncoder, separators=(",", ":"), sort_keys=True
307+
)
308+
self.assertTrue(
309+
fig_json.startswith(
310+
'{"data":[{"customdata":'
311+
'[["2010-01-01T00:00:00",0],["2010-01-02T00:00:00",1]]'
312+
)
313+
)
314+
260315
def test_numpy_masked_json_encoding(self):
261316
l = [1, 2, np.ma.core.masked]
262317
j1 = _json.dumps(l, cls=utils.PlotlyJSONEncoder)

0 commit comments

Comments
 (0)