How to get rid of the background horizontal lines on this chart? The lines that represent the scales 5, 10, 15, etc. You can find the code below:
# Plotting waterfall chart for Years of Experience lost on DA Team
x_data = ['A', 'B', 'C', 'D', 'E']
y_data = [13, 23.5, 17.5, 10, 2.5] # y_data for positioning the annotations
text = ['27', '7', '5', '10', '5']
# Base
Base = go.Bar(x=x_data, y=[0, 20, 15, 5, 0], marker=dict(color='rgba(1,1,1, 0.0)',))
# Total
Total = go.Bar(x=x_data, y=[27, 0, 0, 0, 0], marker=dict(color='#003A6F',line=dict(color='k',width=1,)))
# Individuals
Individuals = go.Bar(x=x_data, y=[0, 7, 5, 10, 0], marker=dict( color='#FFE512',line=dict(color='k',width=1,)))
# Years of Experience Left
Years_left = go.Bar(x=x_data, y=[0, 0, 0, 0, 5], marker=dict(color='00AB39',line=dict(color='k',width=1,)))
# Put all traces in one "data" list
data = [Base, Total, Individuals, Years_left]
# Layout takes care of things like chart title, x and y axis titles and font sizes, etc.
layout = go.Layout(
title='Chart One',
barmode='stack',
yaxis=dict(title='Number of Years', titlefont=dict(size=yaxis_font_size)
, tickfont=dict(size=yaxis_font_size)),
xaxis=dict(title='Names', titlefont=dict(size=xaxis_font_size)
, tickfont=dict(size=yaxis_font_size)) ,
showlegend=False
)
annotations = []
annotations_colors = ['rgba(245, 246, 249, 1)', 'k', 'k', 'k', 'rgba(245, 246, 249, 1)'] # assign colors to annotations
for i in range(0, 5):
annotations.append(dict(x=x_data[i], y=y_data[i], text=text[i], font=dict(family='Arial', size=14, color=annotations_colors[i]), showarrow=False,))
layout['annotations'] = annotations
fig = go.Figure(data=data, layout=layout) # Standard plotly way to assign data and layout
iplot(fig, filename='Chart One')
Thanks!
Enlightening examples can be found at https://plot.ly/python/axes/
Simply add showgrid=False to your yaxis and xaxis dictionaries.
All the dict options for your xaxis can be found at https://plot.ly/python/reference/#layout-xaxis (and the yaxis is similarly at https://plot.ly/python/reference/#layout-yaxis)
we can update layout using fig.update_layout() method
by setting showgrid = False for both xaxis and yaxis parameters.
fig.update_layout(xaxis=dict(showgrid=False),
yaxis=dict(showgrid=False)
)
or
we can directly use fig.update_xaxes() and fig.update_yaxes() methods and assign showgrid=False.
fig.update_xaxes(showgrid=False)
fig.update_yaxes(showgrid=False)
Related
I am producing horizontal stacked bar charts via plotly graph objects in python, and have one issue that I can not figure out:
The lines at the right side of the positive bar are overridden by a negative line, even when the negative value is zero.
I would prefer for a blue line to surround the positive bars, and an orange line to surround the negative bars. This works as intended until a particular group has no negative value, then an orange line appears in the right side of the plot.
I have provided enough code to reproduce the attached image as an example, and have also provided a figure which highlights the problem area (the line circled should be blue when the value of "negative" is zero).
import plotly.graph_objects as go
import plotly.io as pio
pio.renderers.default='svg'
top_words = ['State', 'Opportunities', 'Information', 'Members', 'Enterprise', 'Affiliate', 'Networking', 'Common', 'Goals', 'Process', 'Monthly', 'Level']
neg_words = [0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0]
pos_words = [6, 5, 3, 3, 3, 3, 3, 1, 1, 2, 2, 2]
font_family = "sans-serif"
font_size = 17
fig5 = go.Figure()
fig5.add_bar(
y=top_words,
x=pos_words,
name='Positive',
orientation='h',
marker=dict(
color='rgba(105, 149, 183, .35)',
line=dict(color='rgba(105, 149, 183, 1)', width=2)
)
)
fig5.add_bar(
y=top_words,
x=neg_words,
name='Negative',
orientation='h',
marker=dict(
color='rgba(234, 115, 11, .35)',
line=dict(color='rgba(234, 115, 11, 1)', width=2)
)
)
fig5.update_layout(barmode='stack')
fig5.update_yaxes(autorange="reversed")
fig5.update_layout(
autosize=False,
height=800,
width=650,
template='none',
yaxis=dict(
tickfont_family=font_family,
tickfont_size=font_size,
tickfont_color="#4B4D4B"
),
xaxis=dict(
tickfont_family=font_family,
tickfont_size=font_size,
tickfont_color="#4B4D4B"
),
legend=dict(
font_size=font_size,
font_family=font_family
),
margin_pad=10,
margin=dict(l=135, r=0, t=20, b=45)
)
fig5.update_xaxes(showline=True, linewidth=.2, linecolor='lightgray', mirror=True)
fig5.show()
Thank you in advance for the help, I really tried to figure this out on my own by reading plotly documentation and scouring through stack overflow, but could not find anything on this exact topic.
If instead of 0 you write float('nan') (or math.nan after importing math) you will not get a red line. I've adjusted this for the negative value of "Information" only to demonstrate:
import plotly.graph_objects as go
import plotly.io as pio
pio.renderers.default='svg'
top_words = ['State', 'Opportunities', 'Information', 'Members', 'Enterprise', 'Affiliate', 'Networking', 'Common', 'Goals', 'Process', 'Monthly', 'Level']
neg_words = [0, 1, float("nan"), 0, 0, 0, 0, 1, 1, 0, 0, 0]
pos_words = [6, 5, 3, 3, 3, 3, 3, 1, 1, 2, 2, 2]
... (rest stays the same)
The output is:
I've been struggling with this seemingly simple task: How to align two x axis with related data. In my case one axis is in Celsius and the other in Fahrenheit.
What I want to achieve is to obtain alignment of the two x axis so that:
32°F = 0°C
And
50°F = 10°C
With this relation, the two datasets will be aligned in terms of temperature.
I want to have both unit sets on the same graph so that the viewer can interpret the data according to the units they are used to.
Here is my code:
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from plotly.graph_objs.layout import YAxis,XAxis,Margin
layout = go.Layout(
title="Double X Axis Example",
xaxis=XAxis(
title="Celcius"
),
xaxis2 = XAxis(
title="Fahrenheits",
overlaying= 'x',
side= 'top',
),
yaxis=dict(
title="Y values"
),
)
# Create figure with secondary x-axis
fig = go.Figure(layout=layout)
# Add traces
fig.add_trace(
go.Scatter(x=[10, 20, 30], y=[4.5, 6, 5], name="data set in celcius"),
)
fig.add_trace(
go.Scatter(x=[40, 60, 80], y=[4, 5, 6.5], name="data set in fahrenheit", xaxis='x2'),
)
fig.show()
Here is the resulting figure with the unaligned axes (10°C = 40°F !?):
Thank you,
In this case it might help to set the ranges for the x-axes, something like this:
fig.add_trace(
go.Scatter(x=[10, 20, 30], y=[4.5, 6, 5,], name="data set in celcius",xaxis="x1"),
)
fig.add_trace(
go.Scatter(x=[40, 60, 80], y=[4, 5, 6.5], name="data set in fahrenheit", xaxis='x2'),
)
fig.update_layout(
xaxis1=dict(range=[0, 100]),
xaxis2=dict(range=[32, 212]),
)
...possibly calculating the limit needed of x1 and then base x2 limit on that.
This is my solution and code to your concern. Here, I set the range of the first and second x axes to [0, 100] and [32, 212], respectively. To align the two axes, I made 26 tick marks for both axes and they are aligned because of the equal number of tick marks. Having an equal number of tick marks for both axes (and equal ranges) is crucial so that the aligned numbers are actually equal. Assuming that most data sets that will be plotted are between 0 and 100 degrees Celsius (for data in Celsius) --- or 32 and 212 degrees Fahrenheit (for data in Fahrenheit) --- I believe this solution overflows the data and the traces won't cover the full x ranges. Plot of the graph here.
import numpy as np
import plotly.graph_objects as go
arr1 = np.array([10, 20, 30])
arr2 = np.array([4.5, 6, 5])
arr3 = np.array([40, 60, 80])
arr4 = np.array([4, 5, 6.5])
fig = go.Figure(go.Scatter( x=arr1, y=arr2, name='data set in celsius' ) )
fig.add_trace(go.Scatter( x=arr3, y=arr4, xaxis='x2', name='data set in fahrenheit' ))
fig.update_layout(title_text='Double X Axis Example',
legend=dict(yanchor='top', y=0.875, xanchor='right', x=1),
yaxis=dict(domain = [0.05, 0.875], title='Y values', spikemode='toaxis', spikesnap='cursor'), template='plotly_dark',
xaxis =dict(position = 0, title='Celsius', spikemode='across', spikesnap='cursor',
tickmode='array', tickvals=np.linspace(0,100,26), range=[0,100]),
xaxis2=dict(position = 0.9, title='Fahrenheit', anchor='free', overlaying='x', side='top', tickmode='array',
tickvals=np.linspace(32,212,26), range=[32,212], spikemode='across', spikesnap='cursor' )
)
fig.show()
I have a plotly bar graph. The measurements illustrated by the graph are not directly adjacent; there is space between them. I'd like to fill the space between measurements, making them the same value as the previous measurement. Is this possible with plotly?
Edit for clarification: Let's say I have these measurements: [ 20=3, 25=3, 27=3, 30=10, 31=10, 50=2, 56=2 ] -- I'd want data points 20, 25, and 27 to appear as one big bar on the bar graph (filling the space on the x-axis between 20 and 27), 30 and 31, to be the same bar, and 50 and 56 to be the same bar. The reason I want this is that I have millions of empty points in the graph, and if I fill them all manually, the graph grinds the browser to a halt.
One of the possibilities would be to create a scatter plot for your measurements and add the bars as shapes. The simpler solution using connectgaps: False and fill: tozeroy doesn't work here.
import plotly
plotly.offline.init_notebook_mode()
import plotly.graph_objs as go
meas_x = [20, 25, 27, 30, 31, 50, 56]
meas_y = [3, 3, 3, 10, 10, 2, 2]
meas_y.append('None')
meas_x.append('None')
trace1 = go.Scatter(
x=meas_x,
y=meas_y,
mode='markers'
)
shapes = list()
y = meas_y[0]
x = meas_x[0]
for i, m_y in enumerate(meas_y[1:]):
if y != m_y:
shapes.append({
'type': 'rect',
'x0': x,
'y0': 0,
'x1': meas_x[i],
'y1': meas_y[i - 1],
'fillcolor': '#d3d3d3',
})
y = m_y
x = meas_x[i + 1]
fig = {
'data': [trace1],
'layout': go.Layout(shapes=shapes)
}
plotly.offline.iplot(fig)
I am trying to associate a separate annotation object with each subplot in Plotly (Python), how can this be done?
What I tried
I am setting up the plot like this:
from plotly import tools
fig = tools.make_subplots(rows=2, cols=1)
fig.append_trace(traces[0], 1, 1)
fig.append_trace(traces[1], 2, 1)
where each trace is formed like this:
import plotly.graph_objs as go
traces[0] = go.Scatter(
x=[1,2,3,4],
y=[4,4,2,1],
mode='markers'
)
I know I can access the xaxis of each subplot separately via:
fig['layout']['xaxis1'].update(title='hello1')
fig['layout']['xaxis2'].update(title='hello2')
But how can I access the annotation of each subplot? I tried "annotations1" and "annotation1", with no luck. I also tried to access the layout of subplot 1 via "layout1" as in:
fig['layout1'][...].update(...)
This did not work either.
1) You could assign annotation to specific subplot through setting xref and yref with subplot axis id, such as x1 and y1 represents x axis and y axis of subplot1, as seen from example below and more on link
fig['layout'].update(
annotations=[
dict(
x=2, y=2, # annotation point
xref='x1',
yref='y1',
text='dict Text',
showarrow=True,
arrowhead=7,
ax=10,
ay=70
),
dict(
...
# if have multiple annotations
)
])
2) After you assigned it, you could get access to annotations through
fig['layout']['annotations']
which will return a list of dictionary items:
[{'xref': 'x2', 'arrowhead': 7, 'yref': 'y2', 'text': 'dict Text', 'ay': 40, 'ax': 10, 'y': -1.9491807521563174, 'x': 0.77334098360655923, 'showarrow': True}, {'xref': 'x2', 'arrowhead': 7, 'yref': 'y2', 'text': 'dict Text', 'ay': -40, 'ax': 10, 'y': -0.0041268527747384542, 'x': 1.1132422279202281, 'showarrow': True}]
Hope this could help ;)
it also works with update(),
if you adress the subplot as an element inside the annotations list.
from plotly.subplots import make_subplots
import plotly.graph_objects as go
# create figure with subplots
fig = make_subplots(rows=1, cols=2, subplot_titles = ['title1','title2'])
fig.add_trace(
go.Scatter(x=[1, 2, 3], y=[4, 5, 6]),
row=1, col=1
)
fig.add_trace(
go.Scatter(x=[20, 30, 40], y=[50, 60, 70]),
row=1, col=2
)
fig.update_layout(height=600, width=800, title_text="Subplots")
fig.show()
# to change subtitle, address subplot
fig['layout']['annotations'][0].update(text='your text here');
fig.show()
I have lists of data indicating responses to likert questions with a one (very unhappy) to five (very happy) scale. I would like to create a page of plots showing these lists as skewed stacked horizontal bar charts. The lists of responses can be of different sizes (e.g. when someone has opted out of answering a particular question). Here is a minimal example of the data:
likert1 = [1.0, 2.0, 1.0, 2.0, 1.0, 3.0, 3.0, 4.0, 4.0, 1.0, 1.0]
likert2 = [5.0, 4.0, 5.0, 4.0, 5.0, 3.0]
I would like to be able to plot this with something like:
plot_many_likerts(likert1, likert2)
At the moment I've written a function to iterate over the lists, and plot each one as its own subplot on a shared figure in matplotlib:
def plot_many_likerts(*lsts):
#get the figure and the list of axes for this plot
fig, axlst = plt.subplots(len(lsts), sharex=True)
for i in range(len(lsts)):
likert_horizontal_bar_list(lsts[i], axlst[i], xaxis=[1.0, 2.0, 3.0, 4.0, 5.0])
axlst[i].axis('off')
fig.show()
def likert_horizontal_bar_list(lst, ax, xaxis):
cnt = Counter(lst)
#del (cnt[None])
i = 0
colour_float = 0.00001
previous_right = 0
for key in sorted(xaxis):
ax.barh(bottom=0, width=cnt[key], height=0.4, left=previous_right, color=plt.cm.jet(colour_float),label=str(key))
i += 1
previous_right = previous_right + cnt[key]
colour_float = float(i) / float(len(xaxis))
This works not badly and create stacked bar charts all with the same representative sizes (e.g. the widths share common axis scales). Here is a screen shot:
What is currently Produced http://s7.postimg.org/vh0j816gn/figure_1.jpg
What I would like is to have these two plots centered on midpoints of the mode of the datasets (the datasets will have the same range). For instance:
What I would like to see http://s29.postimg.org/z0qwv4ryr/figure_2.jpg
Suggestions on how I might do this?
I needed to make a divergent bar chart for some likert data. I was using pandas, but the approach would probably be similar without it. The key mechanism is to add in an invisible buffer at the start.
likert_colors = ['white', 'firebrick','lightcoral','gainsboro','cornflowerblue', 'darkblue']
dummy = pd.DataFrame([[1,2,3,4, 5], [5,6,7,8, 5], [10, 4, 2, 10, 5]],
columns=["SD", "D", "N", "A", "SA"],
index=["Key 1", "Key B", "Key III"])
middles = dummy[["SD", "D"]].sum(axis=1)+dummy["N"]*.5
longest = middles.max()
complete_longest = dummy.sum(axis=1).max()
dummy.insert(0, '', (middles - longest).abs())
dummy.plot.barh(stacked=True, color=likert_colors, edgecolor='none', legend=False)
z = plt.axvline(longest, linestyle='--', color='black', alpha=.5)
z.set_zorder(-1)
plt.xlim(0, complete_longest)
xvalues = range(0,complete_longest,10)
xlabels = [str(x-longest) for x in xvalues]
plt.xticks(xvalues, xlabels)
plt.show()
There are many limitations to this approach. First, bars no longer get a black outline, and the legend will have an extra blank element. I just hid the legend (I figure there's probably a way to hide just the individual element). I'm not sure of a convenient way to make the bars have an outline without also adding the outline to the buffer element.
First, we establish some colors and dummy data. Then we calculate the width of the left two columns and half of the middle-most column (which i know to be "SD", "D", and "N", respectively). I find the longest column, and use its width to calculate the difference needed for the other columns. Next, I insert this new buffer column into the first column position with a blank title (which felt gross, lemme tell you). For good measure, I also added a vertical line (axvline) behind the middle of the middle bar based on the advice of [2]. Finally, I adjust the x-axis to have the proper scale by offsetting its labels.
You might want more horizontal space on the left - you can easily do so by adding to "longest".
[2] Heiberger, Richard M., and Naomi B. Robbins. "Design of diverging stacked bar charts for Likert scales and other applications." Journal of Statistical Software 57.5 (2014): 1-32.
I too recently needed to make a divergent bar chart for some Likert data. I took a slightly different approach than #austin-cory-bart.
I modified an example from the gallery instead and created this:
import numpy as np
import matplotlib.pyplot as plt
category_names = ['Strongly disagree', 'Disagree',
'Neither agree nor disagree', 'Agree', 'Strongly agree']
results = {
'Question 1': [10, 15, 17, 32, 26],
'Question 2': [26, 22, 29, 10, 13],
'Question 3': [35, 37, 7, 2, 19],
'Question 4': [32, 11, 9, 15, 33],
'Question 5': [21, 29, 5, 5, 40],
'Question 6': [8, 19, 5, 30, 38]
}
def survey(results, category_names):
"""
Parameters
----------
results : dict
A mapping from question labels to a list of answers per category.
It is assumed all lists contain the same number of entries and that
it matches the length of *category_names*. The order is assumed
to be from 'Strongly disagree' to 'Strongly aisagree'
category_names : list of str
The category labels.
"""
labels = list(results.keys())
data = np.array(list(results.values()))
data_cum = data.cumsum(axis=1)
middle_index = data.shape[1]//2
offsets = data[:, range(middle_index)].sum(axis=1) + data[:, middle_index]/2
# Color Mapping
category_colors = plt.get_cmap('coolwarm_r')(
np.linspace(0.15, 0.85, data.shape[1]))
fig, ax = plt.subplots(figsize=(10, 5))
# Plot Bars
for i, (colname, color) in enumerate(zip(category_names, category_colors)):
widths = data[:, i]
starts = data_cum[:, i] - widths - offsets
rects = ax.barh(labels, widths, left=starts, height=0.5,
label=colname, color=color)
# Add Zero Reference Line
ax.axvline(0, linestyle='--', color='black', alpha=.25)
# X Axis
ax.set_xlim(-90, 90)
ax.set_xticks(np.arange(-90, 91, 10))
ax.xaxis.set_major_formatter(lambda x, pos: str(abs(int(x))))
# Y Axis
ax.invert_yaxis()
# Remove spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
# Ledgend
ax.legend(ncol=len(category_names), bbox_to_anchor=(0, 1),
loc='lower left', fontsize='small')
# Set Background Color
fig.set_facecolor('#FFFFFF')
return fig, ax
fig, ax = survey(results, category_names)
plt.show()