Here is the code that I have tried:
# import pandas as pd
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
df = pd.read_csv("resultant_data.txt", index_col = 0, sep = ",")
display=df[["Velocity", "WinLoss"]]
pos = lambda col : col[col > 0].sum()
neg = lambda col : col[col < 0].sum()
Related_Display_Info = df.groupby("RacerCount").agg(Counts=("Velocity","count"),
WinLoss=("WinLoss","sum"),
Positives=("WinLoss", pos),
Negatives=("WinLoss", neg),
)
# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])
# Add traces
fig.add_trace(
go.Scatter(x=display.index, y=display["Velocity"], name="Velocity", mode="markers"),
secondary_y=False
)
fig.add_trace(
go.Scatter(x=Related_Display_Info.index,
y=Related_Display_Info["WinLoss"],
name="Win/Loss",
mode="markers",
marker=dict(
color=(
(Related_Display_Info["WinLoss"] < 0)
).astype('int'),
colorscale=[[0, 'green'], [1, 'red']]
)
),
secondary_y=True,
)
# Add figure title
fig.update_layout(
title_text="Race Analysis"
)
# Set x-axis title
fig.update_xaxes(title_text="<b>Racer Counts</b>")
# Set y-axes titles
fig.update_yaxes(title_text="<b>Velocity</b>", secondary_y=False)
fig.update_yaxes(title_text="<b>Win/Loss/b>", secondary_y=True)
fig.update_layout(hovermode="x unified")
fig.show()
The output is:
But I was willing to display the following information when I hover on the point:
RaceCount = From Display dataframe value Number of the race corresponding to the dot I hover on.
Velocity = From Display Dataframe value Velocity at that point
Counts = From Related_Display_Info Column
WinLoss = From Related_Display_Info Column
Positives = From Related_Display_Info Column
Negatives = From Related_Display_Info Column
Please can anyone tell me what to do to get this information on my chart?
I have checked this but was not helpful since I got many errors: Python/Plotly: How to customize hover-template on with what information to show?
Data:
RacerCount,Velocity,WinLoss
111,0.36,1
141,0.31,1
156,0.3,1
141,0.23,1
147,0.23,1
156,0.22,1
165,0.2,1
174,0.18,1
177,0.18,1
183,0.18,1
114,0.32,1
117,0.3,1
120,0.29,1
123,0.29,1
126,0.28,1
129,0.27,1
120,0.32,1
144,0.3,1
147,0.3,1
159,0.27,1
165,0.26,1
168,0.25,1
156,0.29,1
165,0.26,1
168,0.26,1
165,0.28,1
213,0.17,1
243,0.15,1
249,0.14,1
228,0.54,1
177,0.67,1
180,0.66,1
183,0.65,1
192,0.66,1
195,0.62,1
198,0.6,1
180,0.66,1
222,0.56,1
114,0.41,1
81,0.82,1
102,0.56,1
111,0.55,1
90,1.02,1
93,1.0,1
90,1.18,1
90,1.18,1
93,1.1,1
96,1.07,1
99,1.04,1
102,0.99,1
105,0.94,1
108,0.92,1
111,0.9,1
162,0.66,1
159,0.63,1
162,0.65,-1
162,0.66,-1
168,0.64,-1
159,0.68,-1
162,0.67,-1
174,0.62,-1
168,0.65,-1
171,0.64,-1
198,0.55,-1
300,0.47,-1
201,0.56,-1
174,0.63,-1
180,0.61,-1
171,0.64,-1
174,0.62,-1
303,0.47,-1
312,0.48,-1
258,0.51,-1
261,0.51,-1
264,0.5,-1
279,0.47,-1
288,0.48,-1
294,0.47,-1
258,0.52,-1
261,0.51,-1
267,0.5,-1
222,0.53,-1
171,0.64,-1
177,0.63,-1
177,0.63,-1
Essentially, this code ungroups the data frame before plotting to create the hovertemplate you're looking for.
As stated in the comments, the data has to have the same number of rows to be shown in the hovertemplate. At the end of my answer, I added the code all in one chunk.
Since you have hovermode as x unified, you probably only want one of these traces to have hover content.
I slightly modified the creation of Related_Display_Info. Instead of WinLoss, which is already in the parent data frame, I modified it to WinLoss_sum, so there wouldn't be a naming conflict when I ungrouped.
Related_Display_Info = df.groupby("RacerCount").agg(
Counts=("Velocity","count"), WinLoss_sum=("WinLoss","sum"),
Positives=("WinLoss", pos), Negatives=("WinLoss", neg))
Now it's time to ungroup the data you grouped. I created dui (stands for display info ungrouped).
dui = pd.merge(df, Related_Display_Info, how = "outer", on="RacerCount",
suffixes=(False, False))
I created the hovertemplate for both traces. I passed the entire ungrouped data frame to customdata. It looks like the only column that isn't in the template is the original WinLoss.
# create hover template for all traces
ht="<br>".join(["<br>RacerCount: %{customdata[0]}",
"Velocity: %{customdata[1]:.2f}",
"Counts: %{customdata[3]}",
"Winloss: %{customdata[4]}",
"Positives: %{customdata[5]}",
"Negatives: %{customdata[6]}<br>"])
The creation of fig is unchanged. However, the traces are both based on dui. Additionally, the index isn't RacerCount, so I used the literal field instead.
# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])
# Add traces
fig.add_trace(go.Scatter(x=dui["RacerCount"], y=dui["Velocity"],
name="Velocity", mode="markers",
customdata=dui, hovertemplate=ht),
secondary_y=False)
fig.add_trace(
go.Scatter(x = dui["RacerCount"], y=dui["WinLoss_sum"], customdata=dui,
name="Win/Loss", mode="markers",
marker=dict(color=((dui["WinLoss_sum"] < 0)).astype('int'),
colorscale=[[0, 'green'], [1, 'red']]),
hovertemplate=ht),
secondary_y=True)
All the code altogether (for easier copy + paste)
import pandas as pd
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
df = pd.read_clipboard(sep = ',')
display=df[["Velocity", "WinLoss"]]
pos = lambda col : col[col > 0].sum()
neg = lambda col : col[col < 0].sum()
Related_Display_Info = df.groupby("RacerCount").agg(
Counts=("Velocity","count"), WinLoss_sum=("WinLoss","sum"),
Positives=("WinLoss", pos), Negatives=("WinLoss", neg))
# ungroup the data for the hovertemplate
dui = pd.merge(df, Related_Display_Info, how = "outer", on="RacerCount",
suffixes=(False, False))
# create hover template for all traces
ht="<br>".join(["<br>RacerCount: %{customdata[0]}",
"Velocity: %{customdata[1]:.2f}",
"Counts: %{customdata[3]}",
"Winloss: %{customdata[4]}",
"Positives: %{customdata[5]}",
"Negatives: %{customdata[6]}<br>"])
# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])
# Add traces
fig.add_trace(go.Scatter(x=dui["RacerCount"], y=dui["Velocity"],
name="Velocity", mode="markers",
customdata=dui, hovertemplate=ht),
secondary_y=False)
fig.add_trace(
go.Scatter(x = dui["RacerCount"], y=dui["WinLoss_sum"], customdata=dui,
name="Win/Loss", mode="markers",
marker=dict(color=((dui["WinLoss_sum"] < 0)).astype('int'),
colorscale=[[0, 'green'], [1, 'red']]),
hovertemplate=ht),
secondary_y=True)
# Add figure title
fig.update_layout(
title_text="Race Analysis"
)
# Set x-axis title
fig.update_xaxes(title_text="<b>Racer Counts</b>")
# Set y-axes titles
fig.update_yaxes(title_text="<b>Velocity</b>", secondary_y=False)
fig.update_yaxes(title_text="<b>Win/Loss/b>", secondary_y=True)
fig.update_layout(hovermode="x unified")
fig.show()
I'm trying to create faceted maps by the column rank in my df. Each map will display the product for each state. I want the color of the product to be consistent across maps.
With the solution below I can achieve that, but the legend will show multiple entries for the same product, one for each state. How can I have the legend show only one entry per distinct product?
import pandas as pd
import plotly.express as px
from random import randint
df = pd.DataFrame({'rank': [1,1,1,1,2,2,2,2],'product':['A','B','C','D','C','D','Z','X'],'state':['WA','OR','CA','ID','WA','OR','CA','ID']})
unique_hi = df['product'].unique()
color_discrete_map = {unique_hi[k]: '#%06X' % randint(0, 0xFFFFFF) for k in range(len(unique_hi))}
fig = px.choropleth(df, color='product', facet_col="rank",facet_col_wrap=2,
locations="state", #featureidkey="properties.district",
locationmode="USA-states",
projection="mercator",height=600,
color_discrete_map=color_discrete_map,
title='Regional products'
)
fig.update_geos(fitbounds="locations", visible=False)
fig.update_layout(margin={"r":0,"t":30,"l":0,"b":0})
fig.show()
If you check the contents of the created map in fig.data, you will find the original name of the legend, which is collected and only the names of the non-duplicated.
import pandas as pd
import plotly.express as px
from random import randint
df = pd.DataFrame({'rank': [1,1,1,1,2,2,2,2],'product':['A','B','C','D','C','D','Z','X'],'state':['WA','OR','CA','ID','WA','OR','CA','ID']})
unique_hi = df['product'].unique()
color_discrete_map = {unique_hi[k]: '#%06X' % randint(0, 0xFFFFFF) for k in range(len(unique_hi))}
fig = px.choropleth(df, color='product', facet_col="rank",facet_col_wrap=2,
locations="state", #featureidkey="properties.district",
locationmode="USA-states",
projection="mercator",height=600,
color_discrete_map=color_discrete_map,
title='Regional products'
)
fig.update_geos(fitbounds="locations", visible=False)
fig.update_layout(margin={"r":0,"t":30,"l":0,"b":0})
# update
names = set()
fig.for_each_trace(
lambda trace:
trace.update(showlegend=False)
if (trace.name in names) else names.add(trace.name))
fig.show()
The way to add a product name as an annotation is not possible to specify it using map coordinates (I referred to this for the rationale), so adding the following code will make the annotation, but all products will need to be manually addressed. Upon further investigation, it seems that a combination of go.choroplethmapbox() and go.scattergeo() would do it. In this case, you will need to rewrite the code from scratch.
fig.add_annotation(
x=0.2,
xref='paper',
y=0.85,
yref='paper',
text='A',
showarrow=False,
font=dict(
color='yellow',
size=14
)
)
I created a stacked area chart using stackgroup= and now I'd like to add annotation to last value.
I am copying the code from here and made some modification.
Plotly: How to annotate end of multiple lines with text and marker colors that match the lines?
this is the original plot for stacked area chart
for ipad,pad in enumerate(pad_list):
for iwell,well in enumerate(cols_thispad):
fig.add_scatter(
x=df.index,
y=df[well].values,
mode='lines',
line={"color": colors_discrete[iwell]}, #"color": "#035593"
stackgroup=str(ipad+1), # define stack group
name=well,
row=ipad+1,
col=1,
legendgroup = str(ipad+1),
meta=well,
text=[key.title()+unit_thiskey]*len(df.index),
hovertemplate='%{meta}<br>Datetime: %{x}<br>%{text}:%{y}<extra></extra>',
)
after plotting, I'd like to add annotation to last value of each stacked area chat, here is what I did, if I use stackgroup=, the plot is completely wrong. If I remove stackgroup= in below chat, the last values can be shown and in right chart. However, it is not stacked. so how to show last value markers in stack mode? Thanks
for i, d in enumerate(fig.data):
padname=d.name.split('A')[1][:2]
padname_ix=pad_list.index(padname)
legendgroup=str(padname_ix+1)
row=padname_ix+1
stackgroup=str(padname_ix+1)
fig.add_scatter(x=[d.x[-1]], y = [d.y[-1]],
mode = 'markers+text',
text = f'{d.y[-1]:.2f}',
textfont = dict(color=d.line.color),
textposition='middle right',
marker = dict(color = d.line.color, size = 12),
legendgroup = legendgroup, #d.name,\
stackgroup=stackgroup,
row=row,col=1,
showlegend=False)
here is the plot without using stackgroup in the 2nd code. It works but not correctly.
Since this function groups stacked graphs, the issue can be resolved by giving each group unit a unique name. In the example answer, the area graph is named 'one' and the text annotation by scatter is named 'two'.
import yfinance as yf
ticker = ['AAPL','GOOGL','TSLA','MSFT']
data = yf.download(ticker, start="2021-01-01", end="2021-03-01")['Close']
import plotly.graph_objects as go
fig = go.Figure()
for t in data.columns:
fig.add_trace(go.Scatter(x=data.index,
y=data[t],
hoverinfo='x+y',
mode='lines',
stackgroup='one',
name=t
)
)
fig.add_trace(go.Scatter(x=[data.index[-1]],
y=[data[t][-1]],
mode='markers+text',
text=round(data[t][-1],2),
textposition='middle left',
stackgroup='two',
name=t,
showlegend=False
)
)
fig.update_layout(height=600
)
fig.show()
I'm trying to find out how i can set the domain for a table in a 2x2 subplot in plotly (V4.14.3).
I thought the "domain" option in go.Table(domain=dict(x=[a,b],y=[c,d)) would be the right option but doesn't move while changing the values.
My goal is to have a fixed domain for the plots in the upper row and a dynamic table and legend in the lower one, so if i could variate the table position with a domain option it would be easy.
As you can see i set the domain in my code to domain=dict(x=[0.0, 1.0],y=[0.2, 0.8]) which is different to the result you can see in the attached picture.
Thank you very much in advance
import plotly.graph_objs as go
from plotly.subplots import make_subplots
# Testdata
Test_DataSets = ['Set1','Set2','Set3']
Test_DataVals = [2,3,4]
x = [1,2,3,4,5]
y = [2,5,7,9,6]
# Plots
fig = make_subplots(
rows=2, cols=2,
specs=[[{"type": "scatter"},{"type": "scatter"}],
[{"type": "table"},{"type": "scatter"}]
]
)
fig.add_trace(go.Scatter(x=x,y=y,
name= 'Testdata',
legendgroup = 'group'),1,1)
fig.add_trace(go.Scatter(x=x,y=y,
name= 'Testdata',
legendgroup = 'group'),1,2)
fig.add_trace(
go.Table(domain=dict(x=[0.0, 1.0],y=[0.2, 0.8]),
header=dict(
values=["DataSet", "Val"],
font=dict(size=10),
align="left"),
cells=dict(
values=[Test_DataSets,Test_DataVals]
)),
row=2, col=1
)
fig.show()
this the result
thats how it should be
I found the solution by myself...
the domain can be set with following code:
fig.update_traces(domain_x=[0,0.45], domain_y=[0,0.45], selector=dict(type='table'))
I'm a newbie in Plotly and I was wondering if there is a way to specify where a new trace needs to be centered within the Figure object.
Just to be more clear, this is an example:
import plotly.express as px
import plotly.graph_objects as go
df = pd.DataFrame(something)
fig = go.Figure()
for i in [40,45,50]:
fig.add_shape(
go.layout.Shape(
type='line',
xref='x',
yref='y',
x0=line_data[i]["min"],
y0=i,
x1=line_data[i]["max"],
y1=i,
),
)
fig.add_trace(
go.Scatter(
x=df.ColA.values,
y=df.ColB.values,
mode='markers',
)
)
This is the result
My goal is to build an histogram of the points in each horizontal line.
I don't know if there is a better and faster way, but my idea was to add more traces, each one with an histogram, and then center those traces in each line. Is there a way to do it? Maybe some position parameter for a trace, like (xcenter=7.5, ycenter=50)?
My ideal result should be:
you describe histogram / frequency multiple observed items
have mapped these to y-axis using base
import numpy as np
import plotly.graph_objects as go
df = pd.DataFrame({40:np.random.normal(5,2, 200).astype(int),50:np.random.normal(6,2, 200).astype(int),60:np.random.normal(6.5,2, 200).astype(int)})
# change to frequency of observed values
df2 = df[40].value_counts().to_frame().join(df[50].value_counts(), how="outer").join(df[60].value_counts(), how="outer")
# plot bar of frequency, setting base based on observation
fig = go.Figure([go.Bar(x=df2.index, y=df2[c]/len(df2), base=c, name=c) for c in df2.columns])
fig.update_layout(barmode="overlay")