Stacking by patterns and grouping by colors at a same time in bar chart

In the below chart, I would like to group by machine and stack them based on their shifts.

code:
fig = px.bar(df_plot,
x=“Date”,
y=“Efficiency”,
color=“Machine”,
barmode=‘group’,
pattern_shape=“DayNight”,
)
fig.show()

df:
Date Machine DayNight Efficiency
138 2023-05-08 Eq_01 DAY 1.00
139 2023-05-08 Eq_02 DAY 1.62
140 2023-05-08 Eq_03 DAY 1.85
141 2023-05-08 Eq_06 DAY 1.09
142 2023-05-08 Eq_07 DAY 1.85
143 2023-05-08 Eq_21 DAY 0.00
144 2023-05-08 Eq_52 DAY 1.00
145 2023-05-08 Eq_53 DAY 1.05
146 2023-05-08 Eq_54 DAY 1.01
147 2023-05-08 Eq_55 DAY 0.12
148 2023-05-08 Eq_01 Night 1.04
149 2023-05-08 Eq_02 Night 0.00
150 2023-05-08 Eq_03 Night 1.00
151 2023-05-08 Eq_06 Night 1.21
152 2023-05-08 Eq_07 Night 0.98
153 2023-05-08 Eq_21 Night 1.00
154 2023-05-08 Eq_52 Night 1.00
155 2023-05-08 Eq_53 Night 0.23
156 2023-05-08 Eq_54 Night 1.99

df_plot

Thanks for your help

Hi @mrnour ,

as far as I know, I think it is not possible if you use plotly express.
Because barmode will make you choose just group or relative (stack) only, not both, even you are using pattern_shape.

My suggestion is to use graph_objects because it is more flexible to make you custom graphs.

Another suggestion is using Bar Charts With Multicategory Axis Type,you can refer to this docs:

First, you can make x axis from 2 columns (Date and Machine). This is to make Multicategory X Axis.

x = [list(df_plot["Date"].values),list(df_plot["Machine"].values)]

After that,

You can filter the df_plot based on DayNight unique values (Replace opposite shift name with NA) and get the Efficiency column values to create every traces.

for shift_name in df_plot["DayNight"].unique():
	y = df_plot['Efficiency'].mask(df_plot["DayNight"]!=shift_name, pd.NA)
	fig.add_bar(x=x,y=y,name=shift_name)

And last, use hovertext and hovertemplate to make readable hovered text.

     fig.add_bar(
		x=x,
		y=y,
		name=shift_name,
		hovertext = df_plot["DayNight"],
		hovertemplate="Date: %{x[0]}<br>"+
		"Machine: %{x[1]}<br>"+
		"Efficiency: %{y}<br>"+
		"DayNight: %{hovertext}<br>"
	)

If I put all together in full code.

import plotly.graph_objects as go
import pandas as pd

df_plot = pd.read_csv("Date_Machine_DayNight_Efficiency.csv",sep=",")

x = [list(df_plot["Date"].values),list(df_plot["Machine"].values)]

print(x)
fig = go.Figure()

for shift_name in df_plot["DayNight"].unique():
        #  create series of Effieciency for certain shift , and replace other with NA
	y = df_plot['Efficiency'].mask(df_plot["DayNight"]!=shift_name, pd.NA)
	fig.add_bar(
		x=x,
		y=y,
		name=shift_name,
		hovertext = df_plot["DayNight"],
		hovertemplate="Date: %{x[0]}<br>"+
		"Machine: %{x[1]}<br>"+
		"Efficiency: %{y}<br>"+
		"DayNight: %{hovertext}<br>"
	)

fig.update_layout(
	barmode="relative",
	xaxis_title="Date",
    yaxis_title="Efficiency",)
fig.show()

Hope this help.

*REVISION : I have updated the code, so it will work for multiple days for now.

1 Like

Hi Farispriadi,

Thanks for your reply,

It wouldnot work for multiple days,:

ideally color should show Eq_x in each day and stacked pattern for day or night

Hi @mrnour ,

Thanks for your feedback!

Actually I have updated my code above.

Now , it can support with multiple days.

Thanks.

Thanks!
I have updated your code to use color for machines and pattern for shifts.
However, i am not sure how i can add colors to the legends. The legend only shows shifts (day|night)

Thanks for your time!

import plotly.graph_objects as go
import pandas as pd

df_plot[“Date”]=df_plot[“Date”].astype(str)
x = [list(df_plot[“Date”].values),list(df_plot[“Machine”].values)]

colors,color_map=cat2color(df_plot[‘Machine’], px.colors.qualitative.Plotly)
fig = go.Figure()

for shift_name, upattern in zip(df_plot[“DayNight”].unique(), [‘’, ‘/’]):
# create series of Effieciency for certain shift , and replace other with NA]
y = df_plot[‘Efficiency’].mask(df_plot[“DayNight”]!=shift_name, pd.NA)
fig.add_bar(
x=x,
y=y,
name=shift_name,
hovertext = df_plot[“DayNight”],
marker=dict(color =colors, ),
marker_pattern_shape=upattern,
# legendgroup={shift_name:color_map},
# legendgrouptitle_text={shift_name:color_map},

	hovertemplate="Date: %{x[0]}<br>"+
	"Machine: %{x[1]}<br>"+
	"Efficiency: %{y}<br>"+
	"DayNight: %{hovertext}<br>"
)

fig.update_layout(
barmode=“relative”,
xaxis_title=“Date”,
yaxis_title=“Efficiency”,)
fig.show()

1 Like

Hi @mrnour ,

That great idea to use color marker to make each machine has different color.

And your reply inspire me another approach to give different color each bar.

By adding each machine of every shift as a single trace, it will generate different colors of every bar.
And it will give us option to add legend name that combination of machine name and shift name.

Using your updated code,

import plotly.graph_objects as go
import pandas as pd

df_plot = pd.read_csv("Date_Machine_DayNight_Efficiency.csv",sep=",")
df_plot["Date"]=df_plot["Date"].astype(str)
x = [list(df_plot["Date"].values),list(df_plot["Machine"].values)]

# colors,color_map=cat2color(df_plot['Machine'], px.colors.qualitative.Plotly)
fig = go.Figure()

for shift_name, upattern in zip(df_plot["DayNight"].unique(), ['', '/']):
	# create new datafame  for certain shift , and replace other with NA]
	df_tmp = df_plot.mask(df_plot["DayNight"]!=shift_name, pd.NA)

	# Adding another loop for machine name so it creare each trace on every machine
	for machine_name in df_plot["Machine"].unique():
        # create series of Efficiency for certain machine name , and replace other with NA from certain shift dataframe
		y = df_tmp["Efficiency"].mask(df_plot["Machine"]!=machine_name, pd.NA)
		fig.add_bar(
			x=x,
			y=y,
			# update the trace name on legend
			name=f"{machine_name} - {shift_name}", 
			hovertext = df_plot["DayNight"],
			# marker=dict(color =colors, ),
			marker_pattern_shape=upattern,
			# legendgroup={shift_name:color_map},
			# legendgrouptitle_text={shift_name:color_map},

			hovertemplate="Date: %{x[0]}<br>"+
			"Machine: %{x[1]}<br>"+
			"Efficiency: %{y}<br>"+
			"DayNight: %{hovertext}<br>"
		)	
fig.update_layout(
barmode="relative",
xaxis_title="Date",
yaxis_title="Efficiency",)
fig.show()

Hope help you find solution adding colors to the legends.

1 Like

Thanks! @farispriadi

1 Like