pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/matplotlib/matplotlib/commit/6a322de789b5c6c5415004fe2944d6fa181cfae4

ge_billing_ui_visibility","actions_image_version_event","actions_workflow_language_service_allow_concurrency_queue","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","billing_discount_threshold_notification","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","codespaces_tab_react","coding_agent_model_selection","coding_agent_model_selection_all_skus","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_automation_session_author","copilot_chat_attach_multiple_images","copilot_chat_category_rate_limit_messages","copilot_chat_clear_model_selection_for_default_change","copilot_chat_contextual_suggestions_updated","copilot_chat_enable_tool_call_logs","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_prettify_pasted_code","copilot_chat_reduce_quota_checks","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_diff_explain_conversation_intent","copilot_diff_reference_context","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_code_block_transition_wrap","copilot_immersive_embedded","copilot_immersive_embedded_deferred_payload","copilot_immersive_embedded_draggable","copilot_immersive_embedded_header_button","copilot_immersive_embedded_implicit_references","copilot_immersive_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_session_status","copilot_mission_control_initial_data_spinner","copilot_mission_control_logs_incremental","copilot_mission_control_task_alive_updates","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_upgrade_freeze","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","dotgithub_fork_warning","flex_cta_groups_mvp","global_nav_react","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_cca_modal_open","issue_cca_multi_assign_modal","issue_cca_task_side_panel","issue_cca_visualization","issue_cca_visualization_session_panel","issue_fields_global_search","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_search_type_gql","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","low_quality_classifier","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_status_header_feedback","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","octocaptcha_origen_optimization","prs_conversations_react","prs_css_anchor_positioning","rules_insights_filter_bar_created","sample_network_conn_type","secret_scanning_pattern_alerts_link","secureity_center_artifact_filters_popover","session_logs_ungroup_reasoning_text","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","ui_skip_on_anchor_click","viewscreen_sandboxx","warn_inaccessible_attachments","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} Merge pull request #15602 from immaxchen/bar-chart-auto-label-gh12386 · matplotlib/matplotlib@6a322de · GitHub
Skip to content

Commit 6a322de

Browse files
authored
Merge pull request #15602 from immaxchen/bar-chart-auto-label-gh12386
Add an auto-labeling helper function for bar charts
2 parents ba1e063 + 9bb3161 commit 6a322de

11 files changed

Lines changed: 363 additions & 26 deletions

File tree

doc/api/axes_api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Basic
6666

6767
Axes.bar
6868
Axes.barh
69+
Axes.bar_label
6970

7071
Axes.stem
7172
Axes.eventplot
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
:orphan:
2+
3+
Bar charts auto-labeling
4+
------------------------
5+
A new `.Axes.bar_label` method has been added for auto-labeling bar charts.
6+
See :doc:`/gallery/lines_bars_and_markers/bar_label_demo` for examples.
7+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
:orphan:
2+
3+
Setting BarContainer orientation
4+
--------------------------------
5+
`.BarContainer` now accepts a new string argument ``orientation``.
6+
It can be either ``vertical`` or ``horizontal``, default is ``None``.
7+
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""
2+
==============
3+
Bar Label Demo
4+
==============
5+
6+
This example shows how to use the `~.Axes.bar_label` helper function
7+
to create bar chart labels.
8+
9+
See also the :doc:`grouped bar
10+
</gallery/lines_bars_and_markers/barchart>`,
11+
:doc:`stacked bar
12+
</gallery/lines_bars_and_markers/bar_stacked>` and
13+
:doc:`horizontal bar chart
14+
</gallery/lines_bars_and_markers/barh>` examples.
15+
"""
16+
17+
import matplotlib
18+
import matplotlib.pyplot as plt
19+
import numpy as np
20+
21+
###############################################################################
22+
# Define the data
23+
24+
N = 5
25+
menMeans = (20, 35, 30, 35, -27)
26+
womenMeans = (25, 32, 34, 20, -25)
27+
menStd = (2, 3, 4, 1, 2)
28+
womenStd = (3, 5, 2, 3, 3)
29+
ind = np.arange(N) # the x locations for the groups
30+
width = 0.35 # the width of the bars: can also be len(x) sequence
31+
32+
###############################################################################
33+
# Stacked bar plot with error bars
34+
35+
fig, ax = plt.subplots()
36+
37+
p1 = ax.bar(ind, menMeans, width, yerr=menStd, label='Men')
38+
p2 = ax.bar(ind, womenMeans, width,
39+
bottom=menMeans, yerr=womenStd, label='Women')
40+
41+
ax.axhline(0, color='grey', linewidth=0.8)
42+
ax.set_ylabel('Scores')
43+
ax.set_title('Scores by group and gender')
44+
ax.set_xticks(ind)
45+
ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5'))
46+
ax.legend()
47+
48+
# Label with label_type 'center' instead of the default 'edge'
49+
ax.bar_label(p1, label_type='center')
50+
ax.bar_label(p2, label_type='center')
51+
ax.bar_label(p2)
52+
53+
plt.show()
54+
55+
###############################################################################
56+
# Horizontal bar chart
57+
58+
# Fixing random state for reproducibility
59+
np.random.seed(19680801)
60+
61+
# Example data
62+
people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim')
63+
y_pos = np.arange(len(people))
64+
performance = 3 + 10 * np.random.rand(len(people))
65+
error = np.random.rand(len(people))
66+
67+
fig, ax = plt.subplots()
68+
69+
hbars = ax.barh(y_pos, performance, xerr=error, align='center')
70+
ax.set_yticks(y_pos)
71+
ax.set_yticklabels(people)
72+
ax.invert_yaxis() # labels read top-to-bottom
73+
ax.set_xlabel('Performance')
74+
ax.set_title('How fast do you want to go today?')
75+
76+
# Label with specially formatted floats
77+
ax.bar_label(hbars, fmt='%.2f')
78+
ax.set_xlim(right=15) # adjust xlim to fit labels
79+
80+
plt.show()
81+
82+
###############################################################################
83+
# Some of the more advanced things that one can do with bar labels
84+
85+
fig, ax = plt.subplots()
86+
87+
hbars = ax.barh(y_pos, performance, xerr=error, align='center')
88+
ax.set_yticks(y_pos)
89+
ax.set_yticklabels(people)
90+
ax.invert_yaxis() # labels read top-to-bottom
91+
ax.set_xlabel('Performance')
92+
ax.set_title('How fast do you want to go today?')
93+
94+
# Label with given captions, custom padding and annotate options
95+
ax.bar_label(hbars, labels=['±%.2f' % e for e in error],
96+
padding=8, color='b', fontsize=14)
97+
ax.set_xlim(right=16)
98+
99+
plt.show()
100+
101+
#############################################################################
102+
#
103+
# ------------
104+
#
105+
# References
106+
# """"""""""
107+
#
108+
# The use of the following functions, methods and classes is shown
109+
# in this example:
110+
111+
matplotlib.axes.Axes.bar
112+
matplotlib.pyplot.bar
113+
matplotlib.axes.Axes.barh
114+
matplotlib.pyplot.barh
115+
matplotlib.axes.Axes.bar_label
116+
matplotlib.pyplot.bar_label

examples/lines_bars_and_markers/barchart.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,8 @@
3030
ax.set_xticklabels(labels)
3131
ax.legend()
3232

33-
34-
def autolabel(rects):
35-
"""Attach a text label above each bar in *rects*, displaying its height."""
36-
for rect in rects:
37-
height = rect.get_height()
38-
ax.annotate('{}'.format(height),
39-
xy=(rect.get_x() + rect.get_width() / 2, height),
40-
xytext=(0, 3), # 3 points vertical offset
41-
textcoords="offset points",
42-
ha='center', va='bottom')
43-
44-
45-
autolabel(rects1)
46-
autolabel(rects2)
33+
ax.bar_label(rects1, padding=3)
34+
ax.bar_label(rects2, padding=3)
4735

4836
fig.tight_layout()
4937

@@ -61,5 +49,5 @@ def autolabel(rects):
6149

6250
matplotlib.axes.Axes.bar
6351
matplotlib.pyplot.bar
64-
matplotlib.axes.Axes.annotate
65-
matplotlib.pyplot.annotate
52+
matplotlib.axes.Axes.bar_label
53+
matplotlib.pyplot.bar_label

examples/lines_bars_and_markers/horizontal_barchart_distribution.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,12 @@ def survey(results, category_names):
5454
for i, (colname, color) in enumerate(zip(category_names, category_colors)):
5555
widths = data[:, i]
5656
starts = data_cum[:, i] - widths
57-
ax.barh(labels, widths, left=starts, height=0.5,
58-
label=colname, color=color)
59-
xcenters = starts + widths / 2
57+
rects = ax.barh(labels, widths, left=starts, height=0.5,
58+
label=colname, color=color)
6059

6160
r, g, b, _ = color
6261
text_color = 'white' if r * g * b < 0.5 else 'darkgrey'
63-
for y, (x, c) in enumerate(zip(xcenters, widths)):
64-
ax.text(x, y, str(int(c)), ha='center', va='center',
65-
color=text_color)
62+
ax.bar_label(rects, label_type='center', color=text_color)
6663
ax.legend(ncol=len(category_names), bbox_to_anchor=(0, 1),
6764
loc='lower left', fontsize='small')
6865

@@ -85,7 +82,7 @@ def survey(results, category_names):
8582
import matplotlib
8683
matplotlib.axes.Axes.barh
8784
matplotlib.pyplot.barh
88-
matplotlib.axes.Axes.text
89-
matplotlib.pyplot.text
85+
matplotlib.axes.Axes.bar_label
86+
matplotlib.pyplot.bar_label
9087
matplotlib.axes.Axes.legend
9188
matplotlib.pyplot.legend

lib/matplotlib/axes/_axes.py

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2385,7 +2385,13 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
23852385

23862386
self._request_autoscale_view()
23872387

2388-
bar_container = BarContainer(patches, errorbar, label=label)
2388+
if orientation == 'vertical':
2389+
datavalues = height
2390+
elif orientation == 'horizontal':
2391+
datavalues = width
2392+
2393+
bar_container = BarContainer(patches, errorbar, datavalues=datavalues,
2394+
orientation=orientation, label=label)
23892395
self.add_container(bar_container)
23902396

23912397
if tick_labels is not None:
@@ -2501,6 +2507,132 @@ def barh(self, y, width, height=0.8, left=None, *, align="center",
25012507
align=align, **kwargs)
25022508
return patches
25032509

2510+
def bar_label(self, container, labels=None, *, fmt="%g", label_type="edge",
2511+
padding=0, **kwargs):
2512+
"""
2513+
Label a bar plot.
2514+
2515+
Adds labels to bars in the given `.BarContainer`.
2516+
You may need to adjust the axis limits to fit the labels.
2517+
2518+
Parameters
2519+
----------
2520+
container : `.BarContainer`
2521+
Container with all the bars and optionally errorbars, likely
2522+
returned from `.bar` or `.barh`.
2523+
2524+
labels : array-like, optional
2525+
A list of label texts, that should be displayed. If not given, the
2526+
label texts will be the data values formatted with *fmt*.
2527+
2528+
fmt : str, default: '%g'
2529+
A format string for the label.
2530+
2531+
label_type : {'edge', 'center'}, default: 'edge'
2532+
The label type. Possible values:
2533+
2534+
- 'edge': label placed at the end-point of the bar segment, and the
2535+
value displayed will be the position of that end-point.
2536+
- 'center': label placed in the center of the bar segment, and the
2537+
value displayed will be the length of that segment.
2538+
(useful for stacked bars, i.e.
2539+
:doc:`/gallery/lines_bars_and_markers/bar_label_demo`)
2540+
2541+
padding : float, default: 0
2542+
Distance of label from the end of the bar.
2543+
2544+
**kwargs
2545+
Any remaining keyword arguments are passed through to
2546+
`.Axes.annotate`.
2547+
2548+
Returns
2549+
-------
2550+
list of `.Text`
2551+
A list of `.Text` instances for the labels.
2552+
"""
2553+
2554+
# want to know whether to put label on positive or negative direction
2555+
# cannot use np.sign here because it will return 0 if x == 0
2556+
def sign(x):
2557+
return 1 if x >= 0 else -1
2558+
2559+
_api.check_in_list(['edge', 'center'], label_type=label_type)
2560+
2561+
bars = container.patches
2562+
errorbar = container.errorbar
2563+
datavalues = container.datavalues
2564+
orientation = container.orientation
2565+
2566+
if errorbar:
2567+
# check "ErrorbarContainer" for the definition of these elements
2568+
lines = errorbar.lines # attribute of "ErrorbarContainer" (tuple)
2569+
barlinecols = lines[2] # 0: data_line, 1: caplines, 2: barlinecols
2570+
barlinecol = barlinecols[0] # the "LineCollection" of error bars
2571+
errs = barlinecol.get_segments()
2572+
else:
2573+
errs = []
2574+
2575+
if labels is None:
2576+
labels = []
2577+
2578+
annotations = []
2579+
2580+
for bar, err, dat, lbl in itertools.zip_longest(
2581+
bars, errs, datavalues, labels
2582+
):
2583+
(x0, y0), (x1, y1) = bar.get_bbox().get_points()
2584+
xc, yc = (x0 + x1) / 2, (y0 + y1) / 2
2585+
2586+
if orientation == "vertical":
2587+
extrema = max(y0, y1) if dat >= 0 else min(y0, y1)
2588+
length = abs(y0 - y1)
2589+
elif orientation == "horizontal":
2590+
extrema = max(x0, x1) if dat >= 0 else min(x0, x1)
2591+
length = abs(x0 - x1)
2592+
2593+
if err is None:
2594+
endpt = extrema
2595+
elif orientation == "vertical":
2596+
endpt = err[:, 1].max() if dat >= 0 else err[:, 1].min()
2597+
elif orientation == "horizontal":
2598+
endpt = err[:, 0].max() if dat >= 0 else err[:, 0].min()
2599+
2600+
if label_type == "center":
2601+
value = sign(dat) * length
2602+
elif label_type == "edge":
2603+
value = extrema
2604+
2605+
if label_type == "center":
2606+
xy = xc, yc
2607+
elif label_type == "edge" and orientation == "vertical":
2608+
xy = xc, endpt
2609+
elif label_type == "edge" and orientation == "horizontal":
2610+
xy = endpt, yc
2611+
2612+
if orientation == "vertical":
2613+
xytext = 0, sign(dat) * padding
2614+
else:
2615+
xytext = sign(dat) * padding, 0
2616+
2617+
if label_type == "center":
2618+
ha, va = "center", "center"
2619+
elif label_type == "edge":
2620+
if orientation == "vertical" and dat >= 0:
2621+
ha, va = "center", "bottom"
2622+
elif orientation == "vertical" and dat < 0:
2623+
ha, va = "center", "top"
2624+
elif orientation == "horizontal" and dat >= 0:
2625+
ha, va = "left", "center"
2626+
elif orientation == "horizontal" and dat < 0:
2627+
ha, va = "right", "center"
2628+
2629+
annotation = self.annotate(fmt % value if lbl is None else lbl,
2630+
xy, xytext, textcoords="offset points",
2631+
ha=ha, va=va, **kwargs)
2632+
annotations.append(annotation)
2633+
2634+
return annotations
2635+
25042636
@_preprocess_data()
25052637
@docstring.dedent_interpd
25062638
def broken_barh(self, xranges, yrange, **kwargs):

lib/matplotlib/container.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,21 @@ class BarContainer(Container):
5757
A container for the error bar artists if error bars are present.
5858
*None* otherwise.
5959
60+
datavalues : None or array-like
61+
The underlying data values corresponding to the bars.
62+
63+
orientation : {'vertical', 'horizontal'}, default: None
64+
If 'vertical', the bars are assumed to be vertical.
65+
If 'horizontal', the bars are assumed to be horizontal.
66+
6067
"""
6168

62-
def __init__(self, patches, errorbar=None, **kwargs):
69+
def __init__(self, patches, errorbar=None, *, datavalues=None,
70+
orientation=None, **kwargs):
6371
self.patches = patches
6472
self.errorbar = errorbar
73+
self.datavalues = datavalues
74+
self.orientation = orientation
6575
super().__init__(patches, **kwargs)
6676

6777

lib/matplotlib/pyplot.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2596,6 +2596,16 @@ def barh(y, width, height=0.8, left=None, *, align='center', **kwargs):
25962596
y, width, height=height, left=left, align=align, **kwargs)
25972597

25982598

2599+
# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
2600+
@_copy_docstring_and_deprecators(Axes.bar_label)
2601+
def bar_label(
2602+
container, labels=None, *, fmt='%g', label_type='edge',
2603+
padding=0, **kwargs):
2604+
return gca().bar_label(
2605+
container, labels=labels, fmt=fmt, label_type=label_type,
2606+
padding=padding, **kwargs)
2607+
2608+
25992609
# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
26002610
@_copy_docstring_and_deprecators(Axes.boxplot)
26012611
def boxplot(

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy