Plotly timeline with objects - python

In the below example, I would like to group the elements of y axis by continent, and to display the name of the continent at the top of each group. I can't figure out in the layout where we can set it. the example come from this plotly page
import pandas as pd
import plotly.graph_objects as go
from plotly import data
df = data.gapminder()
df = df.loc[ (df.year.isin([1987, 2007]))]
countries = (
df.loc[ (df.year.isin([2007]))]
.sort_values(by=["pop"], ascending=True)["country"]
.unique()
)[5:-10]
data = {"x": [], "y": [], "colors": [], "years": []}
for country in countries:
data["x"].extend(
[
df.loc[(df.year == 1987) & (df.country == country)]["pop"].values[0],
df.loc[(df.year == 2007) & (df.country == country)]["pop"].values[0],
None,
]
)
data["y"].extend([country, country, None]),
data["colors"].extend(["cyan", "darkblue", "white"]),
data["years"].extend(["1987", "2007", None])
fig = go.Figure(
data=[
go.Scatter(
x=data["x"],
y=data["y"],
mode="lines",
marker=dict(
color="grey",
)),
go.Scatter(
x=data["x"],
y=data["y"],
text=data["years"],
mode="markers",
marker=dict(
color=data["colors"],
symbol=["square","circle","circle"]*10,
size=16
),
hovertemplate="""Country: %{y} <br> Population: %{x} <br> Year: %{text} <br><extra></extra>"""
)
]
)

To show grouping by continent instead of the code you showed would require looping through the data structure from dictionary format to data frame. y-axis by continent by specifying a multi-index for the y-axis.
I have limited myself to the top 5 countries by continent because the large number of categorical variables on the y-axis creates a situation that is difficult to see for visualization. You can rewrite/not set here according to your needs. Furthermore, in terms of visualization, I have set the x-axis type to log format because the large discrepancies in the numbers make the visualization weaker. This is also something I added on my own and you can edit it yourself.
import pandas as pd
import plotly.graph_objects as go
from plotly import data
df = data.gapminder()
df = df.loc[(df.year.isin([1987, 2007]))]
# top5 by continent
countries = (df.loc[df.year.isin([2007])]
.groupby(['continent',], as_index=False, sort=[True])[['country','pop']].head()['country']
)
df = df[df['country'].isin(countries.tolist())]
fig = go.Figure()
for c in df['continent'].unique():
dff = df.query('continent == #c')
#print(dff)
for cc in dff['country'].unique():
dfc = dff.query('country == #cc')
fig.add_trace(go.Scatter(x=dfc['pop'].tolist(),
y=[dfc['continent'],dfc['country']],
mode='lines+markers',
marker=dict(
color='grey',
))
)
fig.add_trace(go.Scatter(x=dfc['pop'].tolist(),
y=[dfc['continent'],dfc['country']],
text=dfc["year"],
mode="markers",
marker=dict(
color=["cyan", "darkblue", "white"],
size=16,
))
)
fig.update_layout(autosize=False, height=800, width=800, showlegend=False)
fig.update_xaxes(type='log')
fig.show()

Related

3d animated line plot with plotly in python

I saw this 3d plot. it was animated and added a new value every day. i have not found an example to recreate it with plotly in python.
the plot should start with the value from the first row (100). The start value should remain (no rolling values). The plot should be animated in such a way that each row value is added one after the other and the x-axis expands. the following data frame contains the values (df_stocks) and Dates to plot. assigning the colors would be a great addition. the more positive the deeper the green, the more negative the darker red.
import yfinance as yf
import pandas as pd
stocks = ["AAPL", "MSFT"]
df_stocks = pd.DataFrame()
for stock in stocks:
df = yf.download(stock, start="2022-01-01", end="2022-07-01", group_by='ticker')
df['perct'] = df['Close'].pct_change()
df_stocks[stock] = df['perct']
df_stocks.iloc[0] = 0
df_stocks += 1
df_stocks = df_stocks.cumprod()*100
df_stocks -= 100
You can use a list of go.Frame objects as shown in this example. Since you want the line plot to continually extend outward, each frame needs to include data that's one row longer than the previous frame, so we can use a list comprehension like:
frames = [go.Frame(data=
## ...extract info from df_stocks.iloc[:i]
for i in range(len(df_stocks))]
To add colors to your lines depending on their value, you can use binning and labels (as in this answer) to create new columns called AAPL_color and MSFT_color that contain the string of the css color (like 'darkorange' or 'green'). Then you can pass the information from these columns using the argument line=dict(color=...) in each go.Scatter3d object.
import yfinance as yf
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
stocks = ["AAPL", "MSFT"]
df_stocks = pd.DataFrame()
for stock in stocks:
df = yf.download(stock, start="2022-01-01", end="2022-07-01", group_by='ticker')
df['perct'] = df['Close'].pct_change()
df_stocks[stock] = df['perct']
df_stocks.iloc[0] = 0
df_stocks += 1
df_stocks = df_stocks.cumprod()*100
df_stocks -= 100
df_min = df_stocks[['AAPL','MSFT']].min().min() - 1
df_max = df_stocks[['AAPL','MSFT']].max().max() + 1
labels = ['firebrick','darkorange','peachpuff','palegoldenrod','palegreen','green']
bins = np.linspace(df_min,df_max,len(labels)+1)
df_stocks['AAPL_color'] = pd.cut(df_stocks['AAPL'], bins=bins, labels=labels).astype(str)
df_stocks['MSFT_color'] = pd.cut(df_stocks['MSFT'], bins=bins, labels=labels).astype(str)
frames = [go.Frame(
data=[
go.Scatter3d(
y=df_stocks.iloc[:i].index,
z=df_stocks.iloc[:i].AAPL.values,
x=['AAPL']*i,
name='AAPL',
mode='lines',
line=dict(
color=df_stocks.iloc[:i].AAPL_color.values, width=3,
)
),
go.Scatter3d(
y=df_stocks.iloc[:i].index,
z=df_stocks.iloc[:i].MSFT.values,
x=['MSFT']*i,
name='MSFT',
mode='lines',
line=dict(
color=df_stocks.iloc[:i].MSFT_color.values, width=3,
)
)]
)
for i in range(len(df_stocks))]
fig = go.Figure(
data=list(frames[1]['data']),
frames=frames,
layout=go.Layout(
# xaxis=dict(range=[0, 5], autorange=False),
# yaxis=dict(range=[0, 5], autorange=False),
# zaxis=dict(range=[0, 5], autorange=False),
template='plotly_dark',
legend = dict(bgcolor = 'grey'),
updatemenus=[dict(
type="buttons",
font=dict(color='black'),
buttons=[dict(label="Play",
method="animate",
args=[None])])]
),
)
fig.show()

Distance between title and the plot in radarplot plotly

I want to increase the distance between the title of radar plot with the plot itself but I don't know how to do it.
Reproducible code:
import pandas as pd
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.io as pio
pio.renderers.default = "browser"
# Create a random data frame with 4 rows and 4 columns
# Column names are M1:M4
# Row names are A, B, C, D
np.random.seed(123)
df = pd.DataFrame(
np.random.randint(0, 100, size=(4, 4)),
columns=["M1", "M2", "M3", "M4"],
index=list("ABCD")
)
# Create a list of tuples. each tuple shows the position of each
# column in the radar plot. The first element of each tuple is the
# row indicator and the second element is the column indicator.
# Number of radars to be plotted
n_rads = len(df.columns)
positions = [(i//(n_rads//2)+1, i%(n_rads//2)+1) for i in range(n_rads)]
mm_cols = df.apply(lambda x: (min(x), max(x))).values.T
# Create a figure with 2 rows and 2 columns
fig = make_subplots(
rows=2, cols=2,
specs=[
[{"type": "polar"}]*2,
[{"type": "polar"}]*2
],
subplot_titles=df.columns,
)
# add the traces to the figure
for idx, col in enumerate(df.columns):
fig.add_trace(
go.Scatterpolar(
r=df[col],
theta=df.index,
fill='toself',
name=col
),
row=positions[idx][0],
col=positions[idx][1]
)
fig.update_layout(
polar=dict(
radialaxis=dict(
visible=True,
range=mm_cols[idx]
)
),
showlegend=True
)
fig.show()
The following is the snapshot of the first radar and I show what I mean by the "distance between the title of radar plot with the plot itself":
Note that I'm using make_subplots.

Control a Plotly line color according to its value?

I'm creating a dashboard in which I would like to compare the difference of price between two regions directly. If the price of region 1 is higher, y is POSITIVE, if the price of region 2 is higher, y is NEGATIVE.
The problem is that I would like the line and its fill to change color accordingly to its value, so it has a better representation.
I'm using fill='tozeroy'. I would like y-negative = red and y-positive = blue, for the lines and the fill.
def func(est1, est2):
est1, est2 = 'RIO GRANDE DO SUL', 'SANTA CATARINA' # filter to simulate the callback
df1 = df[df.ESTADO.isin([est1])]
df2 = df[df.ESTADO.isin([est2])]
df_final = pd.DataFrame()
df_estado1 = df1.groupby(pd.PeriodIndex(df1['DATA'], freq="M"))['VALOR REVENDA (R$/L)'].mean().reset_index()
df_estado2 = df2.groupby(pd.PeriodIndex(df2['DATA'], freq="M"))['VALOR REVENDA (R$/L)'].mean().reset_index()
df_estado1['DATA'] = pd.PeriodIndex(df_estado1['DATA'], freq="M")
df_estado2['DATA'] = pd.PeriodIndex(df_estado2['DATA'], freq="M")
df_final['DATA'] = df_estado1['DATA'].astype('datetime64[ns]')
df_final['VALOR REVENDA (R$/L)'] = df_estado1['VALOR REVENDA (R$/L)']-df_estado2['VALOR REVENDA (R$/L)']
fig = go.Figure()
fig.add_trace(go.Scatter(name='Comparação', y=df_final['VALOR REVENDA (R$/L)'], x=df_final['DATA'],
fill='tozeroy', mode='lines'))
return fig
Just for help porpouses, that's the "df_final" format which is returned:
df_final DataFrame
Here's the graph that is being returned from the function: graph returned
Also, how can I style my fill? Maybe add some gradient etc
I found this Plotly reference library, where I scraped the information I'm answering you with: https://plotly.com/python/creating-and-updating-figures/#plotly-express
import plotly.express as px
df = px.data.iris()
fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species",
title="Using The add_trace() method With A Plotly Express Figure")
fig.add_trace(
go.Scatter(
x=[2, 4],
y=[4, 8],
mode="lines",
line=go.scatter.Line(color="gray"),
showlegend=False)
)
fig.show()
Basically, if you put "df_final" in the place of df, and change the axis's data, you'll be good to go.

Plotly: Setting the marker size based on the column in the exported data?

The code is running well; however, in my dataset, there is a column SD in my custom dataset. I would like the size of these markers should be based on SD and I did it in the seaborn library, it is running well. However, I get errors here.
%Error is
Did you mean "line"?
Bad property path:
size
^^^^
Code is
df=pd.read_csv("Lifecycle.csv")
df1=df[df["Specie"]=="pot_marigold"]
df1
df2=df[df["Specie"]=="Sunflowers"]
df2
trace=go.Scatter(x=df1["Days"], y=df1["Lifecycle"],text=df1["Specie"],marker={"color":"green"}, size=df1[SD],
mode="lines+markers")
trace1=go.Scatter(x=df2["Days"], y=df2["Lifecycle"],text=df2["Specie"],marker={"color":"red"},
mode="lines+markers")
data=[trace,trace1]
layout=go.Layout(
title="Lifecycle",
xaxis={"title":"Days"},
yaxis={"title":"Lifecycle"})
fig=go.Figure(data=data,layout=layout)
pyo.plot(fig)
you have not provided sample data, so I have simulated based on what I can imply from your code
simply you can set marker_size within framework you have used
this type of plot is far simpler with Plotly Express have also shown code for this
import pandas as pd
import numpy as np
import plotly.graph_objects as go
# df=pd.read_csv("Lifecycle.csv")
df = pd.DataFrame(
{
"Specie": np.repeat(["pot_marigold", "Sunflowers"], 10),
"Days": np.tile(np.arange(1, 11, 1), 2),
"Lifecycle": np.concatenate(
[np.sort(np.random.uniform(1, 5, 10)).astype(int) for _ in range(2)]
),
"SD": np.random.randint(1, 8, 20),
}
)
df1 = df[df["Specie"] == "pot_marigold"]
df2 = df[df["Specie"] == "Sunflowers"]
trace = go.Scatter(
x=df1["Days"],
y=df1["Lifecycle"],
text=df1["Specie"],
marker={"color": "green"},
marker_size=df1["SD"],
mode="lines+markers",
)
trace1 = go.Scatter(
x=df2["Days"],
y=df2["Lifecycle"],
text=df2["Specie"],
marker={"color": "red"},
mode="lines+markers",
)
data = [trace, trace1]
layout = go.Layout(
title="Lifecycle", xaxis={"title": "Days"}, yaxis={"title": "Lifecycle"}
)
fig = go.Figure(data=data, layout=layout)
fig
Plotly Express
import plotly.express as px
px.scatter(
df,
x="Days",
y="Lifecycle",
color="Specie",
size="SD",
color_discrete_map={"pot_marigold": "green", "Sunflowers": "red"},
).update_traces(mode="lines+markers")
You can use plotly.express instead:
import plotly.express as px
trace=px.scatter(df, x="Days", y="Lifecycle", text="Specie", marker="SD")

Plotly go.Bar : Add custom legend labels based on values

I have a dataframe with positive and negative values in one column. I am using plotly barplot, and I'd like customize legend labels based on the value.
Here's a mock pandas DataFrame:
df = pd.DataFrame({'Date': [07-2020, 08-2020, 09-2020, 10-2020],
'Value': [3, -2, 4, -1] })
df["Color"] = np.where(df["Value"]<0, 'rgb(0,0,255)', 'rgb(255,0,0)')
df["Name"] = np.where(df["Value"]<0, 'Low', 'High')
fig = go.Figure(
data=[
go.Bar(
x=df["Date"],
y=df["Value"],
color=df['Name'],
marker_color=df['Color']
),
],
layout=go.Layout(
xaxis=dict(
tickangle=60,
tickfont=dict(family="Rockwell", color="crimson", size=14)
),
yaxis=dict(
title="Net Change",
showticklabels=True
),
barmode="stack",
)
)
How do I add legend labels Low when value is negative and High when positive?
I wasn't sure if your legend label was a legend or an annotation label, so I added support for both. To annotate a bar chart, you can specify it in the text The display position will automatically determine the location. To add high and low to the legend, I created a high data frame and a low data frame and gave each a name. As a layout, we specify the tick positions and display names in order to arrange them in data frame order.
import pandas as pd
import plotly.graph_objects as go
import numpy as np
df = pd.DataFrame({'Date': ['07-2020', '08-2020', '09-2020', '10-2020'], 'Value': [3, -2, 4, -1] })
df["Color"] = np.where(df["Value"]<0, 'rgb(0,0,255)', 'rgb(255,0,0)')
df["Name"] = np.where(df["Value"]<0, 'Low', 'High')
df_high = df[df['Name'] == 'High']
df_Low = df[df['Name'] == 'Low']
fig = go.Figure(data=[
go.Bar(
x=[0,2],
y=df_high["Value"],
text=df_high["Name"],
textposition='auto',
name='High',
marker_color=df_high['Color']
),],)
fig.add_trace(
go.Bar(
x=[1,3],
y=df_Low["Value"],
text=df_Low["Name"],
textposition='auto',
name='Low',
marker_color=df_Low['Color'])
)
fig.update_layout(
xaxis=dict(
tickangle=60,
tickfont=dict(family="Rockwell", color="crimson", size=14),
tickvals=[0,1,2,3],
ticktext=df['Date']
),
yaxis=dict(
title="Net Change",
showticklabels=True
),
barmode="stack",
)
fig.show()

Categories

Resources