Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
Description
Problem
Recently, I wanted to get split violin plots at defined x-positions (see image).
My problem is that while it's possible to specify the x-position inpyplot.violinplot()
, there is no option for split violins. This is not true anymore since v0.13seaborn.violinplot()
on the other hand allows split violins but seems to enforce discrete x-positions with fixed intervals.
This example shows two data sets (black and red/blue) in four categories defined by
The code that produces the plot based onthis answer on stackoverflow.
importmatplotlib.pyplotaspltimportnumpyasnpnp.random.seed(0)cols= ["firebrick","dodgerblue"]leg= {"patches": [],"labels": []}# Loop over categories A and BforAinrange(0,2):xpos= []dLeft= []dRight= []forBinrange(0,2):# Create data for the left and right violins in the current categorymu=np.random.randint(10,100)dLeft.append(np.random.poisson(mu,1000))dRight.append(np.random.normal(mu+10,np.sqrt(mu+10),1000))# Create x-positions for the violinsxpos.append(B+(A-0.5)*0.15)# Create right violins (colors)vRight=plt.violinplot(dRight,positions=xpos,widths=0.1,showextrema=False,showmedians=False)forvrinvRight["bodies"]:# Get the centerm=np.mean(vr.get_paths()[0].vertices[:,0])# Get the rightmost pointr=np.max(vr.get_paths()[0].vertices[:,0])# Only allow values right of the centervr.get_paths()[0].vertices[:,0]=np.clip(vr.get_paths()[0].vertices[:,0],m,r)# Set category A colorvr.set_color(cols[A])vr.set_alpha(1.)# Create left violins (black)vLeft=plt.violinplot(dLeft,positions=xpos,widths=0.15,showextrema=False,showmedians=False)forvlinvLeft["bodies"]:# Get the centerm=np.mean(vl.get_paths()[0].vertices[:,0])# Get the leftmost pointl=np.min(vl.get_paths()[0].vertices[:,0])# Only allow values left of the centervl.get_paths()[0].vertices[:,0]=np.clip(vl.get_paths()[0].vertices[:,0],l,m)# Set colorvl.set_color("k")vl.set_alpha(1.)# Add legend entryleg["patches"].append(vRight["bodies"][0])leg["labels"].append(fr"$A={A}$")plt.legend(leg["patches"],leg["labels"])plt.xlabel(r"$B$")plt.xticks([0,1],["0","1"])plt.xlim(-0.5,1.5)plt.tight_layout()plt.savefig("example_violins.png")
Proposed solution
An easy-to-use solution would be to add an argument in theviolinplot()
function through which the user can define which side to plot. Something likeasymetric=None, "below", "above"
, where"below"
/"above"
only plots the side below/above the values given topositions
.
This would mean that calling
vRight=plt.violinplot(dRight,positions=xpos,widths=0.1,showextrema=False,showmedians=False,asymetric="above")
internally executes this loop
forvrinvRight["bodies"]:# Get the centerm=np.mean(vr.get_paths()[0].vertices[:,0])# Get the rightmost pointr=np.max(vr.get_paths()[0].vertices[:,0])# Only allow values right of the centervr.get_paths()[0].vertices[:,0]=np.clip(vr.get_paths()[0].vertices[:,0],m,r)