0

Trying to add the average value to each category in the plot. I have been trying to add these average values independently, per category, but without success. Is there a way that catplot can average the values from the data set and plot that extra value with a different color? My goal is to add and differentiate the average value from the individual values so can be visually identified.

plt.rcParams["figure.figsize"] = [5.50, 5.50]
plt.rcParams["figure.autolayout"] = True

ax = sns.catplot(x="Sample Set", y="Values [%]", data=df)
ax.set_xticklabels(rotation=90)
ax.despine(right=True, top=True)

sp = 100
delta = 5
plt.axhline(y=sp, color='gray', linestyle='--', label='Target')

plt.axhline(y=sp*((100+(delta*2))/100), color='r', linestyle='--', label='10%')
plt.axhline(y=sp*((100-(delta*2))/100), color='r', linestyle='--')

plt.ylim(80, 120)
plt.title('Sample Location[enter image description here][1]', fontsize = 14, y=1.05)
    
plt.legend(frameon=False, loc ="lower right")
plt.savefig(outputFileName, dpi=300, bbox_inches = 'tight')
plt.show()
plt.draw()

Current Output from Code

Derek O
  • 16,770
  • 4
  • 24
  • 43
  • You might be able to create a `catplot` with the average values excluded, then superimpose the mean values – Derek O Jan 20 '22 at 00:50
  • 1
    @DerekO like the idea, my only constraint right now after implementing what you are proposing is adding a legend that accounts for the dash lines, individual values, and the avg value. Any help will be appreciated. – Yomar Aymat Jan 20 '22 at 02:56
  • I didn't have enough time to make a serious attempt at a solution, so I left my idea as a comment to hopefully help either you or someone else get started on a solution. when i have a bit more time, i'd be happy to revisit your question and see if I can help further – Derek O Jan 20 '22 at 03:17
  • @DerekO, thanks! – Yomar Aymat Jan 20 '22 at 16:09

2 Answers2

2

You probably run into strange error messages, as you named the return value of sns.catplot as ax. sns.catplot is a "figure-level" function and returns a FacetGrid, often assigned to a variable named g. A figure-level function can have one or more subplots, accessible via g.axes. When there is only one subplot, g.ax points to that subplot.

Also note that the catplot's figsize isn't set via the rcParams. The figure size comes from the height= parameter (height in inches of one subplot) and the aspect= parameter (ratio between width and height of a subplot), multiplied by the number of rows/columns of subplots.

Further, you seem to be mixing the "object-oriented" and the pyplot interface for matplotlib. For readability and code maintenance, it is preferred to stick to one interface.

To indicate the means, sns.pointplot without confidence interval might be suited. ax.axhspan might be used to visualize the range around the target.

Here is some example code starting from seaborn's iris dataset.

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

iris = sns.load_dataset('iris')

g = sns.catplot(data=iris, x="species", y="sepal_length", height=5.50, aspect=1)
ax = g.ax
ax.tick_params(axis='x', rotation=0, length=0)
sns.pointplot(data=iris, x="species", y="sepal_length", estimator=np.mean,
              join=False, ci=None, markers=['D'], color='black', size=20, zorder=3, ax=ax)
sns.despine(right=True, top=True)

sp = 6
delta = 10
ax.axhline(y=sp, color='gray', linestyle='--', label='Target')
ax.axhspan(ymin=sp * (100 - delta) / 100, ymax=sp * (100 + delta) / 100,
           color='r', alpha=0.15, linestyle='--', label='10%')

ax.collections[-1].set_label('Mean')
ax.legend(frameon=False, loc="lower right")
# plt.savefig(outputFileName, dpi=300, bbox_inches='tight')
plt.tight_layout()
plt.show()

catplot with means

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • thanks for your help and advice. Really well appreciated! – Yomar Aymat Jan 20 '22 at 16:12
  • I will since it really attacks the problem as stated. However, the only missing part is how to add the legend for the average values. I have been trying to use the label approach within the point plot function but did not work. – Yomar Aymat Jan 21 '22 at 16:50
  • Seaborn and legends are often complicated, as typical functions combine many matplotlib elements. In this case, `ax.collections[-1].set_label('Mean')` adds a label to the collection that contains the means. – JohanC Jan 21 '22 at 17:29
  • Thanks for your help JohanC! That's works nicely. – Yomar Aymat Jan 25 '22 at 16:59
-1

According to plotting with seaborn using the matplotlib object-oriented interface as catplot is a Figure-leveltype of graph, will be much harder than doing it comparing to some other types of graph.

The second group of functions (Figure-level) are distinguished by the fact that the resulting plot can potentially include several Axes which are always organized in a "meaningful" way. That means that the functions need to have total control over the figure, so it isn't possible to plot, say, an lmplot onto one that already exists. Calling the function always initializes a figure and sets it up for the specific plot it's drawing.

pedro_bb7
  • 1,601
  • 3
  • 12
  • 28