I would like to get a plot with more than two different y-axes in seaborn using a pandas dataframe similar to this example for matlotlib: https://matplotlib.org/examples/axes_grid/demo_parasite_axes2.html
As it will be used in a function I want to be flexible in selecting how many and which column of a Pandas dataframe will be ploted.
Unfortunately Seaborn seems to only move the last added scale.
Here is what I want to do with a Seaborn sample dataset:
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
import seaborn as sns
df=sns.load_dataset("mpg")
df=df.loc[df['model_year']<78]
show=['mpg','displacement','acceleration']
sns.set(rc={'figure.figsize':(11.7,8.27)})
sns.scatterplot('weight',show[0],data=df.reset_index(),style='model_year')
del show[0]
k=1
off=0
for i in show:
a = plt.twinx()
a=sns.scatterplot('weight',i,data=df.reset_index(),ax=a, color=list(mcolors.TABLEAU_COLORS)[k],legend=False,style='model_year')
a.spines['right'].set_position(('outward', off))
a.yaxis.label.set_color(list(mcolors.TABLEAU_COLORS)[k])
k+=1
off+=60
I want to create a function with the possibility to flexible plot different columns. Up to now this seems to be quite complicated in plotly to me (no way of just do a loop). I would also go with plotly, if there is a good way.
There is actually a good way in Plotly, you can see the code example for the picture below, similar to your matplotlib example in this section of the docs.
I now implemented this using plotly.
import seaborn as sns
import plotly.graph_objects as go
df=sns.load_dataset("mpg")
show=['mpg','displacement','acceleration']
mcolors=[
'#1f77b4', # muted blue
'#ff7f0e', # safety orange
'#2ca02c', # cooked asparagus green
'#d62728', # brick red
'#9467bd', # muted purple
'#8c564b', # chestnut brown
'#e377c2', # raspberry yogurt pink
'#7f7f7f', # middle gray
'#bcbd22', # curry yellow-green
'#17becf' # blue-teal
];
fig = go.Figure()
m=0
for k in df.model_year.unique():
fig.add_trace(go.Scatter(
x = df.loc[df.model_year == k]['weight'],
y = df.loc[df.model_year == k][show[0]],
name = str(k),
mode = 'markers',
marker_symbol=m,
marker_line_width=0,
marker_size=6,
marker_color=mcolors[0],
))
m+=1
layout = {'xaxis':dict(
domain=[0,0.7]
),
'yaxis':dict(
title=show[0],
titlefont=dict(
color=mcolors[0]
),
tickfont=dict(
color=mcolors[0]
),
showgrid=False
)}
n=2
for i in show[1::]:
m=0
for k in df.model_year.unique():
fig.add_trace(go.Scatter(
x = df.loc[df.model_year == k]['weight'],
y = df.loc[df.model_year == k][i],
name = str(k),
yaxis ='y'+str(n),
mode = 'markers',
marker_symbol=m,
marker_line_width=0,
marker_size=6,
marker_color=mcolors[n],
showlegend = False
))
m+=1
layout['yaxis'+str(n)] = dict(
title=i,
titlefont=dict(
color=mcolors[n]
),
tickfont=dict(
color=mcolors[n]
),
anchor="free",
overlaying="y",
side="right",
position=(n)*0.08+0.55,
showgrid=False,
)
n+=1
fig.update_layout(**layout)
fig.show()
Related
I want use a for cycle to call a charting function and then represent the outcome chart into a section of a multi charting pageExample single chart
expected outcome
I have a charting function (see below Charting Function Section) that i recall in a the main script with a for cycle to get several charts in sequence. Now I would like to represent all the charts, in compact size (2 columns 4 rows) in one single page. In literature I find that Subplot allows me to do so but I struggle to find the right command to represent the outcome from the charting function.
I thought something like the below in the Main Section would work but it is not
---------- Main Section ---------
import pandas as pd
import numpy as np
import plotly.graph_objs as go
from sklearn.cluster import KMeans
from plotly.subplots import make_subplots
for cont in range(8):
fig = charting_func(cont)
fig_all.add_trace(fig,
row=1, col=1
) #row and col incrementing function to be defined
fig_all.update_layout(height=600, width=800, title_text="Side By Side Subplots")
fig_all.show()
----- Charting Function ------
def charting_func(n_chrt):
# Arbitrarily 10 colors for up to 10 clusters
#colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet', 'purple','pink', 'silver']
# Create Scatter plot, assigning each point a color where
# point group = color index.
fig = btc.plot.scatter(
x=btc.index,
y="Adj Close",
color=[colors[i] for i in lists_clusters[n_chrt]],
title="k-values = {0}".format(n_chrt+2)
)
# Add horizontal lines
for cluster_avg in output[n_chrt][1:-1]:
fig.add_hline(y=cluster_avg, line_width=1, line_color="blue")
# Add a trace of the price for better clarity
fig.add_trace(go.Scatter(
x=btc.index,
y=btc['Adj Close'],
line_color="black",
line_width=1
))
# Make it pretty
layout = go.Layout(
plot_bgcolor='#D9D9D9',
showlegend=False,
# Font Families
font_family='Monospace',
font_color='#000000',
font_size=20,
xaxis=dict(
rangeslider=dict(
visible=False
))
)
fig.update_layout(layout)
return fig
type here
The basic form of a subplot is to add a location arrangement to the graph setup. So the functionalization needs to have matrix information or something like that. I have no data to present, so I have taken the stock prices of 4 companies and graphed them. As for the clustering by price, it is not included in the code, so the binning process is used to get the values and labels for the horizontal line. Please rewrite this part to your own logic. If you are good enough, the functionalization should work well.
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import itertools
import yfinance as yf
stock = ['TSLA','MSFT','AAPL','AMD']
#fig = go.Figure()
fig = make_subplots(rows=2, cols=2, subplot_titles=['MSFT','TSLA','AMD','AAPL'])
for s,rc in zip(stock, itertools.product([1,2],[2,1])):
#print(s, rc[0], rc[1])
df = yf.download(s, start="2017-09-01", end="2022-04-01", interval='1mo', progress=False)
colors = ['blue', 'red', 'green', 'purple', 'orange']
s_cut, bins = pd.cut(df['Adj Close'], 5, retbins=True, labels=colors)
fig.add_trace(go.Scatter(mode='markers+lines',
x=df.index,
y=df['Adj Close'],
marker=dict(
size=10,
color=s_cut.tolist()
)),
row=rc[0], col=rc[1]
)
for b in bins[1:-1]:
fig.add_hline(y=b, line_width=1, line_color="blue", row=rc[0], col=rc[1])
fig.update_layout(autosize=True, height=600, title_text="Side By Side Subplots")
fig.show()
I have a vertical line plot of Lithology data (x=Lithology, y=Depth) and a dictionary with colors and patterns. I need to fill my line plot using plotly and patterns:colors from my dictionary, like one in this picture on the right side. In matplotlib this achieved with ax.fill_betweenx().
ax.fill_betweenx(well['DEPTH_MD'], 0, well['LITHOLOGY'],
where=(well['LITHOLOGY']==key),
facecolor=color, hatch=hatch)
How can it be done in plotly?
Well Logs
A workaround:
import numpy as np
import plotly.graph_objects as go
depth = list(range(2900, 3100, 5))
density1 = np.random.random_sample((len(depth), )) * 2
density2 = np.random.random_sample((len(depth), )) * 2 + 1
fig = go.Figure()
fig.add_trace(go.Scatter(
x=density1,
y=depth,
mode='lines',
name='left'
))
fig.add_trace(go.Scatter(
x=density2,
y=depth,
mode='lines',
name='right'
))
fig.for_each_trace(
lambda trace: trace.update(fill='tonextx') if trace.name == "right" else (),
)
fig.show()
My solution requires you to give a name to each trace (ie. lines). Only after the two traces are updated, do fig.for_each_trace() to update either one of the traces with argument fill='tonextx'.
I want to add a caption below my plotly choropleth Map (using Python). I've looked into using annotations with graph_objs, but it only seems to work for locations within the map area. Is there a way to make the annotations show up below the choropleth map and/or is there an alternative way of doing this?
Right now I'm getting this but would like the caption to appear below the map area:
I've tried inputting y values less than 0, but then the text annotations just don't show up at all.
Here's my code:
import plotly.graph_objects as go
import pandas as pd
import plotly
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_world_gdp_with_codes.csv')
fig = go.Figure(data=go.Choropleth(
locations = df['CODE'],
z = df['GDP (BILLIONS)'],
text = df['COUNTRY'],
colorscale = 'Blues',
autocolorscale=False,
reversescale=True,
marker_line_color='darkgray',
marker_line_width=0.5,
colorbar_tickprefix = '$',
colorbar_title = 'GDP<br>Billions US$',
))
fig.update_layout(
title_text='2014 Global GDP',
geo=dict(
showframe=False,
showcoastlines=False,
projection_type='equirectangular'
),
annotations = [dict(
x=0.5,
y=0, #Trying a negative number makes the caption disappear - I'd like the caption to be below the map
xref='paper',
yref='paper',
text='Source: <a href="https://www.cia.gov/library/publications/the-world-factbook/fields/2195.html">\
CIA World Factbook</a>',
showarrow = False
)]
)
fig.show()
Setting y=-0.1 works fine on my end:
Plot 1
If that for some reason is not the case on your end (perhaps a version issue?), you should try to just leave it at y=0 and rather make room below the figure utself by adjustin the margins of the plot like this:
fig.update_layout(
margin=dict(l=20, r=20, t=60, b=20),
paper_bgcolor="LightSteelBlue")
Plot 2:
Complete code:
import plotly.graph_objects as go
import pandas as pd
import plotly
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_world_gdp_with_codes.csv')
fig = go.Figure(data=go.Choropleth(
locations = df['CODE'],
z = df['GDP (BILLIONS)'],
text = df['COUNTRY'],
colorscale = 'Blues',
autocolorscale=False,
reversescale=True,
marker_line_color='darkgray',
marker_line_width=0.5,
colorbar_tickprefix = '$',
colorbar_title = 'GDP<br>Billions US$',
))
fig.update_layout(
title_text='2014 Global GDP',
geo=dict(
showframe=False,
showcoastlines=False,
projection_type='equirectangular'
),
annotations = [dict(
x=0.5,
y=0, #Trying a negative number makes the caption disappear - I'd like the caption to be below the map
xref='paper',
yref='paper',
text='Source: <a href="https://www.cia.gov/library/publications/the-world-factbook/fields/2195.html">\
CIA World Factbook</a>',
showarrow = False
)]
)
fig.update_layout(
margin=dict(l=20, r=20, t=60, b=20),
paper_bgcolor="LightSteelBlue")
fig.show()
In the following code block I use a Jupyter IntSlider to adjust the number of dots visualized in a Plotly express scatter 3d plot. The example already fits my use case, but I noticed that Plotly has built-in slider functionalities that could improve the performance.
As a Plotly beginner I find it quite hard to map the slider example from Plotly to my use case.
Any suggestions?
import numpy as np
import plotly.express as px
import pandas as pd
from ipywidgets import interact, widgets
NUM_DOTS = 100
NUM_DIMS = 3
random_data = pd.DataFrame(np.random.random((NUM_DOTS,NUM_DIMS) ), columns=['x_1','x_2','x_3'])
def update_plotly(x):
fig = px.scatter_3d(random_data[:x], x='x_1', y='x_2', z='x_3')
fig.show()
interact(update_plotly, x=widgets.IntSlider(min=1, max=NUM_DOTS, step=1, value=NUM_DOTS))
Actually it's not that hard to build the slider, just follow the path of the example shown by plotly:
import plotly.graph_objects as go
import numpy as np
NUM_DOTS = 100
NUM_DIMS = 3
# Create figure
fig = go.Figure()
# Add traces, one for each slider step
for step in np.arange(1, NUM_DOTS, 1):
#Random data
random_data = pd.DataFrame(np.random.random((step, NUM_DIMS)), columns=['x_1','x_2','x_3'])
fig.add_trace(
go.Scatter3d(
visible=False,
line=dict(color="#00CED1", width=6),
name="𝜈 = " + str(step),
z=random_data['x_3'],
x=random_data['x_1'],
y=random_data['x_2']))
# Make 10th trace visible
fig.data[10].visible = True
# Create and add slider
steps = []
for i in range(len(fig.data)):
step = dict(
method="restyle",
args=["visible", [False] * len(fig.data)],
)
step["args"][1][i] = True # Toggle i'th trace to "visible"
steps.append(step)
sliders = [dict(
active=10,
currentvalue={"prefix": "Frequency: "},
pad={"t": 50},
steps=steps
)]
fig.update_layout(
sliders=sliders
)
fig.show()
resulting:
or with more points:
As you correctly figured out, it is way more performant than the widget slider, because with this method, you just toggle the trace visibility in the 3D Scatter chart.
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()