Make subplots using plotly express with values coming from a dataframe - python

Assuming I have a toy model df which lists the model of the car and customer rating of one car showroom.
CustomerID Model Cust_rating
1 Corolla A
2 Corolla B
3 Forester A
4 GLC C
5 Xterra A
6 GLC A
Using plotly express, I created pie charts of percentage of cars by model and by Cust_rating, respectively as two separate graphs:
import plotly.express as px
px.pie(df,names='Model',title='Proportion Of each Model')
px.pie(df,names='Cust_rating',title='Proportion Of each Rating')
Now, I want to create subplots, and all the ways of doing it using the documentation are throwing up errors:
ValueError: Trace type 'pie' is not compatible with subplot type 'xy'
at grid position (1, 1)
This is what I tried:
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig = make_subplots(rows=1, cols=2)
fig.add_trace(go.Pie(values=df['Model']), row=1, col=1)
fig.add_trace(go.Pie(values=df['Cust_rating']), row=1, col=2)
fig.update_layout(height=700, showlegend=False)
fig.show()

A pie chart in a graph object requires a pair of labels and values. You must also specify the plot type in the subplot. See this for an example of a subplot type.
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig = make_subplots(rows=1, cols=2, subplot_titles=("Model", "Rating"), specs=[[{'type': 'domain'},{'type': 'domain'}]])
fig.add_trace(go.Pie(labels=df['Model'].value_counts().index,
values=df['Model'].value_counts(),
legendgroup='model',
legendgrouptitle=dict(text='Model'),
),
row=1, col=1)
fig.add_trace(go.Pie(labels=df['Cust_rating'].value_counts().index,
values=df['Cust_rating'].value_counts(),
legendgroup='rating',
legendgrouptitle=dict(text='Rating')),
row=1, col=2)
fig.update_layout(height=400, width=600, showlegend=True)
fig.show()

Use matplotlib and seaborn:
import matplotlib.pyplot as plt
import seaborn as sns
if you want two and more plot use
fig, ax = plt.subplots(2,2, figsize=(20, 15))
And use ax=ax[0,1], row and col,
sns.boxplot(x = 'bedrooms', y = 'price', data = dataset_df, ax=ax[0,1])
sns.boxplot(x = 'floor, y = 'price', data = dataset_df, ax=ax[0,2])

Related

Adding text or cross sign on every subplot of plotly, each in unique positions of the subplots

I am struggling to put a cross sign in certain positions of each subplots of plotly in Python. I have 2 subplots and in each one, I want to out the cross in certain positions as below.
Position of the cross sign at the subplot_1 and 2 are attached.
import numpy as np
import plotly.graph_objs as go
import plotly.figure_factory as ff
from plotly.subplots import make_subplots
import string
#Define data for heatmap
N=5
x = np.array([10*k for k in range(N)])
y = np.linspace(0, 2, N)
z1 = np.random.randint(5,15, (N,N))
z2 = np.random.randint(10,27, (N,N))
mytext = np.array(list(string.ascii_uppercase))[:25].reshape(N,N)
fig1 = ff.create_annotated_heatmap(z1, x.tolist(), y.tolist(), colorscale='matter')
fig2 = ff.create_annotated_heatmap(z2, x.tolist(), y.tolist(), annotation_text=mytext, colorscale='Viridis')
fig = make_subplots(
rows=1, cols=2,
horizontal_spacing=0.05,
)
fig.add_trace(fig1.data[0], 1, 1)
fig.add_trace(fig2.data[0], 1, 2)
annot1 = list(fig1.layout.annotations)
annot2 = list(fig2.layout.annotations)
for k in range(len(annot2)):
annot2[k]['xref'] = 'x2'
annot2[k]['yref'] = 'y2'
fig.update_layout(annotations=annot1+annot2)
There are two ways to deal with this question: the first is to use the line mode of the scatterplot and the second is to add a shape. In the line mode of the scatterplot, the real starting position is -0.5, so the heatmap and the cross line are misaligned. So I chose to add a figure.
Also, I can now annotate without using figure_factory, so I'll use a graph object to construct the graph. The configuration is one heatmap combined with two shapes, with the y-axis and x-axis scales changed.
import numpy as np
import plotly.graph_objs as go
from plotly.subplots import make_subplots
np.random.seed(1)
fig = make_subplots(rows=1,
cols=2,
horizontal_spacing=0.05,
)
fig.add_trace(go.Heatmap(z=z1,
text=z1,
texttemplate='%{text}',
showscale=False,
),
row=1,col=1
)
fig.add_shape(type='line',
x0=1.5, y0=1.5, x1=2.5, y1=2.5,
line=dict(color='black', width=2)
)
fig.add_shape(type='line',
x0=2.5, y0=1.5, x1=1.5, y1=2.5,
line=dict(color='black', width=2)
)
fig.add_trace(go.Heatmap(z=z2,
text=mytext,
texttemplate='%{text}',
showscale=False,
colorscale = 'Viridis'
),
row=1,col=2
)
fig.add_shape(type='line',
x0=0.5, y0=-0.5, x1=1.5, y1=0.5,
line=dict(color='black', width=2),
row=1,col=2
)
fig.add_shape(type='line',
x0=1.5, y0=-0.5, x1=0.5, y1=0.5,
line=dict(color='black', width=2),
row=1, col=2
)
fig.update_yaxes(tickvals=[0,1,2,3,4], ticktext=y.tolist())
fig.update_xaxes(tickvals=[0,1,2,3,4], ticktext=x.tolist())
fig.update_layout(autosize=False, width=800)
fig.show()

Draw multiple CSV files in a HTML page using Plotly

I want to draw multiple CSV files on an HTML page with fig = make_subplots(rows=.., cols=..).
df1 = pd.read_csv('first_input.csv')
fig1 = px.scatter(df, x="...", y="...", color="..")
df2 = pd.read_csv('first_input.csv')
fig2 = px.scatter(df, x="...", y="...", color="..")
Unfortunately plotly subplots do not directly support plotly.express figures as explained in the documentation here.
However, when you create a plotly.express figure using fig1 = px.scatter(df, x="...", y="...", color=".."), you are actually creating a figure where fig1.data is a tuple of go.Scatter traces. You can access each trace in fig1.data and add it to your subplots object.
If you have multiple px.scatter figures, you can iterate through them, and add each trace from px.scatter figure to your subplots object at the appropriate row and column. Then we can add the axes titles from each px.scatter figure to the subplots object layout.
I'll use the tips sample dataset to demonstrate:
import plotly.express as px
from plotly.subplots import make_subplots
df = px.data.tips()
fig1 = px.scatter(df, x="total_bill", y="tip", color="smoker")
fig2 = px.scatter(df, x="total_bill", y="tip", color="day")
fig_subplots = make_subplots(rows=2, cols=1)
for trace in fig1.data:
fig_subplots.add_trace(
trace,
row=1, col=1
)
for trace in fig2.data:
fig_subplots.add_trace(
trace,
row=2, col=1
)
## x and y axies in fig_subplots["layout"] are called xaxis, xaxis2, ..., yaxis, yaxis2, ...
## here we are making the assumption you are stacking your plots vertically
def modify_axis_titles(fig_subplots, px_fig, nrow):
xaxis_name, yaxis_name = f"xaxis{nrow}", f"yaxis{nrow}"
fig_subplots['layout'][xaxis_name]['title'] = px_fig.layout['xaxis']['title']
fig_subplots['layout'][yaxis_name]['title'] = px_fig.layout['yaxis']['title']
for px_fig, nrow in zip([fig1, fig2],[1,2]):
modify_axis_titles(fig_subplots, px_fig, nrow)
fig_subplots.show()

Plotly horizontal bar comparizon

The chart attached is from R plotly package. Does this exist or can be done in python using the plotly package?
You can create diverging stacked bars in plotly-python by plotting the bars for male and female populations as separate traces, making the population values negative for the men, and then using the original values in the customdata so the populations for men display positive values.
I followed the method outlined by #empet in his answer here, and modified the categories and hovertemplate to fit your example.
import numpy as np
import pandas as pd
import plotly.graph_objects as go
d = {'Age': ['0-19','20-29','30-39','40-49','50-59','60-Inf'],
'Male': [1000,2000,4200,5000,3500,1000],
'Female': [1000,2500,4000,4800,2000,1000],
}
df = pd.DataFrame(d)
fig = go.Figure()
fig.add_trace(go.Bar(x=-df['Male'].values,
y=df['Age'],
orientation='h',
name='Male',
customdata=df['Male'],
hovertemplate = "Age: %{y}<br>Pop:%{customdata}<br>Gender:Male<extra></extra>"))
fig.add_trace(go.Bar(x= df['Female'],
y =df['Age'],
orientation='h',
name='Female',
hovertemplate="Age: %{y}<br>Pop:%{x}<br>Gender:Female<extra></extra>"))
fig.update_layout(barmode='relative',
height=400,
width=700,
yaxis_autorange='reversed',
bargap=0.01,
legend_orientation ='h',
legend_x=-0.05, legend_y=1.1
)
fig.show()

How to format plotly legend when using marker color?

I want to follow up on this post: Plotly: How to colorcode plotly graph objects bar chart using Python?.
When using plotly express, and specifying 'color', the legend is correctly produced as seen in the post by vestland.
This is my plotly express code:
data = {'x_data': np.random.random_sample((5,)),
'y_data': ['A', 'B', 'C', 'D', 'E'],
'c_data': np.random.randint(1, 100, size=5)
}
df = pd.DataFrame(data=data)
fig = px.bar(df,
x='x_data',
y='y_data',
orientation='h',
color='c_data',
color_continuous_scale='YlOrRd'
)
fig.show()
But when using go.Bar, the legend is incorrectly displayed as illustrated here:
This is my code using graph objects:
bar_trace = go.Bar(name='bar_trace',
x=df['x_data'],
y=df['y_data'],
marker={'color': df['c_data'], 'colorscale': 'YlOrRd'},
orientation='h'
)
layout = go.Layout(showlegend=True)
fig = go.FigureWidget(data=[bar_trace], layout=layout)
fig.show()
I'm learning how to use FigureWidget and it seems it can't use plotly express so I have to learn how to use graph objects to plot. How do I link the legend to the data such that it works like the plotly express example in vestland's post.
This really comes down to understanding what a high level API (plotly express) does. When you specify color in px if it is categorical it creates a trace per value of categorical. Hence the below two ways of creating a figure are mostly equivalent. The legend shows an item for each trace, not for each color.
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import numpy as np
df = pd.DataFrame({"x":np.linspace(0,10,10), "y":np.linspace(5,15,10), "color":np.random.choice(list("ABCD"),10)})
px.bar(df, x="x", y="y", color="color", orientation="h").show()
fig = go.Figure()
for g in df.groupby("color"):
fig.add_trace(go.Bar(x=g[1]["x"], y=g[1]["y"], name=g[0], orientation="h"))
fig
supplementary based on comments
you do not have to use graph objects if you are using FigureWidget() as demonstrated by second figure, create with plotly express and then generate FigureWidget()
for continuous data normal pattern is to use a single trace and a colorbar (also demonstrated in second figure). However if you want a discrete legend, create a trace per value in c_data and use https://plotly.com/python-api-reference/generated/plotly.colors.html sample_colorscale()
import plotly.express as px
import plotly.colors
import plotly.graph_objects as go
import numpy as np
import pandas as pd
# simulate data frame...
df = pd.DataFrame(
{
"x_data": np.linspace(0, 10, 10),
"y_data": np.linspace(5, 15, 10),
"c_data": np.random.randint(0, 4, 10),
}
)
# build a trace per value in c_data using graph objects ... correct legend !!??
bar_traces = [
go.Bar(
name="bar_trace",
x=d["x_data"],
y=d["y_data"],
marker={
"color": plotly.colors.sample_colorscale(
"YlOrRd",
d["c_data"] / df["c_data"].max(),
)
},
orientation="h",
)
for c, d in df.groupby("c_data")
]
layout = go.Layout(showlegend=True)
fig = go.FigureWidget(data=bar_traces, layout=layout)
fig.show()
fig = px.bar(
df,
x="x_data",
y="y_data",
color="c_data",
orientation="h",
color_continuous_scale="YlOrRd",
)
fig = go.FigureWidget(data=fig.data, layout=fig.layout)
fig.show()

How do I combine two plots into one figure using Plotly?

I have 2 csv files, my codes are as below.
df = pd.read_csv("test.csv",
sep='\t',skiprows=range(9),names=['A', 'B', 'C','D'])
df2 = pd.read_csv("LoadMatch_Limit.csv",skiprows=range(1),names=['X','Y'])
fig = px.line([df,df2], x=['A','X'] , y=['D','Y'])
I would like my line chart, x-axis to take from (columns 'A' and 'X') and my y-axis to take from (columns 'D' and 'Y').
Is there anyway I can plot these 2 charts as one figure?
You could create the two plots and combine them with plotly graph objects
import plotly.express as px
import plotly.graph_objects as go
fig1 = px.line(df, x='A', y='D')
fig2 = px.line(df2, x='X', y='Y')
fig = go.Figure(data = fig1.data + fig2.data)
fig.show()
Plotly.offline has no attribute line. You need to use the graph object to plot two graphs in a single figure. A simple example is shown below(run the code below and see the output graph)
import numpy as np
import pandas as pd
import plotly.offline as py
import plotly.graph_objs as go
from plotly import tools
df1 = pd.DataFrame({"X":np.linspace(0,30,10), "Y":np.random.rand(10)})
df2 = pd.DataFrame({"A":np.linspace(0,40,10), "B":np.random.rand(10)})
# plotting the graphs...
# 'rgb(128, 0, 128)'
# color=sns.color_palette()
def scatter_chart(x, y, color, name):
trace = go.Scatter(
x=x.values,
y=y.values,
name=name,
marker=dict(
color=color,
line=dict(
color=color,
width=1)
),
)
return trace
trace1 = scatter_chart(df2["A"], df2["B"], 'rgb(128, 0, 128)', "df2")
trace2 = scatter_chart(df1["X"], df1["Y"], 'rgba(50, 171, 96, 0.6)', "df1")
fig = tools.make_subplots(rows=1,cols=1, vertical_spacing=0.5)
fig.add_trace(trace1)
fig.add_trace(trace2)
fig.update_layout(
title="df2 and df1 plot",
height=600,
width=600,
# annotations=annotations,
xaxis=dict(tickangle=-45),
legend=dict(x=0.029, y=1.038, font_size=10),
margin=dict(l=100, r=20, t=70, b=70),
paper_bgcolor='rgb(248, 248, 255)',
plot_bgcolor='rgb(248, 248, 255)',)
py.iplot(fig, filename='pageviews_plots_4')

Categories