Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9ac5043

Browse files
committed
Improving error message for width and position type mismatch in violinplot
1 parent c80e8ae commit 9ac5043

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datetime
12
import functools
23
import itertools
34
import logging
@@ -9047,6 +9048,20 @@ def violin(self, vpstats, positions=None, vert=None,
90479048
elif len(widths) != N:
90489049
raise ValueError(datashape_message.format("widths"))
90499050

9051+
# Proactive validation: if positions are datetime-like
9052+
# widths must be timedelta-like.
9053+
if any(isinstance(p, (datetime.datetime, datetime.date))
9054+
for p in positions):
9055+
_widths = widths if not np.isscalar(widths) else [widths] * N
9056+
if any(not isinstance(w, (datetime.timedelta, np.timedelta64))
9057+
for w in _widths):
9058+
raise TypeError(
9059+
"If positions are datetime/date values, pass widths as "
9060+
"datetime.timedelta (e.g., datetime.timedelta(days=10))"
9061+
"or numpy.timedelta64."
9062+
)
9063+
9064+
90509065
# Validate side
90519066
_api.check_in_list(["both", "low", "high"], side=side)
90529067

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"""
2+
Unit tests for proactive validation of datetime
3+
positions and timedelta widths in violinplot.
4+
"""
5+
6+
import datetime
7+
import pytest
8+
9+
import matplotlib.pyplot as plt
10+
11+
12+
def make_vpstats():
13+
"""Create minimal valid stats for a violin plot."""
14+
15+
16+
def violin_plot_stats():
17+
# Stats for violin plot
18+
datetimes = [
19+
datetime.datetime(2023, 2, 10),
20+
datetime.datetime(2023, 5, 18),
21+
datetime.datetime(2023, 6, 6)
22+
]
23+
return [{
24+
'coords': datetimes,
25+
'vals': [0.1, 0.5, 0.2],
26+
'mean': datetimes[1],
27+
'median': datetimes[1],
28+
'min': datetimes[0],
29+
'max': datetimes[-1],
30+
'quantiles': datetimes
31+
}, {
32+
'coords': datetimes,
33+
'vals': [0.2, 0.3, 0.4],
34+
'mean': datetimes[2],
35+
'median': datetimes[2],
36+
'min': datetimes[0],
37+
'max': datetimes[-1],
38+
'quantiles': datetimes
39+
}]
40+
41+
42+
def test_datetime_positions_with_float_widths_raises():
43+
"""Test that datetime positions with float widths raise TypeError."""
44+
fig, ax = plt.subplots()
45+
try:
46+
vpstats = violin_plot_stats()
47+
positions = [datetime.datetime(2020, 1, 1), datetime.datetime(2021, 1, 1)]
48+
widths = [0.5, 1.0]
49+
with pytest.raises(TypeError,
50+
match="positions are datetime/date.*widths as datetime\\.timedelta"):
51+
ax.violin(vpstats, positions=positions, widths=widths)
52+
finally:
53+
plt.close(fig)
54+
55+
56+
def test_datetime_positions_with_scalar_float_width_raises():
57+
"""Test that datetime positions with scalar float width raise TypeError."""
58+
fig, ax = plt.subplots()
59+
try:
60+
vpstats = violin_plot_stats()
61+
positions = [datetime.datetime(2020, 1, 1), datetime.datetime(2021, 1, 1)]
62+
widths = 0.75
63+
with pytest.raises(TypeError,
64+
match="positions are datetime/date.*widths as datetime\\.timedelta"):
65+
ax.violin(vpstats, positions=positions, widths=widths)
66+
finally:
67+
plt.close(fig)
68+
69+
70+
def test_numeric_positions_with_float_widths_ok():
71+
"""Test that numeric positions with float widths work."""
72+
fig, ax = plt.subplots()
73+
try:
74+
vpstats = violin_plot_stats()
75+
positions = [1.0, 2.0]
76+
widths = [0.5, 1.0]
77+
ax.violin(vpstats, positions=positions, widths=widths)
78+
finally:
79+
plt.close(fig)
80+
81+
82+
def test_mixed_positions_datetime_and_numeric_behaves():
83+
"""Test that mixed datetime and numeric positions
84+
with float widths raise TypeError.
85+
"""
86+
fig, ax = plt.subplots()
87+
try:
88+
vpstats = violin_plot_stats()
89+
positions = [datetime.datetime(2020, 1, 1), 2.0]
90+
widths = [0.5, 1.0]
91+
with pytest.raises(TypeError,
92+
match="positions are datetime/date.*widths as datetime\\.timedelta"):
93+
ax.violin(vpstats, positions=positions, widths=widths)
94+
finally:
95+
plt.close(fig)

0 commit comments

Comments
 (0)