-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Example 2 for Butterfly chart (version2) #4984
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
Open
SimaRaha
wants to merge
13
commits into
plotly:doc-prod
Choose a base branch
from
SimaRaha:patch-3
base: doc-prod
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 11 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
fbc94a2
Added plotly-universities-1.ipynb for visualization updates
SimaRaha f386c0b
butterfly with neutral column
rl-utility-man 911b993
Delete scratchwork file
rl-utility-man 40cd4c8
replaced hard coded data with a read_csv from github
rl-utility-man 70b7f49
improved the comments.
rl-utility-man 886078f
add python decorator
rl-utility-man d5e2284
corrected quotation mark type
rl-utility-man be52c35
replace an update_layout call with a go.figure() call
rl-utility-man 2140af6
moved yaxis title and legned layout to go.Figure()
rl-utility-man a14f4cb
introductory text edit.
rl-utility-man 8d0a2eb
attempted to manually merge the latest changes to doc-prod so the new…
rl-utility-man 3c4c096
removed the paste of the first diverging bar example
rl-utility-man 22c6434
edited introductory text to be order independent
rl-utility-man File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,182 @@ fig.update_layout(annotations=annotations) | |
| fig.show() | ||
| ``` | ||
|
|
||
| ### Diverging Bar (or Butterfly) Chart | ||
|
|
||
| Diverging bar charts show counts of positive outcomes or sentiments to the right of zero and counts of negative outcomes to the left of zero, allowing the reader to easily spot areas of excellence and concern. This example allows the reader of the graph to infer the number of people offering a neutral response because the neutral category, which is left implicit, would make the responses add to 100%. | ||
|
|
||
| ```python | ||
| import plotly.graph_objects as go | ||
| import pandas as pd | ||
|
|
||
|
|
||
| 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() | ||
| # this color palette conveys meaning: blues for positive, red and orange for negative | ||
| color_by_category={ | ||
| "Strongly Agree":'darkblue', | ||
| "Agree":'lightblue', | ||
| "Disagree":'orange', | ||
| "Strongly Disagree":'red', | ||
| } | ||
|
|
||
|
|
||
| # 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, | ||
| } | ||
| # Add bars for each category | ||
| for col in ["Disagree","Strongly Disagree","Agree","Strongly Agree"]: | ||
| 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] | ||
| )) | ||
|
|
||
| fig.update_layout( | ||
| title="Reactions to statements from the 2002 General Social Survey:", | ||
| yaxis_title = "", | ||
| barmode='relative', # Allows bars to diverge from the center | ||
| plot_bgcolor="white", | ||
| ) | ||
|
|
||
| fig.update_xaxes( | ||
| title="Percent of Responses", | ||
| zeroline=True, # Ensure there's a zero line for divergence | ||
| zerolinecolor="black", | ||
| # use array tick mode to show that the counts to the left of zero are still positive. | ||
| # this is hard coded; generalize this if you plan to create a function that takes unknown or widely varying data | ||
| tickmode = 'array', | ||
| tickvals = [-50, 0, 50, 100], | ||
| ticktext = [50, 0, 50, 100] | ||
| ) | ||
|
|
||
| fig.show() | ||
|
|
||
| ``` | ||
|
|
||
|
|
||
| ### Diverging Bar (or Butterfly) Chart with Neutral Column | ||
|
|
||
| The previous diverging bar chart example excluded 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 | ||
|
Comment on lines
+268
to
+292
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The example doesn't run for me, and I believe it's related to setting of the xaxis here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I had " where I needed ' . Fixed. Thank you! |
||
| ) | ||
| ) | ||
|
|
||
| # 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 +436,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, | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Is there a reason to rename the column here rather in the dataset? Is that just how the dataset was?
Uh oh!
There was an error while loading. Please reload this page.
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.
It's a shortcoming of the data set. I just proposed a PR to label the column properly in the data set plotly/datasets#64 A search of github shows no uses of that data set other than in this PR and #4994, so it appears safe to accept that PR. (I uploaded this data set recently in plotly/datasets#62 ) As soon as you merge plotly/datasets#64 , we can remove the rename commands from this and from #4994