diff --git a/doc/python/horizontal-bar-charts.md b/doc/python/horizontal-bar-charts.md
index 25aa9d4f7d1..dd915f72d7e 100644
--- a/doc/python/horizontal-bar-charts.md
+++ b/doc/python/horizontal-bar-charts.md
@@ -5,10 +5,10 @@ jupyter:
text_representation:
extension: .md
format_name: markdown
- format_version: '1.1'
- jupytext_version: 1.1.1
+ format_version: '1.3'
+ jupytext_version: 1.16.4
kernelspec:
- display_name: Python 3
+ display_name: Python 3 (ipykernel)
language: python
name: python3
language_info:
@@ -20,7 +20,7 @@ jupyter:
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
- version: 3.6.7
+ version: 3.11.10
plotly:
description: How to make horizontal bar charts in Python with Plotly.
display_as: basic
@@ -217,6 +217,113 @@ fig.update_layout(annotations=annotations)
fig.show()
```
+
+
+### Diverging Bar (or Butterfly) Chart with Neutral Column
+
+The other diverging bar chart example excludes neutral responses. This variation includes them in a separate column. Jonathan Schwabish discusses tradeoffs between these options on page 92-97 of _Better Data Visualizations_.
+
+```python
+import pandas as pd
+import plotly.graph_objects as go
+
+
+df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/refs/heads/master/gss_2002_5_pt_likert.csv')
+df.rename(columns={'Unnamed: 0':"Category"}, inplace=True)
+
+
+#achieve the diverging effect by putting a negative sign on the "disagree" answers
+for v in ["Disagree","Strongly Disagree"]:
+ df[v]=df[v]*-1
+
+fig = go.Figure(layout=go.Layout(
+ title="Reactions to statements from the 2002 General Social Survey:",
+ plot_bgcolor="white",
+ barmode='relative', # Allows bars to diverge from the center
+ # Put the legend at the bottom center of the figure
+ legend=dict(
+ orientation="h", # a horizontal legend matches the horizontal bars
+ yref="container",
+ yanchor="bottom",
+ y=0.02,
+ xanchor="center",
+ x=0.5),
+ # use an unlabeled Y axis, since we're going to list specific questions on the y-axis.
+ yaxis=dict(
+ title=""
+ ),
+ )
+)
+
+
+# this color palette conveys meaning: blues for agreement, reds and oranges for disagreement, gray for Neither Agree nor Disagree
+color_by_category={
+ "Strongly Agree":'darkblue',
+ "Agree":'lightblue',
+ "Disagree":'orange',
+ "Strongly Disagree":'red',
+ "Neither Agree nor Disagree":'gray',
+}
+
+
+# We want the legend to be ordered in the same order that the categories appear, left to right --
+# which is different from the order in which we have to add the traces to the figure.
+# since we need to create the "somewhat" traces before the "strongly" traces to display
+# the segments in the desired order
+
+legend_rank_by_category={
+ "Strongly Disagree":1,
+ "Disagree":2,
+ "Agree":3,
+ "Strongly Agree":4,
+ "Neither Agree nor Disagree":5
+}
+
+# Add bars
+for col in ["Disagree","Strongly Disagree","Agree","Strongly Agree","Neither Agree nor Disagree"]:
+ fig.add_trace(go.Bar(
+ y=df["Category"],
+ x=df[col],
+ name=col,
+ orientation='h',
+ marker=dict(color=color_by_category[col]),
+ legendrank=legend_rank_by_category[col],
+ xaxis=f"x{1+(col=='Neither Agree nor Disagree')}", # in this context, putting "Neither Agree nor Disagree" on a secondary x-axis on a different domain
+ # yields results equivalent to subplots with far less code
+ )
+)
+
+# make calculations to split the plot into two columns with a shared x axis scale
+# by setting the domain and range of the x axes appropriately
+
+# Find the maximum width of the bars to the left and right sides of the origin; remember that the width of
+# the plot is the sum of the longest negative bar and the longest positive bar even if they are on separate rows
+max_left = min(df[["Disagree","Strongly Disagree"]].sum(axis=1))
+max_right = max(df[["Agree","Strongly Agree"]].sum(axis=1))
+
+# we are working in percent, but coded the negative reactions as negative numbers; so we need to take the absolute value
+max_width_signed = abs(max_left)+max_right
+max_width_neither = max(df["Neither Agree nor Disagree"])
+
+fig.update_xaxes(
+ zeroline=True, #the zero line distinguishes between positive and negative segments
+ zerolinecolor="black",
+ #starting here, we set domain and range to create a shared x-axis scale
+ # multiply by .98 to add space between the two columns
+ range=[max_left, max_right],
+ domain=[0, 0.98*(max_width_signed/(max_width_signed+max_width_neither))]
+)
+
+fig.update_layout(
+ xaxis2=dict(
+ range=[0, max_width_neither],
+ domain=[(1-.98*(1-max_width_signed/(max_width_signed+max_width_neither))), 1.0],
+ )
+)
+
+fig.show()
+```
+
### Bar Chart with Line Plot
```python
@@ -260,7 +367,7 @@ fig.append_trace(go.Scatter(
), 1, 2)
fig.update_layout(
- title='Household savings & net worth for eight OECD countries',
+ title=dict(text='Household savings & net worth for eight OECD countries'),
yaxis=dict(
showgrid=False,
showline=False,
@@ -335,4 +442,4 @@ fig.show()
### Reference
-See more examples of bar charts and styling options [here](https://plotly.com/python/bar-charts/).
See https://plotly.com/python/reference/bar/ for more information and chart attribute options!
\ No newline at end of file
+See more examples of bar charts and styling options [here](https://plotly.com/python/bar-charts/).
See https://plotly.com/python/reference/bar/ for more information and chart attribute options!
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: