I'm trying to add two separate traces using different y values to the exact same facet grid figure. The figure uses facet_col
to separate subplots based on color
. For each subplot the x-axis display cut
as a categorical variable. I have two separate columns relating to price (price
,price2
).
I want to plot both of these values in the same facetgrid layout. But price2
trace should be displayed as a dashed line
1). They work individually but when trying to combine them in a single figure, the facetgrid layout is dismissed and a single plot is returned.
2). When combining the y-values in a single call, I distinguish between them by applying a dashed line to price2
. But then the color sequence is lost and the legend is not updated.
import seaborn as sns
import plotly.express as px
import plotly.graph_objects as go
import numpy as np
diamonds = sns.load_dataset('diamonds')
diamonds['val'] = np.random.randint(1, 3, diamonds.shape[0])
diamonds['price2'] = diamonds['price'] + 5000
diamonds = diamonds.drop_duplicates(subset = ['cut','color'])
color_discrete_sequence = px.colors.qualitative.G10
fig1 = px.line(diamonds,
x = "cut",
y = "price",
color = "val",
facet_col = "color",
facet_col_wrap = 3,
facet_row_spacing = 0.08,
facet_col_spacing = 0.08,
markers = True,
color_discrete_sequence = color_discrete_sequence,
category_orders = {"cut": ['E', 'I', 'J', 'H', 'F', 'G', 'D']}
)
fig2 = px.line(diamonds,
x = "cut",
y = "price2",
color = "val",
facet_col = "color",
facet_col_wrap = 3,
facet_row_spacing = 0.08,
facet_col_spacing = 0.08,
markers = True,
color_discrete_sequence = color_discrete_sequence,
category_orders = {"cut": ['E', 'I', 'J', 'H', 'F', 'G', 'D']}
)
#update y-values for price2 only to be dashed
fig2.update_traces(patch={"line": {"dash": 'dot'}})
fig1_2 = go.Figure(data = fig1.data + fig2.data)
fig1_2.show()
2)
fig = px.line(diamonds,
x = "cut",
y = ["price","price2"],
color = "val",
facet_col = "color",
facet_col_wrap = 3,
facet_row_spacing = 0.08,
facet_col_spacing = 0.08,
markers = True,
color_discrete_sequence = color_discrete_sequence,
category_orders = {"cut": ['E', 'I', 'J', 'H', 'F', 'G', 'D']}
)
for i in range(1,28,2):
fig.data[i]['line'] = dict(dash='dot')
fig.show()
Edit 3:
To customize the line style, you will want to use a graph object. In the case of this question, you want to make each of the two y-values a different line type, so you can achieve this in the following way.
import seaborn as sns
import numpy as np
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import itertools
np.random.seed(20231129)
diamonds = sns.load_dataset('diamonds')
diamonds['val'] = np.random.randint(1, 3, diamonds.shape[0])
diamonds['price2'] = diamonds['price'] + 1000
diamonds = diamonds.drop_duplicates(subset = ['cut','color'])
lst = itertools.product(range(1,5),range(1,4))
colors = ['E', 'I', 'J', 'H', 'F', 'G', 'D']
sub_titles = [f'color={c}' for c in colors]
fig = make_subplots(rows=4, cols=3,
start_cell="top-left",
shared_yaxes=True,
#shared_xaxes=True,
subplot_titles=sub_titles,
)
for c,rc in zip(colors, lst):
df_color = diamonds.query('color == @c')
df_color.set_index('cut', inplace=True)
df_color = df_color.reindex(index=['Ideal','Fair','Very Good','Premium','Good'])
df_color.reset_index(inplace=True)
for v in df_color['val'].unique():
df = df_color.query('val == @v')
if v == 1:
color = 'blue'
# dash = 'dot'
else:
color = 'red'
# dash = 'dot'
fig.add_trace(go.Scatter(
x=df['cut'],
y=df['price'],
mode='markers+lines',
marker=dict(color=color),
line=dict(dash='solid'),
name=f'{v}'
), row=rc[0],col=rc[1])
fig.add_trace(go.Scatter(
x=df['cut'],
y=df['price2'],
mode='markers+lines',
marker=dict(color=color),
line=dict(dash='dot'),
name=f'{v}'
), row=rc[0],col=rc[1])
names = set()
fig.for_each_trace(
lambda trace:
trace.update(showlegend=False)
if (trace.name in names) else names.add(trace.name))
# update xaxis and yaxis titles
fig.layout['xaxis7']['title']['text']='cut'
fig.layout['yaxis']['title']['text']='Value'
fig.layout['yaxis4']['title']['text']='Value'
fig.layout['yaxis7']['title']['text']='Value'
fig.update_layout(height=450)
fig.show()