# Violin plots

A violin plot is enhanced form of boxplot that also shows a kernel density estimation of the data. This is useful for bimodal data that are 'concealed' by a boxplot (Hintze & Nelson (1998) The American Statistician 52(2):181-4). The following code comes following a discussion in the forums on how to do this in IgorPro (http://www.igorexchange.com/node/6141). The 1D kernel density estimation method used here is Silverman's rule-of-thumb (Bowman & Azzalini (1997) Applied Smoothing Techniques for Data Analysis, OSP). R also uses this, but has options for other smoothing methods. The implementation here is to first make a boxplot. The code then calculates the violins and places them behind the boxplots in ProgBack (see example experiment). This could be turned into a Project to incorporate this into the WavePercentiles.ipf to generate Violin Plots directly, add other options for colour/formatting the boxplot etc.

#pragma rtGlobals=3     // Use modern global access method and strict wave access.
#include <Percentile and Box Plot>

//Before executing, you need to have plotted the boxplots using Percentile and Box Plot
//Plot the box plots into a target window using all waves from a source window
//This is a limitation of the functions, they rely on two graph windows (source and target) for making the violins.
//A width of 0.1 is good for the box plots and the boxplots look better with whiskers going to 0 and 100 rather than 10 and 90 (Igor default)

"Violin Plot...",  ViolinPlot()
"Scrub Violins", ClearViolins()
End

Function ViolinPlot ()
String sourceWindow // window with the waves to make boxplot and violins
String targetWindow // window with the boxplots to add violins
Variable trimValue=1    // violins can be trimmed (default) or plotted with long tails
Variable thickValue=0.1 // this is the width of the boxplot

Prompt sourceWindow, "What is the source graph?", popup, WinList("*", ";", "WIN:1")
Prompt targetWindow, "What is the box plot graph?", popup, WinList("*", ";", "WIN:1")
Prompt  trimValue, "yes=1, no=0"
Prompt thickValue, "auto=0.1"
DoPrompt "Pick graphs", sourceWindow, targetWindow, trimValue, thickValue

String/G gSrcWin = sourceWindow //set global strings and variables
String/G gTgtWin = targetWindow
Variable/G gtrim = trimValue
Variable/G gthick = thickValue

DoWindow /F \$sourceWindow
String wlist = Wavelist("*",";","WIN:")
String name
Variable nWaves = ItemsInList(wlist)

Variable i

for(i = 0; i < nWaves; i += 1)
name = StringFromList(i,wList)  //picks waves from source window and sends them to be plotted as violins in target window
Wave w0 = \$name
Violin(w0,i)
endfor

DoWindow /F \$targetWindow
SetAxis/A/N=1/E=1 left
SetAxis bottom -0.5,(nWaves-0.5)
End

///     @param  w   1D wave for plotting as violin
///     @param  xpos    variable to describe where violin will go on x-axis
Function Violin(w,xpos)
Wave w
Variable xpos

SVAR srcWin = gSrcWin //set local ref for global strings
SVAR tgtWin = gTgtWin
NVAR trim = gtrim
NVAR thick = gThick

Variable nPoints = numpnts(w)
Variable xMin, xMax

if(trim == 1)
xMin = wavemin(w)
xMax = wavemax(w)                       // trimmed violins
else
Variable bw = ((4 * sqrt(variance(w))^5) / (3 * numpnts(w)))^0.2    // this is Silverman's rule-of-thumb
xMin = wavemin(w) - (2*bw)
xMax = wavemax(w) + (2*bw)              // not trimmed, limit kde to 2 bandwidths beyond min/max
endif

Variable dX = min((xMax - xMin) / 100,1) // delta x for StatsKDE
StatsKDE/Q/BWM=2/DEST=kdeWave/KT=1/S={xMin,dX,xMax} w
WAVE/Z w1 = kdeWave
Variable auc = sum(w1)
w1 /= auc * (thick / 2) // omit *thick for a violin=2 i.e. two half-violins that =1

String wnL = NameofWave(w) + "_kdeL"
Duplicate/D/O w1 \$wnL
String wnR = NameofWave(w) + "_kdeR"
Duplicate/D/O w1 \$wnR
String wnLx = NameofWave(w) + "_kdeLx"
Duplicate/D/O w1 \$wnLx
String wnRx = NameofWave(w) + "_kdeRx"
Duplicate/D/O w1 \$wnRx
String vwny = NameofWave(w) + "_kdevy"
String vwnx = NameofWave(w) + "_kdevx"
KillWaves/Z w1
Wave wnL1 = \$wnL
Wave wnR1 = \$wnR
Wave wnLx1 = \$wnLx
Wave wnRx1 = \$wnRx
wnLx1 = x                   //get x scale
wnRx1 = x

if (trim == 1)
InsertPoints 0,1, wnLx1 //add point, set to same as first point to complete the bottom of a trimmed violin
InsertPoints 0,1, wnRx1
wnLx1 = wnLx1
wnRx1 = wnRx1
InsertPoints 0,1, wnL1
InsertPoints 0,1, wnR1
wnL1 = 0
wnR1 = 0
endif

wnR1 *= -1                                      //make the other side of violin
Reverse wnR1, wnRx1                             //reverse the righthand side of violin
Concatenate/O/NP {wnL1,wnR1}, \$vwny     // form the wave for y position of outline (will be x when vertical)
Concatenate/O/NP {wnLx1,wnRx1}, \$vwnx       // form the wave for x position of outline (will be y when vertical)
wave vwny1 = \$vwny
wave vwnx1 = \$vwnx

DoWindow/F \$tgtWin                                                      //bring target window to the front
SetDrawLayer ProgBack                                                       // violins are drawn in ProgBack
SetDrawEnv ycoord=left, xcoord=bottom, fillfgc=(65535,0,0),fillpat=3        // violins are red
DrawPoly xpos+vwny1,vwnx1,1,1, vwny1,vwnx1                    // draws violin
KillWaves/Z wnL1,wnR1,wnLx1,wnRx1                                       // clean-up
SetDrawLayer UserFront                                                  // set layer back
End

//Handy function to scrub the violins away
Function ClearViolins()
SVAR tgtWin = gTgtWin
DoWindow /F \$tgtWin
SetDrawLayer /K ProgBack // kills ProgBack
SetDrawLayer UserFront
End

Edit: updated this procedure to use StatsKDE. Only runs in IP7. Violin Demo file is out of date. Hello! I'm trying to use this to make violin plots with my data but I'm running into some issues I just can't quite get my head around. When I run the script with the "demo" data everything works fine, but when I try to run the script with my own data things go wrong. The boxplot x-axis just expands and there is no violin plots that is overlaid on it. I also tried making a "dummy wave" with 20 random values and trying to make the violin plot with that, but it would do the same thing. I've attached some screenshots of what happens when I run the macro, and I also attached one of the waves that I'm trying to represent as a violin plot. Any help or guidance would be much appreciated! Thanks in advance! Forum Support Gallery