You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[MNT]: DISCUSSION: Should Axes.grouped_bar() apply a default hatch pattern when none is provided? #30728
Currently, when no hatch argument is passed to Axes.grouped_bar(), hatching is entirely disabled:
if hatch is None:
# No hatch specified: disable hatching entirely by cycling [None].
hatches = itertools.cycle([None])
# TODO: Discussion —
# Should grouped_bar() apply a default hatch pattern (e.g., '//')
# when none is provided ?
This behavior matches the philosophy of “explicit is better than implicit,” keeping hatching opt-in.
However, there’s a possible usability question:
Should grouped_bar() automatically apply a default hatch pattern (for example '//') when multiple datasets are plotted, to help users visually distinguish bars even when colors are similar or when figures are printed in grayscale?
Discussion Points:
Would automatic hatching improve accessibility and readability (especially in black-and-white printing)?
Should the default depend on the number of datasets or color distinctness?
Follow-up to the Enh/Add hatch pattern support to Axes.grouped_bar #30726
Should grouped_bar() automatically apply a default hatch pattern (for example '//') when multiple datasets are plotted, to help users visually distinguish bars even when colors are similar or when figures are printed in grayscale?
No, it should default to no hatch because that matches the behavior of histogram and stack plot, which are the other methods that plot multiple datasets using area (bars and filled curves) respectively.
Also, this is a fundamental behavior of the functionality of the PR. It should be primarily discussed there (if there was anything left to discuss). Please do not pull related discussions aparat by excessively opening discussion issues that are in the scope of the PR (like this and #30729).
Should grouped_bar() automatically apply a default hatch pattern (for example '//') when multiple datasets are plotted, to help users visually distinguish bars even when colors are similar or when figures are printed in grayscale?
A single hatch pattern applied to all bars logically would not distinguish them.
Matplotlib automatically cycles through the property color cycle (axes.prop_cycle), giving each dataset a distinct color by default.
When colors is a single string (e.g. 'blue'):
Since strings are iterable, itertools.cycle('blue') will incorrectly iterate over the characters 'b', 'l', 'u', 'e', producing unintended results.
To apply one color to all datasets, use a single-element list, like ['blue'].
When colors is a single-element list (e.g. ['blue']):
The same color is applied to all datasets.
When colors is a sequence (e.g. ['#1f77b4', '#ff7f0e', '#2ca02c']):
The sequence is wrapped in itertools.cycle(colors).
If there are more datasets than colors, the list repeats from the beginning, cycling through the same colors again.
If there are fewer datasets than colors, only the needed colors are used; the extra colors are ignored.
This behavior matches how color lists work in bar() and stackplot().
When colors is an empty list ([]):
Treated as None, so the default property color cycle is used.
When colors is a non-iterable (e.g. 123):
A TypeError occurs since it cannot be cycled.
For hatch:
When hatch=None:
Hatching is disabled entirely (itertools.cycle([None])), making hatching explicitly opt-in.
When hatch is a single string (e.g. '//'):
Raises a ValueError:
"‘hatch’ must be a sequence of strings (e.g., ['//']) or None; a single string like '//' is not allowed."
This prevents accidental iteration over individual characters ('/', '/').
When hatch is a single-element list (e.g. ['//']):
The same hatch pattern is applied to all datasets.
Because the list is wrapped with itertools.cycle(hatch), it repeats the single entry indefinitely — so every dataset receives '//'.
This is intentional and valid, unlike passing '//' as a plain string (which raises a ValueError).
Users can use this form to apply one uniform hatch pattern across all datasets.
When hatch is a sequence of strings (e.g. ['//', 'xx', '..']):
The sequence is wrapped in itertools.cycle(hatch).
If there are more datasets than hatch patterns, the patterns repeat from the beginning, cycling through the same sequence again.
If there are fewer datasets than hatch patterns, only the first few are used; the extras are ignored.
This allows flexible per-dataset hatching without enforcing strict length matching.
When hatch is an empty list ([]):
Treated as None, disabling hatching.
When hatch is non-iterable (e.g. 123):
Raises a ValueError, since it cannot be converted into a sequence.
When hatch contains non-string entries (e.g. [1, 2, 3]):
Raises a TypeError — all entries must be strings representing valid hatch patterns.
Should grouped_bar() automatically apply a default hatch pattern (for example '//') when multiple datasets are plotted, to help users visually distinguish bars even when colors are similar or when figures are printed in grayscale?
A single hatch pattern applied to all bars logically would not distinguish them.
When hatch is a single-element list (e.g. ['//']):
The same hatch pattern is applied to all datasets.
When colors is a single-element list (e.g. ['blue']):
The same color is applied to all datasets.
So we already have this behavior -->>> A single hatch pattern applied to all bars
Also, we can also choose to provide a sequence of default hatch patterns if needed.
Also, this is a fundamental behavior of the functionality of the PR. It should be primarily discussed there (if there was anything left to discuss). Please do not pull related discussions aparat by excessively opening discussion issues that are in the scope of the PR (like this and #30729).
When colors=None:
Matplotlib automatically cycles through the property color cycle (axes.prop_cycle), giving each dataset a distinct color by default.
When colors is an empty list ([]):
Treated as None, so the default property color cycle is used.
So I wanted to mimic the same behavior for hatch too
@ilakkmanoharan I'm sorry, I won't read #30728 (comment). It's a long list of current behavior descriptions - i.e. lots of detail but no scoping and communication of intent. You've lost me here.
If you want attention, please communicate more concisely. A few well-crafted sentences can communicate a lot more. I accidentally happen to have read your last sentence in the previous post. It seems this was your overall topic, and a single sentence would have been sufficient:
Should hatching in grouped_bar follow the property-cycle, as color does.
@ilakkmanoharan I'm sorry, I won't read #30728 (comment). It's a long list of current behavior descriptions - i.e. lots of detail but no scoping and communication of intent. You've lost me here.
If you want attention, please communicate more concisely. A few well-crafted sentences can communicate a lot more. I accidentally happen to have read your last sentence in the previous post. It seems this was your overall topic, and a single sentence would have been sufficient:
Should hatching in grouped_bar follow the property-cycle, as color does.
Yes! Absolutely!! Should hatching in grouped_bar follow the property-cycle, as color does.-->>> this is what I wanted to communicate but I wanted to be more clear.
is what I wanted to communicate but I wanted to be more clear.
What's currently happening is folks are instead getting completely lost in your comments. What I suggest going forward is that you post the overview/big picture question and only provide details as/if requested. If you feel you need to provide details to get your point across, please first have one sentence explaining why you are posting these details.
Activity
story645 commentedon Nov 5, 2025
No, it should default to no hatch because that matches the behavior of histogram and stack plot, which are the other methods that plot multiple datasets using area (bars and filled curves) respectively.
timhoffm commentedon Nov 5, 2025
I've answered this already in #30726 (comment).
Also, this is a fundamental behavior of the functionality of the PR. It should be primarily discussed there (if there was anything left to discuss). Please do not pull related discussions aparat by excessively opening discussion issues that are in the scope of the PR (like this and #30729).
rcomer commentedon Nov 5, 2025
A single hatch pattern applied to all bars logically would not distinguish them.
ilakkmanoharan commentedon Nov 5, 2025
@timhoffm @story645 @rcomer
Current behavior of colors and hatch:
When colors=None:
Matplotlib automatically cycles through the property color cycle (axes.prop_cycle), giving each dataset a distinct color by default.
When colors is a single string (e.g. 'blue'):
Since strings are iterable, itertools.cycle('blue') will incorrectly iterate over the characters 'b', 'l', 'u', 'e', producing unintended results.
To apply one color to all datasets, use a single-element list, like ['blue'].
When colors is a single-element list (e.g. ['blue']):
The same color is applied to all datasets.
When colors is a sequence (e.g. ['#1f77b4', '#ff7f0e', '#2ca02c']):
The sequence is wrapped in itertools.cycle(colors).
If there are more datasets than colors, the list repeats from the beginning, cycling through the same colors again.
If there are fewer datasets than colors, only the needed colors are used; the extra colors are ignored.
This behavior matches how color lists work in bar() and stackplot().
When colors is an empty list ([]):
Treated as None, so the default property color cycle is used.
When colors is a non-iterable (e.g. 123):
A TypeError occurs since it cannot be cycled.
For hatch:
When hatch=None:
Hatching is disabled entirely (itertools.cycle([None])), making hatching explicitly opt-in.
When hatch is a single string (e.g. '//'):
Raises a ValueError:
"‘hatch’ must be a sequence of strings (e.g., ['//']) or None; a single string like '//' is not allowed."
This prevents accidental iteration over individual characters ('/', '/').
When hatch is a single-element list (e.g. ['//']):
The same hatch pattern is applied to all datasets.
Because the list is wrapped with itertools.cycle(hatch), it repeats the single entry indefinitely — so every dataset receives '//'.
This is intentional and valid, unlike passing '//' as a plain string (which raises a ValueError).
Users can use this form to apply one uniform hatch pattern across all datasets.
When hatch is a sequence of strings (e.g. ['//', 'xx', '..']):
The sequence is wrapped in itertools.cycle(hatch).
If there are more datasets than hatch patterns, the patterns repeat from the beginning, cycling through the same sequence again.
If there are fewer datasets than hatch patterns, only the first few are used; the extras are ignored.
This allows flexible per-dataset hatching without enforcing strict length matching.
When hatch is an empty list ([]):
Treated as None, disabling hatching.
When hatch is non-iterable (e.g. 123):
Raises a ValueError, since it cannot be converted into a sequence.
When hatch contains non-string entries (e.g. [1, 2, 3]):
Raises a TypeError — all entries must be strings representing valid hatch patterns.
ilakkmanoharan commentedon Nov 5, 2025
When hatch is a single-element list (e.g. ['//']):
The same hatch pattern is applied to all datasets.
When colors is a single-element list (e.g. ['blue']):
The same color is applied to all datasets.
So we already have this behavior -->>> A single hatch pattern applied to all bars
Also, we can also choose to provide a sequence of default hatch patterns if needed.
ilakkmanoharan commentedon Nov 5, 2025
When colors=None:
Matplotlib automatically cycles through the property color cycle (axes.prop_cycle), giving each dataset a distinct color by default.
When colors is an empty list ([]):
Treated as None, so the default property color cycle is used.
So I wanted to mimic the same behavior for hatch too
timhoffm commentedon Nov 5, 2025
@ilakkmanoharan I'm sorry, I won't read #30728 (comment). It's a long list of current behavior descriptions - i.e. lots of detail but no scoping and communication of intent. You've lost me here.
If you want attention, please communicate more concisely. A few well-crafted sentences can communicate a lot more. I accidentally happen to have read your last sentence in the previous post. It seems this was your overall topic, and a single sentence would have been sufficient:
ilakkmanoharan commentedon Nov 5, 2025
Yes! Absolutely!! Should hatching in grouped_bar follow the property-cycle, as color does.-->>> this is what I wanted to communicate but I wanted to be more clear.
story645 commentedon Nov 5, 2025
What's currently happening is folks are instead getting completely lost in your comments. What I suggest going forward is that you post the overview/big picture question and only provide details as/if requested. If you feel you need to provide details to get your point across, please first have one sentence explaining why you are posting these details.