I am trying to generate a frequency plot at different time frames with each time frame consists of four conditions and two possible outcomes, High or low. I can use hue to separate the four conditions but I also want to separate each condition with outcomes. Here's my data-
import pandas as pd
import seaborn as sns
exp = {'Time':['2hrs', '2hrs', '2hrs','2hrs','2hrs', '2hrs', '2hrs','4hrs','4hrs','4hrs','4hrs','4hrs','4hrs','4hrs','4hrs' ],
'Condition':['A+','A-','B+','B-','B+','B-','B+','A+','A-','B+','B-','A+','A-','A+','A-',],
'Outcome': ['High', 'Low','High','High', 'Low','High', 'Low','High','High','High','Low', 'Low','Low','Low', 'High']}
df = pd.DataFrame(data=exp)
df.head()
hue_order = ['A+', 'A-', 'B+', 'B-']
ax = sns.countplot(data=df, x='Time' , hue='Condition', hue_order=hue_order, palette='Set1')
plt.legend(title='', loc='upper left', bbox_to_anchor=(1,1))
What I got is-
But what I want is to further split each column proportional to how many "High" or "Low" are there for that specific condition and time frame. For example, at 4hrs there are three A+ of which one is with "High" outcome and remaining two are with "Low" outcomes. So, I want to shade 1/3 with dark red and 2/3 with light red.
Not sure if I described the problem clearly.
If you need the High/Low to be shown as stacked along with the currently grouped bars, it is not available directly in seaborn. Once you plot the above bar, you will need to add the HIGHs (or LOWs) to the bar chart to show this. Steps will include:
ax.containers
to get the barsCODE:
import pandas as pd
import seaborn as sns
from matplotlib.patches import Rectangle ## Added NEW
exp = {'Time':['2hrs', '2hrs', '2hrs','2hrs','2hrs', '2hrs', '2hrs','4hrs','4hrs','4hrs','4hrs','4hrs','4hrs','4hrs','4hrs' ],
'Condition':['A+','A-','B+','B-','B+','B-','B+','A+','A-','B+','B-','A+','A-','A+','A-',],
'Outcome': ['High', 'Low','High','High', 'Low','High', 'Low','High','High','High','Low', 'Low','Low','Low', 'High']}
df = pd.DataFrame(data=exp)
hue_order = ['A+', 'A-', 'B+', 'B-']
ax = sns.countplot(data=df, x='Time' , hue='Condition', hue_order=hue_order, palette='Set1')
## Added code starts here
## Get the HIGH values for each bar, adding 0 if no HIGHS
high_heights=df[df.Outcome=='High'].groupby(['Condition', 'Time']).count().unstack(fill_value=0).stack().reset_index().Outcome
i=0
## For each container, for each bar within this container
for bars in ax.containers:
for bar in bars:
## Use same bar x,y,width, but different heights and darker facecolor
rect=Rectangle(bar.get_xy(), bar.get_width(), high_heights[i], color=tuple(t/1.5 for t in bar.get_facecolor()[0:3]))
ax.add_patch(rect) ## Add to the plot
i=i+1
plt.legend(title='', loc='upper left', bbox_to_anchor=(1,1))
This is the output...
I also wanted to show you another option here... rather than shades, you can use hatches, which will show something like this. Most steps are the same, expect that when you are drawing the new rectangle, plot the same x,y, color, but different height and add the hatch. More options for hatches are available here
Just update the rectangle creation line as such, rest is as above...
rect=Rectangle(bar.get_xy(), bar.get_width(), high_heights[i], hatch='oo', facecolor=bar.get_facecolor())