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()
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.
I want to plot a scatter_mapbox plot over a choropleth_mapbox plot using plotly. I want to use Picnic for the scatter_mapbox plot's color scale. When I run this, the scatter_mapbox colorscale is set to the same properties as the choropleth_mapbox properties. Namely, it uses the color scale Viridis instead of Picnic and uses the choropleth's numerical scale. How can I make the the colorscale for the scatterplot Picnic.
import numpy as np
import pandas as pd
import geopandas as gpd
import plotly.express as px
import geopandas as gpd
import shapely
df = px.data.election()
df = gpd.GeoDataFrame.from_features(
px.data.election_geojson()["features"]
).merge(df, on="district").set_index("district")
df = df.loc[df['geometry'].map(lambda x: type(x) == shapely.geometry.polygon.Polygon)]
df2 = df.copy()
df2['geometry'] = df2['geometry'].map(lambda x: x.exterior.coords[0]).map(shapely.geometry.Point)
#make the charts
map_fig = px.choropleth_mapbox(
df,
geojson=df.geometry,
locations=df.index,
color='Bergeron',
center= { 'lon': df2.geometry.x.iloc[0], 'lat': df2.geometry.y.iloc[0]},
color_continuous_scale="Viridis",
mapbox_style="carto-positron",
opacity = 0.2,
)
map_fig2 = px.scatter_mapbox(
df2,
lat=df2.geometry.y,
lon=df2.geometry.x,
size='Bergeron',
zoom=12,
color='Bergeron', color_continuous_scale='Picnic',
opacity = 1,
size_max=10
)
map_fig.add_trace(map_fig2.data[0])
map_fig.update_geos(fitbounds="locations", visible=False)
map_fig.show()
A step in the right direction is to add this, which puts the scatter_mapbox on a separate coloraxis, but sets the color scale to the plotly default, Plasma, instead of Picnic, as specified. It also overlays the colorbar.
'color' : np.array(df2['Bergeron']),
'coloraxis' : 'coloraxis2',
'opacity' : 1,
'colorscale' : 'Picnic',
'sizemode' : 'area',
'sizeref' : .01,
'autocolorscale' : False
}
If this is what you're aiming to do:
Then follow these steps in addition to what you're already doing:
1. Steal the coloraxis from fig2 where color='Picnic' to fig with:
fig.layout.coloraxis2 = fig2.layout.coloraxis
2. Include the second trace with:
fig.add_trace(fig2.data[0])
3. Assign colors to the second trace with:
fig['data'][1]['marker'] = { 'color' : np.array(df2['Bergeron']),
'coloraxis' : 'coloraxis2',
}
4. Move the second colorbar to a more suitable place with:
fig.layout.coloraxis2.colorbar.x = -0.2
The third step makes the colors for the second trace available through 'coloraxis' : 'coloraxis2'
I hope this is what you were looking for. Don't hesitate to let me know if not!
Complete code:
(Sorry, I got tired of typing map_fig so I change the references to merely fig)
import numpy as np
import pandas as pd
import geopandas as gpd
import plotly.express as px
import geopandas as gpd
import shapely
df = px.data.election()
df = gpd.GeoDataFrame.from_features(
px.data.election_geojson()["features"]
).merge(df, on="district").set_index("district")
df = df.loc[df['geometry'].map(lambda x: type(x) == shapely.geometry.polygon.Polygon)]
df2 = df.copy()
df2['geometry'] = df2['geometry'].map(lambda x: x.exterior.coords[0]).map(shapely.geometry.Point)
#make the charts
fig = px.choropleth_mapbox(
df,
geojson=df.geometry,
locations=df.index,
color='Bergeron',
center= { 'lon': df2.geometry.x.iloc[0], 'lat': df2.geometry.y.iloc[0]},
color_continuous_scale="Viridis",
mapbox_style="carto-positron",
opacity = 0.2,
)
fig2 = px.scatter_mapbox(
df2,
lat=df2.geometry.y,
lon=df2.geometry.x,
size='Bergeron',
zoom=12,
color='Bergeron',
color_continuous_scale='picnic',
opacity = 1,
size_max=10
)
fig.add_trace(fig2.data[0])
fig.layout.coloraxis2 = fig2.layout.coloraxis
fig['data'][1]['marker'] = { 'color' : np.array(df2['Bergeron']),
'coloraxis' : 'coloraxis2',
'opacity' : 1,
'sizemode' : 'area',
'sizeref' : .01,
'autocolorscale' : False
}
fig.update_geos(fitbounds="locations", visible=False)
fig.layout.coloraxis2.colorbar.x = -0.2
fig.show()
I am trying to create a plot using plotly with multiple axes. And for this, I am using the following code:
#Plotly libraries and options for graphic logic
from plotly.io import to_html
import plotly.io as pio
pio.renderers.default='browser'
import plotly.graph_objects as go
#Generic libraries
import pandas as pd
import numpy as np
from datetime import datetime
input_df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
threshold =2.8
name_yaxis="Gap"
input_df["AAPL.High"] = (input_df["AAPL.High"]-min(input_df["AAPL.High"]))*(threshold)/(max(input_df["AAPL.High"])-min(input_df["AAPL.High"]))+np.random.uniform(0.3,0.4,1)
ID_TAIL = "ID_1"
fig = go.Figure()
fig.add_trace(go.Scatter(x=input_df['Date'], y=input_df['AAPL.High'],
mode='lines+markers',
marker_size=12,
line = dict(color="#C4C4C4"),
marker=dict(color=( (0 < input_df['AAPL.High']) & (input_df['AAPL.High'] < threshold)).astype('int'),
colorscale=[[0, '#A51890'], [1, '#3BBFFE']]
),
showlegend=False,
xaxis="x1",
name = ""
)
)
my_x = [ID_TAIL + "_" +format(i, '04d') + "_0" for i in range(1,input_df.shape[0])]
fig.add_trace(go.Scatter(x=my_x, y=input_df['AAPL.High'],
mode='lines+markers',
marker_size=12,
line = dict(color="#C4C4C4"),
marker=dict(color=( (0 < input_df['AAPL.High']) & (input_df['AAPL.High'] < threshold)).astype('int'),
colorscale=[[0, '#A51890'], [1, '#3BBFFE']]
),
showlegend=False,
xaxis="x2",
name = ""
)
)
#== Add title boxes ==#
# Add title legend for box status
fig.add_annotation( text="<b>Health status<b>", xref="paper", yref="paper",
x=1.02, xanchor="left",
y=0.9, yanchor="bottom", # Same y as legend below
showarrow=False,
font = dict(family = "Roboto", size = 10))
#== End ==#
My problem is that as you can see in the following image, the ticks are overlapping:
So, my question is, how to create space between them?
Thanks in advance.
Here's a quick fix. Pop this line at the bottom of your code, and it will move xaxis2 to the top of the graph:
fig.update_layout({'xaxis2': {'side': 'top', 'tickangle': 45, 'nticks': 50}})
Output:
Shifting the secondary xaxis to the top will look like this.
Another Option:
Another approach would be to concatenate the axis titles into a single string, and display the concatenated string on the x-axis. This SO answer demonstrates this logic.
You can reduce the number of ticks by adding the following line
fig.update_layout(xaxis={'nticks': 8, 'tickangle': 90}, xaxis2={'nticks': 8, 'tickangle': 90})
Depending on the size of the plot, ticks may still overlap. In that case, you can either further reduce the tick number or hardcode the tick positions:
tickvalsX = ['2015-07', '2016-01', '2016-07', '2017-01']
tickvalsY = ['ID_1_0001_0', 'ID_1_00100_0', 'ID_1_0200_0', 'ID_1_0300_0', 'ID_1_0400_0', 'ID_1_0500_0']
fig.update_layout(xaxis={'tickmode': 'array', 'tickangle': 90, 'tickvals': tickvalsX}, xaxis2={'tickmode': 'array', 'tickangle': 90, 'tickvals': tickvalsY})
Further style elements of the axis you can find in the Plotly reference.
These resources show how to take data from a single Pandas DataFrame and plot different columns subplots on a Plotly graph. I'm interested in creating figures from separate DataFrames and plotting them to the same graph as subplots. Is this possible with Plotly?
https://plot.ly/python/subplots/
https://plot.ly/pandas/subplots/
I'm creating each figure from a dataframe like this:
import pandas as pd
import cufflinks as cf
from plotly.offline import download_plotlyjs, plot,iplot
cf.go_offline()
fig1 = df.iplot(kind='bar',barmode='stack',x='Type',
y=mylist,asFigure=True)
Edit:
Here is an example based on Naren's feedback:
Create the dataframes:
a={'catagory':['loc1','loc2','loc3'],'dogs':[1,5,6],'cats':[3,1,4],'birds':[4,12,2]}
df1 = pd.DataFrame(a)
b={'catagory':['loc1','loc2','loc3'],'dogs':[12,3,5],'cats':[4,6,1],'birds':[7,0,8]}
df2 = pd.DataFrame(b)
The plot will just show the information for the dogs, not the birds or cats:
fig = tls.make_subplots(rows=2, cols=1)
fig1 = df1.iplot(kind='bar',barmode='stack',x='catagory',
y=['dogs','cats','birds'],asFigure=True)
fig.append_trace(fig1['data'][0], 1, 1)
fig2 = df2.iplot(kind='bar',barmode='stack',x='catagory',
y=['dogs','cats','birds'],asFigure=True)
fig.append_trace(fig2['data'][0], 2, 1)
iplot(fig)
Here's a short function in a working example to save a list of figures all to a single HTML file.
def figures_to_html(figs, filename="dashboard.html"):
with open(filename, 'w') as dashboard:
dashboard.write("<html><head></head><body>" + "\n")
for fig in figs:
inner_html = fig.to_html().split('<body>')[1].split('</body>')[0]
dashboard.write(inner_html)
dashboard.write("</body></html>" + "\n")
# Example figures
import plotly.express as px
gapminder = px.data.gapminder().query("country=='Canada'")
fig1 = px.line(gapminder, x="year", y="lifeExp", title='Life expectancy in Canada')
gapminder = px.data.gapminder().query("continent=='Oceania'")
fig2 = px.line(gapminder, x="year", y="lifeExp", color='country')
gapminder = px.data.gapminder().query("continent != 'Asia'")
fig3 = px.line(gapminder, x="year", y="lifeExp", color="continent",
line_group="country", hover_name="country")
figures_to_html([fig1, fig2, fig3])
You can get a dashboard that contains several charts with legends next to each one:
import plotly
import plotly.offline as py
import plotly.graph_objs as go
fichier_html_graphs=open("DASHBOARD.html",'w')
fichier_html_graphs.write("<html><head></head><body>"+"\n")
i=0
while 1:
if i<=40:
i=i+1
#______________________________--Plotly--______________________________________
color1 = '#00bfff'
color2 = '#ff4000'
trace1 = go.Bar(
x = ['2017-09-25','2017-09-26','2017-09-27','2017-09-28','2017-09-29','2017-09-30','2017-10-01'],
y = [25,100,20,7,38,170,200],
name='Debit',
marker=dict(
color=color1
)
)
trace2 = go.Scatter(
x=['2017-09-25','2017-09-26','2017-09-27','2017-09-28','2017-09-29','2017-09-30','2017-10-01'],
y = [3,50,20,7,38,60,100],
name='Taux',
yaxis='y2'
)
data = [trace1, trace2]
layout = go.Layout(
title= ('Chart Number: '+str(i)),
titlefont=dict(
family='Courier New, monospace',
size=15,
color='#7f7f7f'
),
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)',
yaxis=dict(
title='Bandwidth Mbit/s',
titlefont=dict(
color=color1
),
tickfont=dict(
color=color1
)
),
yaxis2=dict(
title='Ratio %',
overlaying='y',
side='right',
titlefont=dict(
color=color2
),
tickfont=dict(
color=color2
)
)
)
fig = go.Figure(data=data, layout=layout)
plotly.offline.plot(fig, filename='Chart_'+str(i)+'.html',auto_open=False)
fichier_html_graphs.write(" <object data=\""+'Chart_'+str(i)+'.html'+"\" width=\"650\" height=\"500\"></object>"+"\n")
else:
break
fichier_html_graphs.write("</body></html>")
print("CHECK YOUR DASHBOARD.html In the current directory")
Result:
You can also try the following using cufflinks:
cf.subplots([df1.figure(kind='bar',categories='category'),
df2.figure(kind='bar',categories='category')],shape=(2,1)).iplot()
And this should give you:
New Answer:
We need to loop through each of the animals and append a new trace to generate what you need. This will give the desired output I am hoping.
import pandas as pd
import numpy as np
import cufflinks as cf
import plotly.tools as tls
from plotly.offline import download_plotlyjs, plot,iplot
cf.go_offline()
import random
def generate_random_color():
r = lambda: random.randint(0,255)
return '#%02X%02X%02X' % (r(),r(),r())
a={'catagory':['loc1','loc2','loc3'],'dogs':[1,5,6],'cats':[3,1,4],'birds':[4,12,2]}
df1 = pd.DataFrame(a)
b={'catagory':['loc1','loc2','loc3'],'dogs':[12,3,5],'cats':[4,6,1],'birds':[7,0,8]}
df2 = pd.DataFrame(b)
#shared Xaxis parameter can make this graph look even better
fig = tls.make_subplots(rows=2, cols=1)
for animal in ['dogs','cats','birds']:
animal_color = generate_random_color()
fig1 = df1.iplot(kind='bar',barmode='stack',x='catagory',
y=animal,asFigure=True,showlegend=False, color = animal_color)
fig.append_trace(fig1['data'][0], 1, 1)
fig2 = df2.iplot(kind='bar',barmode='stack',x='catagory',
y=animal,asFigure=True, showlegend=False, color = animal_color)
#if we do not use the below line there will be two legend
fig2['data'][0]['showlegend'] = False
fig.append_trace(fig2['data'][0], 2, 1)
#additional bonus
#use the below command to use the bar chart three mode
# [stack, overlay, group]
#as shown below
#fig['layout']['barmode'] = 'overlay'
iplot(fig)
Output:
Old Answer:
This will be the solution
Explanation:
Plotly tools has a subplot function to create subplots you should read the documentation for more details here. So I first use cufflinks to create a figure of the bar chart. One thing to note is cufflinks create and object with both data and layout. Plotly will only take one layout parameter as input, hence I take only the data parameter from the cufflinks figure and append_trace it to the make_suplots object. so fig.append_trace() the second parameter is row number and third parameter is column number
import pandas as pd
import cufflinks as cf
import numpy as np
import plotly.tools as tls
from plotly.offline import download_plotlyjs, plot,iplot
cf.go_offline()
fig = tls.make_subplots(rows=2, cols=1)
df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
fig1 = df.iplot(kind='bar',barmode='stack',x='A',
y='B',asFigure=True)
fig.append_trace(fig1['data'][0], 1, 1)
df2 = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('EFGH'))
fig2 = df2.iplot(kind='bar',barmode='stack',x='E',
y='F',asFigure=True)
fig.append_trace(fig2['data'][0], 2, 1)
iplot(fig)
If you want to add a common layout to the subplot I suggest that you do
fig.append_trace(fig2['data'][0], 2, 1)
fig['layout']['showlegend'] = False
iplot(fig)
or even
fig.append_trace(fig2['data'][0], 2, 1)
fig['layout'].update(fig1['layout'])
iplot(fig)
So in the first example before plotting, I access the individual parameters of the layout object and change them, you need to go through layout object properties for refernce.
In the second example before plotting, I update the layout of the figure with the cufflinks generated layout this will produce the same output as we see in cufflinks.
You've already received a few suggestions that work perfectly well. They do however require a lot of coding. Facet / trellis plots using px.bar() will let you produce the plot below using (almost) only this:
px.bar(df, x="category", y="dogs", facet_row="Source")
The only extra steps you'll have to take is to introduce a variable on which to split your data, and then gather or concatenate your dataframes like this:
df1['Source'] = 1
df2['Source'] = 2
df = pd.concat([df1, df2])
And if you'd like to include the other variables as well, just do:
fig = px.bar(df, x="category", y=["dogs", "cats", "birds"], facet_row="Source")
fig.update_layout(barmode = 'group')
Complete code:
# imports
import plotly.express as px
import pandas as pd
# data building
a={'category':['loc1','loc2','loc3'],'dogs':[1,5,6],'cats':[3,1,4],'birds':[4,12,2]}
df1 = pd.DataFrame(a)
b={'category':['loc1','loc2','loc3'],'dogs':[12,3,5],'cats':[4,6,1],'birds':[7,0,8]}
df2 = pd.DataFrame(b)
# data processing
df1['Source'] = 1
df2['Source'] = 2
df = pd.concat([df1, df2])
# plotly figure
fig = px.bar(df, x="category", y="dogs", facet_row="Source")
fig.show()
#fig = px.bar(df, x="category", y=["dogs", "cats", "birds"], facet_row="Source")
#fig.update_layout(barmode = 'group')