-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Creating_parse_bar_color_args to unify color handling in plt.bar with precedence and sequence support for facecolor and edgecolor #29133
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
Conversation
Although some tests are failing, I can't determine the cause; For example, test_bbox_inches_tight in test_bbox_tight fails, even though old/new images appear equal. |
The easiest test to debug might be |
lib/matplotlib/axes/_axes.py
Outdated
edgecolor = kwargs.pop('edgecolor', color) | ||
|
||
facecolor = (facecolor if facecolor is not None | ||
else "b" if mpl.rcParams['_internal.classic_mode'] |
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.
I guess you made "b" the default in classic mode for consistency with scatter
. However this is not the established behaviour for bar
and setting it to blue is likely the cause of at least some of your test failures. For scatter
it will only be there for consistency with old Matplotlib versions. I think we should just use the get_next_color_func
here.
lib/matplotlib/axes/_axes.py
Outdated
@@ -2320,7 +2320,59 @@ def _convert_dx(dx, x0, xconv, convert): | |||
# we do by default and convert dx by itself. | |||
dx = convert(dx) | |||
return dx | |||
|
|||
@staticmethod | |||
def _parse_bar_color_args(kwargs, get_next_color_func): |
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.
According to #12739 the method for parsing scatter
color arguments was made static so that we could have unit tests specifically for that method. Do you plan to write similar tests here? If not, this could be an ordinary method and we would not need to pass the get_next_color_func
but just access self._get_patches_for_fill.get_next_color
directly.
Edit: looking at the discussion in #12739 I think it is not advisable to write such tests but just test the public bar
function.
@rcomer thx for the review, I'll fix it as soon as possible. prob later today |
@rcomer test_bar_color_cycle passed as you said. Others still failed. |
Here are the before and after of that So it seems the edgecolor has changed from black to blue. Note you can download all the test images under "Artifacts" on the Test Summary Page. However those zip files are large so it may be more efficient to run the tests locally and inspect the images you produce that way. |
@timhoffm thx for the review, commited it. All tests in test_axes passed except for I can commit this to the PR if you think it's okay. |
@rcomer In |
Other tests fails for the same reason as both tests above. |
I do not think we really want edgecolor to match color by default: if I take the grouped bar example and
we see that the shorter bars now overlap the larger ones, which is not visually appealing. This does not happen if the edgecolor is left off |
Indeed. Currently, |
@Lucx33 please add a test for the new behaviour. |
@rcomer Is this test good enought? |
lib/matplotlib/axes/_axes.py
Outdated
@@ -2378,6 +2428,9 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", | |||
color : :mpltype:`color` or list of :mpltype:`color`, optional | |||
The colors of the bar faces. |
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 colors of the bar faces. | |
The colors of the bar faces. This is an alias for *facecolor*. | |
If both are given, *facecolor* takes precedence. |
@@ -2378,6 +2428,9 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", | |||
color : :mpltype:`color` or list of :mpltype:`color`, optional | |||
The colors of the bar faces. | |||
|
|||
facecolor : :mpltype:`color` or list of :mpltype:`color`, optional | |||
The colors of the bar faces. |
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 colors of the bar faces. | |
The colors of the bar faces. | |
If both *color* and *facecolor are given, *facecolor* takes precedence. |
lib/matplotlib/tests/test_axes.py
Outdated
for bar in bars: | ||
assert to_rgba(bar.get_facecolor()) == to_rgba('red') | ||
|
||
ax.cla() |
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.
You don't need to clear in between, which unfortunately is a relatively expensive action. Instead I'd do
# case 1: no color specified
bars = ax.bar([1, 2, 3], [4, 5, 6])
[...]
# case 2: Only 'color'
bars = ax.bar([11, 12, 13], [4, 5, 6], color='red')
[...]
# case 3: Only 'color'
bars = ax.bar([21, 22, 23], [4, 5, 6], color='red')
[...]
# case 4: 'facecolor' and 'color'
bars = ax.bar([1, 2, 3], [4, 5, 6], color='red', facecolor='green')
[...]
Putting the no color case first ensures noting is messing with the colorcycle.
Optional: You could use https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.same_color.html#matplotlib.colors.same_color for the asserts.
@timhoffm thx for the review, commited it. |
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Is there anything else I can do to help with the merge? |
No, we just need a second approval from another core developer. Note for other reviewers: The win10 test failure is the flaky determinism check and not related to this PR. |
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.
Thanks @Lucx33, I just have one small comment. Otherwise this looks good to me.
Messed up my commit by changing wrong file manually but fixed already |
@rcomer Changed blue as you said |
…color handling in plt.bar with precedence and sequence support for facecolor and edgecolor
Thanks @Lucx33 and congratulations on your first Matplotlib PR! I hope we hear from you again. |
…133-on-v3.10.x Backport PR #29133 on branch v3.10.x (Creating_parse_bar_color_args to unify color handling in plt.bar with precedence and sequence support for facecolor and edgecolor)
PR summary
This PR introduces the
_parse_bar_color_args function
, which provides consistency in color bar arguments by establishing a parameter precedence, "similar" to the one used inscatter
:facecolor: facecolor -> color -> 'b' if in classic mode else the result of
get_next_color_func()
edgecolor: edgecolor -> color -> None
This ensures that when edgecolors is not explicitly defined, it tries color. Additionally, this change allows facecolor to accept sequences of colors, ensuring consistency in argument handling, as issued in #29090. The tests are consistent with examples in #29072.
Examples:
plt.bar([1, 2, 3], [1, 2, 3], color=['none', 'C2', 'none'], edgecolor=['C1', 'C2', 'C3'])
plt.bar([1, 2, 3], [1, 2, 3], color=['none', 'C2', 'none'])
plt.bar([1, 2, 3], [1, 2, 3])
plt.bar([1, 2, 3], [1, 2, 3],facecolor=('none', 'C2', 'none'))
plt.bar([1, 2, 3], [1, 2, 3], color=('C1', 'C2', 'C3'), facecolor=('none', 'C2', 'none'))
PR checklist