Plotly: How to add a horizontal line to a line graph? - python

I made a line graph with the code below and I'm trying to add a horizontal line at y=1. I tried following the instructions on the plotly site but it is still not showing. Does anyone know why?
date = can_tot_df.date
growth_factor = can_tot_df.growth_factor
trace0 = go.Scatter(
x=date,
y=growth_factor,
mode = 'lines',
name = 'growth_factor'
)
fig = go.Figure()
fig.add_shape(
type='line',
x0=date.min(),
y0=1,
x1=date.max(),
y1=1,
line=dict(
color='Red',
)
)
data = [trace0]
iplot(data)

Short answer, and a general solution:
fig.add_shape(type='line',
x0=0,
y0=40,
x1=8,
y1=40,
line=dict(color='Red',),
xref='x',
yref='y'
)
Details and specifics about OP's question
It's hard to tell exactly what's wrong without a sample of your data.
What I can tell for sure is that you're missing the arguments xref and yref to specify that the line is drawn as units of your y and x axis. Judging by your sample code, this is what you'd like to do since you're specifying your x-values in terms of dates.
Also, you don't need to worry about iplot for newer versions of plotly. You can display your chart just as easily by just running fig.show(). The figure and code sample below will show you how to use fig.show() and how to define your lines in terms of axis units.
Plot:
Code:
import plotly.graph_objects as go
import numpy as np
x = np.arange(10)
fig = go.Figure(data=go.Scatter(x=x, y=x**2))
fig.add_shape(type='line',
x0=0,
y0=40,
x1=8,
y1=40,
line=dict(color='Red',),
xref='x',
yref='y'
)
fig.show()
An alternative to xref='x' is xref='paper'. Now you can specify x0 as a float between 0 and 1 spanning from the start and end of the plot.

You could also use fig.add_hline(y=1) --> see https://plotly.com/python/horizontal-vertical-shapes/
import plotly.graph_objects as go
import numpy as np
x = np.arange(10)
fig = go.Figure(data=go.Scatter(x=x, y=x**2))
fig.add_hline(y=40, line_width=3, line_dash="dash", line_color="green")
fig.show()

If you use subplots, then this is the easiest way I found to add an other line to a subplot. this example draws a horizontal line at y=80 for all x values
from plotly.subplots import make_subplots
fig = make_subplots(rows=2, cols=1,
shared_xaxes=True,
vertical_spacing=0.02)
[some graph]
fig.add_trace(go.Scatter(
name='Y=80',
x = [df['date'].min(), df['date'].max()],
y = [80, 80],
mode = "lines",
marker = dict(color = 'rgba(80, 26, 80, 0.8)')
),row=1, col=1)
i found the solution on github :

df = df
fig = px.scatter(df, x="date", y="growth_factor", mode = 'lines',
hover_name=df['growth_factor'] )
fig.update_layout(shapes=[
dict(
type= 'line',
yref= 'y', y0= 1, y1= 1, # adding a horizontal line at Y = 1
xref= 'paper', x0= 0, x1= 1
)
])
fig.show()

You’re adding the line to your fig object, but fig is not getting passed into the iplot() function, only your data. So only the trace is getting plotted.
If you're using a late version of plotly, the new syntax allows you to create this plot simply using the fig object, like:
from plotly import graph_objects as go
fig = go.Figure()
# Contrived dataset for example.
x = [1, 2, 3, 4]
y = [i**2 for i in x]
fig.add_trace(go.Scatter(
x=x,
y=y,
mode = 'lines',
name = 'growth_factor'))
fig.add_shape(type='line',
x0=min(x),
y0=5,
x1=max(x),
y1=5,
line=dict(color='Red'))
fig.update_shapes(dict(xref='x', yref='y'))
fig.show()
Here are the plotly docs for convenience.

Related

Adding text or cross sign on every subplot of plotly, each in unique positions of the subplots

I am struggling to put a cross sign in certain positions of each subplots of plotly in Python. I have 2 subplots and in each one, I want to out the cross in certain positions as below.
Position of the cross sign at the subplot_1 and 2 are attached.
import numpy as np
import plotly.graph_objs as go
import plotly.figure_factory as ff
from plotly.subplots import make_subplots
import string
#Define data for heatmap
N=5
x = np.array([10*k for k in range(N)])
y = np.linspace(0, 2, N)
z1 = np.random.randint(5,15, (N,N))
z2 = np.random.randint(10,27, (N,N))
mytext = np.array(list(string.ascii_uppercase))[:25].reshape(N,N)
fig1 = ff.create_annotated_heatmap(z1, x.tolist(), y.tolist(), colorscale='matter')
fig2 = ff.create_annotated_heatmap(z2, x.tolist(), y.tolist(), annotation_text=mytext, colorscale='Viridis')
fig = make_subplots(
rows=1, cols=2,
horizontal_spacing=0.05,
)
fig.add_trace(fig1.data[0], 1, 1)
fig.add_trace(fig2.data[0], 1, 2)
annot1 = list(fig1.layout.annotations)
annot2 = list(fig2.layout.annotations)
for k in range(len(annot2)):
annot2[k]['xref'] = 'x2'
annot2[k]['yref'] = 'y2'
fig.update_layout(annotations=annot1+annot2)
There are two ways to deal with this question: the first is to use the line mode of the scatterplot and the second is to add a shape. In the line mode of the scatterplot, the real starting position is -0.5, so the heatmap and the cross line are misaligned. So I chose to add a figure.
Also, I can now annotate without using figure_factory, so I'll use a graph object to construct the graph. The configuration is one heatmap combined with two shapes, with the y-axis and x-axis scales changed.
import numpy as np
import plotly.graph_objs as go
from plotly.subplots import make_subplots
np.random.seed(1)
fig = make_subplots(rows=1,
cols=2,
horizontal_spacing=0.05,
)
fig.add_trace(go.Heatmap(z=z1,
text=z1,
texttemplate='%{text}',
showscale=False,
),
row=1,col=1
)
fig.add_shape(type='line',
x0=1.5, y0=1.5, x1=2.5, y1=2.5,
line=dict(color='black', width=2)
)
fig.add_shape(type='line',
x0=2.5, y0=1.5, x1=1.5, y1=2.5,
line=dict(color='black', width=2)
)
fig.add_trace(go.Heatmap(z=z2,
text=mytext,
texttemplate='%{text}',
showscale=False,
colorscale = 'Viridis'
),
row=1,col=2
)
fig.add_shape(type='line',
x0=0.5, y0=-0.5, x1=1.5, y1=0.5,
line=dict(color='black', width=2),
row=1,col=2
)
fig.add_shape(type='line',
x0=1.5, y0=-0.5, x1=0.5, y1=0.5,
line=dict(color='black', width=2),
row=1, col=2
)
fig.update_yaxes(tickvals=[0,1,2,3,4], ticktext=y.tolist())
fig.update_xaxes(tickvals=[0,1,2,3,4], ticktext=x.tolist())
fig.update_layout(autosize=False, width=800)
fig.show()

How to add labels to plotly Box chart like Scatter chart?

I couldn't find the way to add text labels to plotly/dash box plot like you could add it to a scatterplot. In the example below, for ScatterPlot x=qty, y=price and you can then add Salesperson to the graph when the cursor is on Marker. For adding this I use the 'text' argument.
In the second example for BoxPlot when x=date, y=price I want to add salesperson in the same way. It would be very useful in case of outliers to see immediately who was the salesperson for that purchase. I looked in the documentation, but there is no clue. I assume it's not possible but still decided to try my luck here.
scatterplot:
import plotly.offline as pyo
import plotly.graph_objs as go
purchase={'date':['11/03/2021','12/03/2021','14/03/2021','11/03/2021'],
'price':[300, 400,200, 200],
'currency':['eur', 'usd','usd','usd'],
'qty':[200, 300, 400, 500],
'salesman':['AC', 'BC', "CC", 'DC']}
pur=pd.DataFrame(purchase)
pur
data = [go.Scatter(
x = pur['qty'],
y = pur['price'],
mode = 'markers',
text=pur['salesman'],
marker = dict(
size = 12,
color = 'rgb(51,204,153)',
symbol = 'pentagon',
line = dict(
width = 2,
)
)
)]
layout = go.Layout(
title = 'Random Data Scatterplot',
xaxis = dict(title = 'Some random x-values'),
yaxis = dict(title = 'Some random y-values'),
hovermode ='closest'
)
fig = go.Figure(data=data, layout=layout)
fig.show()
boxplot:
import plotly.offline as pyo
import plotly.graph_objs as go
x = pur['date']
y = pur['price']
data = [
go.Box(
y=y,
x=x,
text=pur['salesman']
)
]
layout = go.Layout(
title = 'box_plot'
)
fig = go.Figure(data=data, layout=layout)
fig.show()
The data you currently have is not suitable for boxplot. If you try to plot a boxplot with your data, the list [300, 400,200, 200] is used only once for the first date. For the other dates, there is no data.
I will show a simpler example with my own data.
dataset.csv
salesman,sales
alan,1.8
bary,2.3
copa,4.2
dac,1.19
eila,2.3
foo,2.5
gary,0.1
holland,10
code
import plotly.graph_objs as go
import pandas as pd
import plotly.io as pio
pio.renderers.default = 'browser'
df = pd.read_csv("deletelater")
fig = go.Figure()
fig.add_trace(go.Box(
y=df["sales"],
name='12/12/22',
customdata=df["salesman"],
hovertemplate='<b>sales: %{y}</b><br>salesperson: %{customdata}'
))
fig.show()
Diagram
As you can see, the name of the outlier salesperson is displayed on the hover label.

stacked barplot in plotly

My input:
names_col = ['Count','Percentage']
dat = [['Matching', 63],['Mismatching', 37]]
plot_df = pd.DataFrame(data=dat,columns=names_col)
I just trying plot within plotly simple bar char where stacked.
my code:
fig = px.bar(p_df, x='Count', y='Percentage', color='Count' ,title='My plot', barmode='stack')
fig.show();
And what I get:
That not what I expected. I want something like this:
Here code within seaborn:
p=p_df.set_index('Count').T.plot(kind='bar', stacked=True, figsize=(12,8),rot=0)
p.set_title('BBPS.2')
for x in p.containers:
p.bar_label(x, label_type='edge', weight='bold')
p.bar_label(x, label_type='center', weight='bold', color='white')
plt.show();
By setting the x axis to 'Count' you are defining the bars to not be stacked.
You could either find a different parameter for the x axis or add a dummy column with the same value for both rows so they have the same x value:
import pandas as pd
import plotly.express as px
names_col = ['Count','Percentage', 'dummy']
dat = [['Matching', 63, 0],['Mismatching', 37, 0]]
plot_df = pd.DataFrame(data=dat,columns=names_col)
fig = px.bar(plot_df, x='dummy', y='Percentage', color='Count' ,title='My plot')
fig.show()
The result:
You need to set the base to the first bar in order to stack them. Right now you have merely defined two separate bars. Take a look at this code from a dev.to post:
fig3 = go.Figure(
data=[
go.Bar(
name="Original",
x=data["labels"],
y=data["original"],
offsetgroup=0,
),
go.Bar(
name="Model 1",
x=data["labels"],
y=data["model_1"],
offsetgroup=1,
),
go.Bar(
name="Model 2",
x=data["labels"],
y=data["model_2"],
offsetgroup=1,
base=data["model_1"],
)
],
layout=go.Layout(
title="Issue Types - Original and Models",
yaxis_title="Number of Issues"
)
)
fig3.show()
That resulted in a plot that looks like this:

Succint way to add line segments to plotly graph (with python/jupyter notebook)?

I want to create a lollipop plot with several horizontal line segments like this - https://python-graph-gallery.com/184-lollipop-plot-with-2-group. I'd like to use plotly since I prefer the graphics (and easy interactivity) but can't find a succint way.
There's both line graphs (https://plot.ly/python/line-charts/) and you can add lines in the layout (https://plot.ly/python/shapes/#vertical-and-horizontal-lines-positioned-relative-to-the-axes), but both of these solutions require each line segment to be added separately, with about 4-8 lines of code each. While I could just for-loop this, would appreciate if anyone can point me to anything with inbuilt vectorization, like the matplotlib solution (first link)!
Edit: Also tried the following code, to first make the plot ala matplotlib, then convert to plotly. The line segments disappear in the process. Starting to think it's just impossible.
mpl_fig = plt.figure()
# make matplotlib plot - WITH HLINES
plt.rcParams['figure.figsize'] = [5,5]
ax = mpl_fig.add_subplot(111)
ax.hlines(y=my_range, xmin=ordered_df['value1'], xmax=ordered_df['value2'],
color='grey', alpha=0.4)
ax.scatter(ordered_df['value1'], my_range, color='skyblue', alpha=1,
label='value1')
ax.scatter(ordered_df['value2'], my_range, color='green', alpha=0.4 ,
label='value2')
ax.legend()
# convert to plotly
plotly_fig = tls.mpl_to_plotly(mpl_fig)
plotly_fig['layout']['xaxis1']['showgrid'] = True
plotly_fig['layout']['xaxis1']['autorange'] = True
plotly_fig['layout']['yaxis1']['showgrid'] = True
plotly_fig['layout']['yaxis1']['autorange'] = True
# plot: hlines disappear :/
iplot(plotly_fig)
You can use None in the data like this:
import plotly.offline as pyo
import plotly.graph_objs as go
fig = go.Figure()
x = [1, 4, None, 2, 3, None, 3, 4]
y = [0, 0, None, 1, 1, None, 2, 2]
fig.add_trace(
go.Scatter(x=x, y=y))
pyo.plot(fig)
Plotly doesn't provide a built in vectorization for such chart, because it can be done easily by yourself, see my example based on your provided links:
import pandas as pd
import numpy as np
import plotly.offline as pyo
import plotly.graph_objs as go
# Create a dataframe
value1 = np.random.uniform(size = 20)
value2 = value1 + np.random.uniform(size = 20) / 4
df = pd.DataFrame({'group':list(map(chr, range(65, 85))), 'value1':value1 , 'value2':value2 })
my_range=range(1,len(df.index)+1)
# Add title and axis names
data1 = go.Scatter(
x=df['value1'],
y=np.array(my_range),
mode='markers',
marker=dict(color='blue')
)
data2 = go.Scatter(
x=df['value2'],
y=np.array(my_range),
mode='markers',
marker=dict(color='green')
)
# Horizontal line shape
shapes=[dict(
type='line',
x0 = df['value1'].loc[i],
y0 = i + 1,
x1 = df['value2'].loc[i],
y1 = i + 1,
line = dict(
color = 'grey',
width = 2
)
) for i in range(len(df['value1']))]
layout = go.Layout(
shapes = shapes,
title='Lollipop Chart'
)
# Plot the chart
fig = go.Figure([data1, data2], layout)
pyo.plot(fig)
With the result I got:

how to add horizontal line in all subplots in plotly python

I want to add horizontal line at 0.09 and -0.09 in every subplot I am generating in plotly. Following is my code to do that.
trace1 = go.Scatter(
x=df1['transaction_date'],
y=df1['difference'],
)
trace2 = go.Scatter(
x=df2['transaction_date'],
y=df2['difference'],
)
trace3 = go.Scatter(
x=df3['transaction_date'],
y=df3['difference'],
)
trace4 = go.Scatter(
x=df4['transaction_date'],
y=df4['difference'],
)
fig = tools.make_subplots(rows=2, cols=2,subplot_titles=('DF1 HS', DF2 HSD',
'DF3 HD', 'DF4 SD',
))
fig.append_trace(trace1, 1, 1)
fig.append_trace(trace2, 1, 2)
fig.append_trace(trace3, 2, 1)
fig.append_trace(trace4, 2, 2)
Then I want to save this 4 subplots as jpeg on disk. How can I do that in python
Try updating layout of fig object with shapes as below:
import plotly.graph_objs as go
from plotly import tools
from plotly.offline import init_notebook_mode, plot
df = pd.DataFrame(np.random.randint(0,100,size=(20,2)),
index=pd.date_range(start='2018-08-21',end='2018-09-09'),
columns=['A','B'])
trace1 = go.Scatter(x=df.index,y=df['A'],)
trace2 = go.Scatter(x=df.index,y=df['B'],)
fig = tools.make_subplots(rows=2, cols=1,subplot_titles=(['A','B']))
fig.append_trace(trace1, 1, 1)
fig.append_trace(trace2, 2, 1)
fig['layout'].update(shapes=[{'type': 'line','y0':50,'y1': 50,'x0':str(df.index[0]),
'x1':str(df.index[-1]),'xref':'x1','yref':'y1',
'line': {'color': 'red','width': 2.5}},
{'type': 'line','y0':50,'y1': 50,'x0':str(df.index[0]),
'x1':str(df.index[-1]),'xref':'x2','yref':'y2',
'line': {'color': 'red','width': 2.5}}])
plot(fig,show_link=False,image='jpeg',image_filename='Temp_plot')
The plot will be saved as Temp_plot.jpeg. Check the image below.
The downside of this method is we need to carefully give axes values to xref and yref with respect to subplots.
I'm pretty new to Plotly so maybe the API has just been updated, but it seems there is a much simpler solution, per the documentation here. One need only use the fig.add_hline() syntax while specifying which subplot (col and row) it should be drawn on, as such:
fig.add_hline(y=1, line_dash="dot", row=1, col=1, line_color="#000000", line_width=2)
This line will instruct Plotly to draw a horizontal line y = 1 on the subplot located at row = 1; col = 1.
Alternatively, as noted in the dox, the "all" keyword can be passed as a value for either the row or col argument to instruct plotly to draw the line on (wait for it...) all the subplots!
You mentioned that you were ok with a matplotlib solution:
Data:
dict = {
"a":np.random.randint(low=-10,high=10,size=20),
"b":np.random.randint(low=-10,high=10,size=20),
"c":np.random.randint(low=-10,high=10,size=20),
"d":np.random.randint(low=-10,high=10,size=20),
}
df = pd.DataFrame(dict)
Plot:
fig, axes = plt.subplots(2,2, figsize=(20,10), sharex=True, sharey=True)
for i,j in zip(axes.ravel(), list(df)):
i.plot(df.index, df[j], 'ro')
i.hlines(y=-3, xmin=0, xmax=22)
i.hlines(y=3, xmin=0, xmax=22)
fig.savefig("testplot.png")
Result:

Categories