-
-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Improving error message for width and position type mismatch in violinplot #30752
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7b44fcb
4d9c8cd
555f4d5
c40820b
2cb8aba
8ff4f12
b0862b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |||||||
| import itertools | ||||||||
| import logging | ||||||||
| import math | ||||||||
| import datetime | ||||||||
| from numbers import Integral, Number, Real | ||||||||
|
|
||||||||
| import re | ||||||||
|
|
@@ -9057,6 +9058,19 @@ def violin(self, vpstats, positions=None, vert=None, | |||||||
| elif len(widths) != N: | ||||||||
| raise ValueError(datashape_message.format("widths")) | ||||||||
|
|
||||||||
| # For usability / better error message: | ||||||||
| # Validate that datetime-like positions have timedelta-like widths. | ||||||||
| # Checking only the first element is good enough for standard misuse cases | ||||||||
| pos0 = positions[0] | ||||||||
| width0 = widths if np.isscalar(widths) else widths[0] | ||||||||
|
||||||||
| if (isinstance(pos0, (datetime.datetime, datetime.date)) | ||||||||
| and not isinstance(width0, datetime.timedelta)): | ||||||||
| raise TypeError( | ||||||||
| "datetime/date 'position' values, require timedelta 'widths'") | ||||||||
|
||||||||
| "datetime/date 'position' values, require timedelta 'widths'") | |
| "datetime/date 'position' values require timedelta 'widths'. " | |
| "For example, use positions=[datetime.date(2024, 1, 1)] and widths=[datetime.timedelta(days=1)].") |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message "np.datetime64 'position' values, require np.timedelta64 'widths'" has the same grammatical issue as the previous error message. The comma after "values" creates a comma splice. Consider removing the comma or restructuring the message for better clarity and grammar.
| "np.datetime64 'position' values, require np.timedelta64 'widths'") | |
| "np.datetime64 'position' values require np.timedelta64 'widths'") |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -4121,6 +4121,81 @@ def test_violinplot_sides(): | |||||||
| showextrema=True, showmedians=True, side=side) | ||||||||
|
|
||||||||
|
|
||||||||
| def violin_plot_stats(): | ||||||||
| datetimes = [ | ||||||||
| datetime.datetime(2023, 2, 10), | ||||||||
| datetime.datetime(2023, 5, 18), | ||||||||
| datetime.datetime(2023, 6, 6) | ||||||||
| ] | ||||||||
| return [{ | ||||||||
| 'coords': datetimes, | ||||||||
| 'vals': [1.2, 2.8, 1.5], | ||||||||
| 'mean': 1.84, | ||||||||
| 'median': 1.5, | ||||||||
| 'min': 1.2, | ||||||||
| 'max': 2.8, | ||||||||
| 'quantiles': [1.2, 1.5, 2.8] | ||||||||
| }, { | ||||||||
| 'coords': datetimes, | ||||||||
| 'vals': [0.8, 1.1, 0.9], | ||||||||
| 'mean': 0.94, | ||||||||
| 'median': 0.9, | ||||||||
| 'min': 0.8, | ||||||||
| 'max': 1.1, | ||||||||
| 'quantiles': [0.8, 0.9, 1.1] | ||||||||
| }] | ||||||||
|
|
||||||||
|
|
||||||||
| def test_datetime_positions_with_datetime64(): | ||||||||
| """Test that datetime positions with float widths raise TypeError.""" | ||||||||
| fig, ax = plt.subplots() | ||||||||
| positions = [np.datetime64('2020-01-01'), np.datetime64('2021-01-01')] | ||||||||
| widths = [0.5, 1.0] | ||||||||
| with pytest.raises(TypeError, | ||||||||
| match="np.datetime64 'position' values, require np.timedelta64 'widths'"): | ||||||||
|
Comment on lines
+4154
to
+4155
|
||||||||
| ax.violin(violin_plot_stats(), positions=positions, widths=widths) | ||||||||
|
|
||||||||
|
|
||||||||
| def test_datetime_positions_with_float_widths_raises(): | ||||||||
| """Test that datetime positions with float widths raise TypeError.""" | ||||||||
| fig, ax = plt.subplots() | ||||||||
| positions = [datetime.datetime(2020, 1, 1), datetime.datetime(2021, 1, 1)] | ||||||||
| widths = [0.5, 1.0] | ||||||||
| with pytest.raises(TypeError, | ||||||||
| match="datetime/date 'position' values, require timedelta 'widths'"): | ||||||||
|
Comment on lines
+4164
to
+4165
|
||||||||
| ax.violin(violin_plot_stats(), positions=positions, widths=widths) | ||||||||
|
|
||||||||
|
|
||||||||
| def test_datetime_positions_with_scalar_float_width_raises(): | ||||||||
| """Test that datetime positions with scalar float width raise TypeError.""" | ||||||||
| fig, ax = plt.subplots() | ||||||||
| positions = [datetime.datetime(2020, 1, 1), datetime.datetime(2021, 1, 1)] | ||||||||
| widths = 0.75 | ||||||||
| with pytest.raises(TypeError, | ||||||||
| match="datetime/date 'position' values, require timedelta 'widths'"): | ||||||||
|
Comment on lines
+4174
to
+4175
|
||||||||
| ax.violin(violin_plot_stats(), positions=positions, widths=widths) | ||||||||
|
|
||||||||
|
|
||||||||
| def test_numeric_positions_with_float_widths_ok(): | ||||||||
| """Test that numeric positions with float widths work.""" | ||||||||
| fig, ax = plt.subplots() | ||||||||
| positions = [1.0, 2.0] | ||||||||
| widths = [0.5, 1.0] | ||||||||
| ax.violin(violin_plot_stats(), positions=positions, widths=widths) | ||||||||
|
||||||||
| ax.violin(violin_plot_stats(), positions=positions, widths=widths) | |
| ax.violin(violin_plot_stats(), positions=positions, widths=widths) | |
| plt.close(fig) |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test function name test_mixed_positions_datetime_and_numeric_behaves is unclear. The suffix "behaves" doesn't indicate what behavior is being tested. Consider renaming to test_mixed_positions_datetime_and_numeric_raises to be consistent with the other test names in this group and clearly indicate that this test expects an error to be raised.
| def test_mixed_positions_datetime_and_numeric_behaves(): | |
| def test_mixed_positions_datetime_and_numeric_raises(): |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The match string for pytest.raises is split across two lines without proper line continuation formatting. This could lead to issues if formatting changes or the line is refactored. Consider using proper line continuation with parentheses or joining into a single line for better maintainability and consistency with pytest best practices.
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While these tests verify that incorrect combinations of datetime positions with float widths raise errors, there's no test for the successful case where datetime positions are paired with timedelta widths (or np.datetime64 with np.timedelta64). Add positive test cases to ensure that the correct usage actually works and doesn't raise an error, similar to test_numeric_positions_with_float_widths_ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accessing
positions[0]without checking if positions is non-empty could raise an IndexError. While the code validates thatlen(positions) == Non line 9052, ifN(which equalslen(vpstats)) is 0, positions could be an empty list. Consider adding a check for empty positions/vpstats or documenting that vpstats must be non-empty.