Change hover text of a plotly express treemap - python

I want to show in a treemap just the label and value of each item, not parent or ID. I have defined it with plotly express. No matter how much I have tinkered with it, I haven’t been able to restrict hover text to the fields I need. Check the code and capture
import plotly.express as px
fig = px.treemap(dfconcepto, path=['type','name'],
values = 'count',
width=900, height=900,
hover_data = ['count'],
)
fig.show()
I also have tried to create it with non-express treemap. Hovertext is what I want, but then a treemap with two levels is rendered asymmetric.
What I want is something like the hovertext of non-express treemap, but balanced and symmetric as in express treemap
What can I do?
Thanks in advance!

It seems to me you should overwrite your hover template
import pandas as pd
import plotly.express as px
url = "https://gist.githubusercontent.com/jlchulilla/3b4e40f68ba73b5dbcb661a1d861f308/raw/e564973db30a4612aba60c5b26dd108edc98f048/test2sof.csv"
df = pd.read_csv(url).drop("Unnamed: 0", axis=1)
fig = px.treemap(df, path=['type','name'],
values = 'coincidencia',
width=900, height=900,
)
# Now your hovertemplate looks like
# fig.data[0].hovertemplate
# 'labels=%{label}<br>coincidencia=%{value}<br>parent=%{parent}<br>id=%{id}<extra></extra>'
# But it seems to me you want something like
fig.data[0].hovertemplate = '%{label}<br>%{value}'
fig.show()

Related

Plotly: refresh data passed to px.line via dropdown from Database

I want to use the plotly express lineplot to create a simple interactive plot that refreshes its data when choosing a new item in a dropdown box.
I have created a simple function prepare_dashboard_data which loads data from a database and does some filtering. The dashboard is showing the data from the initial dataload but I am completely lost on how to create a callback to the px.line function, such that the plot is updated with new data loaded from the database.
I have taken inspiration from this post where plotly.graph_objs are used. But i quite like the functionality of the default lineplot.
And in the example a preloaded dataframe is simply filtered based on the dropdown choice. This is not what I want.
I have some limited knowledge with ipython widgets and the observer pattern, but I am completely lost in this case. Here is a rough sceme of my current code:
import plotly.graph_objs as go
import plotly.express as px
def prepare_dashboard_data(serverconfig, shopname = "myshop"):
# Data is loaded form a db
# Transformed filtered by shopname and so on ...
# This returns a datframe with has an timestamp as an index and many items as columns
return df
# Prepare Dropdown menues
shopnames = df.columns # This just gives a list of available shopnames.
# plotly start
fig = go.Figure()
fig = px.line(prepare_dashboard_data(serverconfig=conf, shopname = "myshop"),width=1600, height=800)
# menu setup
updatemenu= []
# buttons for menu 1, shopnames
buttons=[]
# should i create traces for each shopnames alread here:
for webshop_name in shopnames :
buttons.append(dict(method='update',
label=webshop_name,
visible=True,
args=[#{I guess here goes the magic?}
]
)
)
# some adjustments to the updatemenus
updatemenu=[]
your_menu=dict()
updatemenu.append(your_menu)
updatemenu[0]['buttons']=buttons
updatemenu[0]['direction']='down'
updatemenu[0]['showactive']=True
fig.update_layout(
autosize=False,
width=1800,
height=800,
margin=dict(
l=50,
r=50,
b=100,
t=100,
pad=4
),
updatemenus=updatemenu,
paper_bgcolor="LightSteelBlue",
)
fig.show()
Any help would be really appreciated. I have tried to make sense of the documentation but i think i would need a pointer. Currently i generate my plots in a Vscode/jupyter notebook, not as a standalone app .
You should use plotly-Dash to solve this. Here is an example you can tune to fit the structure of your dataframe.
from dash import Dash, dcc, html, Input, Output
import pandas as pd
import plotly.express as px
app = Dash(__name__)
app.layout = html.Div([
dcc.Dropdown(['a','b','c'], 'a', id='dropdown'),
dcc.Graph(id='graph')
])
df = pd.DataFrame(
{'a': [1,2,3,4,5],
'b': [-1,-2,-3,-4,-5],
'c': [2,4,6,8,10]
}
)
#app.callback(
Output(component_id='graph', component_property='figure'),
Input(component_id='dropdown', component_property='value')
)
def update_output(column):
fig = px.line(df, x=column)
return fig
if __name__ == '__main__':
app.run_server(debug=True)

How to de-dupe legend in faceted choropleth chart?

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
)
)

Dash update colors after figure is created

If I create a plotly express figure like so:
fig = px.line(data, color_discrete_map={"Gold": "gold","Silver": "silver"}),
it works fine.
But if I want to update the colors after the figure is created, like so:
fig = px.line(data)
fig.update_layout(color_discrete_map={"Gold": "gold", "Silver": "silver"})
I get
AttributeError: 'Figure' object has no attribute 'color'
I have also tried with update_traces() with no success.
What is the correct way to do this please?
When you create a figure with plotly.express, you receive a plotly.graph_objs figure.
You can pass the parameter color_discrete_map, which is used in the constructor of the express figure to set the colors of the different lines, but afterwards you only can change them through their plotly.graph_objects properties.
It becomes a lot clearer when you do this:
fig1 = px.line(data, color_discrete_map={"Gold": "gold","Silver": "silver"})
fig2 = px.line(data)
print(fig1)
print(fig2)
You will have to change the line_color property of the respective line. A solution could be to do it somewhat like this:
import plotly.express as px
import pandas as pd
data = pd.DataFrame({"Gold":[1, 2, 3], "Silver":[2, 1, 3]})
fig = px.line(data)
colors = {"Gold":"gold", "Silver":"silver"}
for linename, linecolor in colors.items():
for figline in fig.data:
if figline.name == linename:
figline.line.color = linecolor
fig.show()

How to get standard notation (rather than scientific) when hovering over pie chart in Plotly

I have a pie chart that displays worldwide movie sales by rating. When I hover over the chart the woldwide sales are being displayed in scientific notation. How do I fix this so that worldwide sales are represented in standard notation instead? I would appreciate it if anyone has a solution to this in express or graph objects (or both).
Thank you.
# formatting and importing data
import pandas as pd
movie_dataframe = pd.read_csv("https://raw.githubusercontent.com/NicholasTuttle/public_datasets/main/movie_data.csv") # importing dataset to dataframe
movie_dataframe['worldwide_gross'] = movie_dataframe['worldwide_gross'].str.replace(',', '', regex=True) # removing commas from column
movie_dataframe['worldwide_gross'] = movie_dataframe['worldwide_gross'].str.replace('$', '' , regex=True ) # removing dollar signs from column
movie_dataframe['worldwide_gross'] = movie_dataframe['worldwide_gross'].astype(float)
# narrowing dataframe to specific columns
movies_df = movie_dataframe.loc[:, ['title', 'worldwide_gross', 'rating', 'rt_score', 'rt_freshness']]
# plotly express
import plotly.express as px
fig = px.pie(movies_df,
values= movies_df['worldwide_gross'],
names= movies_df['rating'],
)
fig.show()
# plotly graph objects
import plotly.graph_objects as go
fig = go.Figure(go.Pie(
values = movies_df['worldwide_gross'],
labels = movies_df['rating']
))
fig.show()
Have a look here: https://plotly.com/python/hover-text-and-formatting/#disabling-or-customizing-hover-of-columns-in-plotly-express
Basically you give a dictionary of row name and format string to hover_data. The formatting string follows the d3-format's syntax.
import plotly.express as px
fig = px.pie(
movies_df, values= movies_df['worldwide_gross'], names= movies_df['rating'],
hover_data={
"worldwide_gross": ':.d',
# "worldwide_gross": ':.2f', # float
}
)
fig.show()
For the graph object API you need to write an hover_template:
https://plotly.com/python/reference/pie/#pie-hovertemplate
import plotly.graph_objects as go
fig = go.Figure(go.Pie(
values = movies_df['worldwide_gross'],
labels = movies_df['rating'],
hovertemplate='Rating: %{label}<br />World wide gross: %{value:d}<extra></extra>'
))
fig.show()

Add dropdown menu to plotly express treemap

I am currently trying to add a dropdown menu to my treemap plot
The code I am using :
import pandas as pd
import plotly.express as px
fig = px.treemap(df,
path=['RuleName','RuleNumber','ParaInvolved',"CreationP","MAjP"],
color='Somme',
hover_data=["RuleDecision","RuleMAJ"],
color_continuous_scale='RdBu')
fig.show()
The problem I am facing is that in my column "RuleName" I have 151 different values (but 1300 rows in total), that's why I'm trying to add a button allowing myself to chose for what RuleName value I want to plot my treemap. For now I am using a barbaric method consisting in filtering my dataframe by each RuleName value, which lead me to get 151 different treemap. I don't find any solution on that website or any other.
Thanks for your help
Here I'm basically using the same logic from this answer but I use px.treemap(...).data[0] to produce the traces instead of go.
import plotly.express as px
import plotly.graph_objects as go
df = px.data.tips()
# We have a list for every day
# In your case will be gropuby('RuleName')
# here for every element d
# d[0] is the name(key) and d[1] is the dataframe
dfs = list(df.groupby("day"))
first_title = dfs[0][0]
traces = []
buttons = []
for i,d in enumerate(dfs):
visible = [False] * len(dfs)
visible[i] = True
name = d[0]
traces.append(
px.treemap(d[1],
path=['day', 'time', 'sex'],
values='total_bill').update_traces(visible=True if i==0 else False).data[0]
)
buttons.append(dict(label=name,
method="update",
args=[{"visible":visible},
{"title":f"{name}"}]))
updatemenus = [{'active':0, "buttons":buttons}]
fig = go.Figure(data=traces,
layout=dict(updatemenus=updatemenus))
fig.update_layout(title=first_title, title_x=0.5)
fig.show()

Categories