Positioning comment from dataframe in python + plotly - python

I am graphing a line graph that I'd like to add text comments to some data points. The text is long and causes overlaps on the screen so I'd like to position the text better.
Suggestions on how to do this best? It seems like I may need to use annotations but I do not want to need to absolutely position each one. Is there a way to do this?
trace1 = go.Scatter(
x=df[('Data1')],
y=df[('Data2')],
name='Name',
text=df[('DataComment')],
textposition='top right',
mode = "lines+text",
)

Although a reproducible example would help, I have created a DataFrame with a DataComment column containing the lengthy comment "this is a really long comment that has an overlap" which exceeds the boundaries of the plot and gets cut off.
import numpy as np
import pandas as pd
import plotly.graph_objects as go
np.random.seed(42)
df = pd.DataFrame({
'Data1':[20,40,60,80,100],
'Data2':np.random.randint(100, size=5),
'DataComment':["short comment"] + [float("nan")]*3 + ["this is a really long comment that has an overlap"]
})
fig = go.Figure()
trace1 = go.Scatter(
x=df[('Data1')],
y=df[('Data2')],
name='Name',
text=df[('DataComment')],
textposition='top right',
mode = "lines+text"
)
fig.add_trace(trace1)
fig.show()
One thing you can do to fix this is make the text wrap at a specified length. Before you pass df['DataComment'] to go.Scatter, you can modify the DataFrame with the following code, which will insert a <br> tag every 10 characters so that the text will wrap when Plotly renders it using html.
## insert html breaks <br> into the string text
df['DataComment'] = df['DataComment'].fillna("")
df['DataComment'] = df['DataComment'].str.wrap(10)
df['DataComment']= df['DataComment'].apply(lambda x: x.replace('\n', '<br>'))
fig = go.Figure()
trace1 = go.Scatter(
x=df[('Data1')],
y=df[('Data2')],
name='Name',
text=df[('DataComment')],
textposition='top right',
mode = "lines+text"
)
fig.add_trace(trace1)
fig.show()

Related

Adding a market to a line chart Plotly python

I'm trying to add a point to the last observation on a time series chart with plotly. It is not very different from the example here https://stackoverflow.com/a/72539011/3021252 for instance. Except it is the last observation. Unfortunately following such pattern modifies the axis range.
Here is an example of an original chart
import plotly.express as px
df = px.data.gapminder().query("country=='Canada'")
fig = px.line(df, x="year", y="lifeExp", title='Life expectancy in Canada')
fig.show()
But after adding a marker
import plotly.graph_objects as go
fig.add_trace(
go.Scatter(
x=[df["year"].values[-1]],
y=[df["lifeExp"].values[-1]],
mode='markers'
)
)
It looks like that
Has anyone have an idea how not to introduce this gap on the right?

Plotly: legend is not visible

Here is CDF visualization I have:
fig_cdf = px.ecdf(df['Timespan'], color_discrete_sequence=['blue'],ecdfnorm='probability', orientation='h')
fig_cdf.add_hline(y=90, line_width=2, line_color="red", name='90%', visible=True)
fig_cdf.add_hline(y=30, line_width=2, line_color="red", name='75%', visible=True)
fig_cdf.update_layout(width=500, height=500)
The problem here is that i want horizontal lines' names to be visible and appear as 2nd and 3rd legends. For this, I tried to add visible=True. However, it seems not to work. What's wrong?
This is one way of doing it...
Add the two lines to the dataframe as new columns
Use color_discrete_sequence to identify the colors you want
I am using some random dummy data, which you can replace with your data
import plotly.express as px
df = pd.DataFrame({'firstline': random.sample(range(1, 500), 20),'myX' : range(20)}) #My dummy data
#Add the two lines to dataframe
df['90%'] = [90] * 20
df['75%'] = [75] * 20
fig = px.line(df,
y = ['firstline', '90%', '75%'], x= 'myX', color_discrete_sequence=["blue", "red", "red"])
fig.update_layout(legend_title_text='Legend Heading') #Update Legend header if you dont like 'variable'
fig.show()
Output graph
This is my first experience with this graph, but to add it to the legend, you can use the line mode of the scatter plot. So I took the maximum x-axis value used in the first graph and set the legend name Average using the appropriate y-axis value. This example is taken from the official reference.
import plotly.express as px
import plotly.graph_objects as go
df = px.data.tips()
fig = px.ecdf(df, x=["total_bill", "tip"])
xmax = max(fig.data[0]['x'])
#print(xmax)
fig.add_trace(go.Scatter(
x=[0,xmax],
y=[0.6,0.6],
mode='lines',
line_color='red',
name='mean',
showlegend=True
))
fig.show()

plotly: How to add text to existing figure?

Is it possible to add some text on the same html file as my plotly graph?
For example :
This is the code that generates a graph :
data = pd.read_csv('file.csv')
data.columns = ['price', 'place', 'date']
fig = px.scatter(data, x = "place", y = "price", )
fig.write_html("done.html")
This graph will generate a pyplot graph in an html file and I want to add some simple text (such as a conclusion line explaning the graph) under the graph.
This is an example of the output I would like:
ly
You can use fig.update_layout(margin=dict()) to make room for an explanation, and then fig.add_annotation() to insert any text you'd like below the figure utself to get this:
Complete code:
import plotly.graph_objects as go
import numpy as np
x = np.arange(-4,5)
y=x**3
yticks=list(range(y.min(), y.max(), 14))
#yticks.append(y.max())9
# build figure
fig = go.Figure(data=go.Scatter(x=x, y=y))
# make space for explanation / annotation
fig.update_layout(margin=dict(l=20, r=20, t=20, b=60),paper_bgcolor="LightSteelBlue")
# add annotation
fig.add_annotation(dict(font=dict(color='yellow',size=15),
x=0,
y=-0.12,
showarrow=False,
text="A very clear explanation",
textangle=0,
xanchor='left',
xref="paper",
yref="paper"))
fig.show()

Multiple CSV files in Plotly chart

I have multiple CSV files. I want to make a chart, but it generates garbage. When I generate a single graph it is ok, but multiple graphs are not ok.
If I see the individual chart, it works. But can not see both simultaneously.
Example:
import plotly.graph_objects as go
import pandas as pd
df_usgsn03 = pd.read_csv('Graph_Data/MME/USGSN03/mme.txt',sep='|')
df_usgsn04 = pd.read_csv('Graph_Data/MME/USGSN04/mme.txt',sep='|')
fig = go.Figure()
fig.add_trace(go.Scatter(
x=df_usgsn03['Measurement Time'],
y=df_usgsn03['VS.MM.TaLaMisconfigurationMscSupervision'].diff(),
))
fig.add_trace(go.Scatter(
x=df_usgsn04['Measurement Time'],
y=df_usgsn04['VS.MM.TaLaMisconfigurationMscSupervision'].diff(),
))
fig.show()
CSV Files are: https://gofile.io/?c=g0vztw
This is happening because plotly is naively plotting the records in the order it gets them from the Dataframe. You need to sort the DataFrames by Measurement Time.
Try this:
import plotly.graph_objects as go
import pandas as pd
df_usgsn03 = pd.read_csv('Graph_Data/MME/USGSN03/mme.txt',sep='|')
df_usgsn04 = pd.read_csv('Graph_Data/MME/USGSN04/mme.txt',sep='|')
# Sort both by 'Measurement Time'
df_usgsn03_sorted = df_usgsn03.sort_values(by='Measurement Time')
df_usgsn04_sorted = df_usgsn04.sort_values(by='Measurement Time')
fig = go.Figure()
fig.add_trace(
go.Scatter(
x=df_usgsn03_sorted['Measurement Time'],
y=df_usgsn03_sorted['Value'].diff(),
))
fig.add_trace(
go.Scatter(
x=df_usgsn04_sorted['Measurement Time'],
y=df_usgsn04_sorted['Value'].diff(),
))
fig.show()
Found the issue.
Problem is the Measure Time value.
It was not similar in both CSV.
I excluded second part from the measure time....Then it became ok.
I modified it like:
pd.to_datetime(df_usgsn03['Measurement Time']).apply(lambda t: t.replace(second=0)
Thanks everybody.

How to add more than one shape with loop in plotly

I use plotly package to show dynamic finance chart at python. However I didn't manage to put my all key points lines on one chart with for loop. Here is my code:
fig.update_layout(
for i in range(0,len(data)):
shapes=[
go.layout.Shape(
type="rect",
x0=data['Date'][i],
y0=data['Max_alt'][i],
x1='2019-12-31',
y1=data['Max_ust'][i],
fillcolor="LightSkyBlue",
opacity=0.5,
layer="below",
line_width=0)])
fig.show()
I have a data like below one. It is time series based EURUSD parity financial dataset. I calculated two constraits for both Local Min and Max. I wanted to draw rectangule shape to based on for each Min_alt / Min_ust and Max_alt / Max_range. I can draw for just one date like below image however I didn't manage to show all ranges in same plotly graph.
Here is the sample data set.
Here is the solution for added lines:
import datetime
colors = ["LightSkyBlue", "RoyalBlue", "forestgreen", "lightseagreen"]
ply_shapes = {}
for i in range(0, len(data1)):
ply_shapes['shape_' + str(i)]=go.layout.Shape(type="rect",
x0=data1['Date'][i].strftime('%Y-%m-%d'),
y0=data1['Max_alt'][i],
x1='2019-12-31',
y1=data1['Max_ust'][i],
fillcolor="LightSkyBlue",
opacity=0.5,
layer="below"
)
lst_shapes=list(ply_shapes.values())
fig1.update_layout(shapes=lst_shapes)
fig1.show()
However I have still problems to add traces to those lines. I mean text attribute.
Here is my code:
add_trace = {}
for i in range(0, len(data1)):
add_trace['scatter_' + str(i)] = go.Scatter(
x=['2019-12-31'],
y=[data1['Max_ust'][i]],
text=[str(data['Max_Label'][i])],
mode="text")
lst_trace = list(add_trace.values())
fig2=go.Figure(lst_trace)
fig2.show()
The answer:
For full control of each and every shape you insert, you could follow this logic:
fig = go.Figure()
#[...] data, traces and such
ply_shapes = {}
for i in range(1, len(df)):
ply_shapes['shape_' + str(i)]=go.layout.Shape()
lst_shapes=list(ply_shapes.values())
fig.update_layout(shapes=lst_shapes)
fig.show()
The details:
I'm not 100% sure what you're aimin to do here, but the following suggestion will answer your question quite literally regarding:
How to add more than one shape with loop in plotly?
Then you'll have to figure out the details regarding:
manage to put my all key points lines on one chart
Plot:
The plot itself is most likely not what you're looking for, but since you for some reason are adding a plot by the length of your data for i in range(0,len(data), I've made this:
Code:
This snippet will show how to handle all desired traces and shapes with for loops:
# Imports
import pandas as pd
#import matplotlib.pyplot as plt
import numpy as np
import plotly.graph_objects as go
#from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
# data, random sample to illustrate stocks
np.random.seed(12345)
rows = 20
x = pd.Series(np.random.randn(rows),index=pd.date_range('1/1/2020', periods=rows)).cumsum()
y = pd.Series(x-np.random.randn(rows)*5,index=pd.date_range('1/1/2020', periods=rows))
df = pd.concat([y,x], axis = 1)
df.columns = ['StockA', 'StockB']
# lines
df['keyPoints1']=np.random.randint(-5,5,len(df))
df['keyPoints2']=df['keyPoints1']*-1
# plotly traces
fig = go.Figure()
stocks = ['StockA', 'StockB']
df[stocks].tail()
traces = {}
for i in range(0, len(stocks)):
traces['trace_' + str(i)]=go.Scatter(x=df.index,
y=df[stocks[i]].values,
name=stocks[i])
data=list(traces.values())
fig=go.Figure(data)
# shapes update
colors = ["LightSkyBlue", "RoyalBlue", "forestgreen", "lightseagreen"]
ply_shapes = {}
for i in range(1, len(df)):
ply_shapes['shape_' + str(i)]=go.layout.Shape(type="line",
x0=df.index[i-1],
y0=df['keyPoints1'].iloc[i-1],
x1=df.index[i],
y1=df['keyPoints2'].iloc[i-1],
line=dict(
color=np.random.choice(colors,1)[0],
width=30),
opacity=0.5,
layer="below"
)
lst_shapes=list(ply_shapes.values())
fig.update_layout(shapes=lst_shapes)
fig.show()
Also you can use fig.add_{shape}:
fig = go.Figure()
fig.add_trace(
go.Scatter( ...)
for i in range( 1, len( vrect)):
fig.add_vrect(
x0=vrect.start.iloc[ i-1],
x1=vrect.finish.iloc[ i-1],
fillcolor=vrect.color.iloc[ i-1]],
opacity=0.25,
line_width=0)
fig.show()

Categories

Resources