Visualisation of Likert scale results

July 31, 2016
By

Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

[EDIT: The function now also inludes the possibility to plot the IQR around the median. I shifted the median slightly downwards to prevent the SD and the IQR from overlapping.]

I wrote a function to visualise results of Likert scale items. Please find the function below the post. Here is an example plot:

The function is called ‘plot.likert’ and takes the following arguments:
– vec: The vector with the raw results
– possible.values: A vector with all the possible values. This is sometimes important if not all possible responses were actually ticked by your participants. Defaults to all values found in vec.
– left: Annotation for the left side of the plot
– right: Annotation for the right side of the plot
– plot.median: Plot the median as a little line at the top? Defaults to FALSE.
– plot.sd: Plot the standard deviation around the mean? Defaults to TRUE.
– plot.iqr: Plot the IQR around the median? Defaults to FALSE.
– include.absolutes: Include the absolute values as small bold black numbers above the plot? Defaults to TRUE.
– include.percentages: Include the percentage values as blue numbers above the plot? Defaults to TRUE.
– own.margins: Override the default margins for the plot. Defaults to c(2,2,3,2).
– othermean: Plot another mean into the visualisation as grey line above the bars. I used this to compare the results to older results for the same questions. Defaults to NULL (no other mean is plotted in this case).
– …: Other parameters might be passed that are used for the first call to plot().
The example call for the plot shown above is:
plot.likert(sample(-2:2, 75, replace = T,
prob = c(0, .2, .2, .3, .3)),
left = “strongly disagree”,
right = “strongly agree”,
own.margins = c(2,2,5,2),
main = “I like this visualisation of Likert scale
results.”,
possible.values = -2:2,
othermean = 1.09)

NB: I know of all the stuff regarding the calculation of means on Likert scale items. However, it is still done a lot and you can also include the median after all…

Here’s the function:

plot.likert <- function (vec, possible.values = sort(unique(vec)), left = “linker Pol”, right = “rechter Pol”,
plot.median = F, plot.sd = T, plot.iqr = F, include.absolutes = T, include.percentages = T, own.margins = c(2, 2, 3, 2),
othermean = NULL, …) {
tab <- table(vec)
if (length(tab) != length(possible.values)) {
values.not.in.tab <- possible.values[!(possible.values %in% names(tab))]
for (val in values.not.in.tab) {
tab[as.character(val)] <- 0
}
tab <- tab[sort(names(tab))]
}
prop.tab <- prop.table(tab) * 100
v.sd <- sd(vec, na.rm = T)
v.m <- mean(vec, na.rm = T)
v.med <- median(vec, na.rm = T)
old.mar <- par(“mar”)
par(mar = own.margins)
# Setting-up plot region
plot(x = c(min(as.numeric(names(tab)), na.rm = T) – 1.1, max(as.numeric(names(tab)), na.rm = T) + 1.1),
y = c(0, 100), type = “n”, xaxt = “n”, yaxt = “n”,
xlab = “”, ylab = “”, bty = “n”, …)
# Bars
rect(xleft = as.numeric(names(prop.tab)) – .4,
ybottom = 0,
xright = as.numeric(names(prop.tab)) + .4,
ytop = prop.tab,
border = “#00000000”, col = “#ADD8E6E6”)
# Lower black line
lines(x = c(min(as.numeric(names(tab)), na.rm = T) – .6, max(as.numeric(names(tab)), na.rm = T) + .6),
y = c(0, 0), col = “black”, lwd = 2)
# Upper black line
lines(x = c(min(as.numeric(names(tab)), na.rm = T) – .6, max(as.numeric(names(tab)), na.rm = T) + .6),
y = c(100, 100), col = “black”, lwd = 2)
# Blue lines
for (n.i in names(tab)) {
lines(x = c(n.i, n.i), y = c(0, 100), col = “blue”)
}
# Grey rectangles at sides
rect(xleft = min(as.numeric(names(tab)), na.rm = T) – 1.1,
ybottom = 0,
xright = min(as.numeric(names(tab)), na.rm = T) – .6,
ytop = 100,
border = “#00000000”, col = “grey”)
rect(xleft = max(as.numeric(names(tab)), na.rm = T) + .6,
ybottom = 0,
xright = max(as.numeric(names(tab)), na.rm = T) + 1.1,
ytop = 100,
border = “#00000000”, col = “grey”)
mtext(names(prop.tab), side = 1, at = names(prop.tab))
# Percentages and numbers at the top
if (include.percentages) mtext(paste(round(prop.tab, 0), “%”), side = 3, at = names(prop.tab), line = -.3, col = “blue”)
if (include.absolutes) mtext(tab, side = 3, at = names(tab), line = .5, cex = .8, font = 2)
# Mean line
lines(x = c(v.m, v.m), y = c(95, 85), lwd = 6, col = “blue”)
# Median line
if (plot.median) lines(x = c(v.med, v.med), y = c(90, 80), lwd = 4, col = “#00FF00AA”)
# Inter-Quartile range
if (plot.iqr) {
arrows(x0 = c(v.med, v.med), x1 = c(v.med – IQR(vec, na.rm = T), v.med + IQR(vec, na.rm = T)),
y0 = c(85, 85), y1 = c(85, 85),
angle = 90, length = 0, lwd = 1)
}
# Other mean line
if (!is.null(othermean)) lines(x = c(othermean, othermean), y = c(85, 75), lwd = 6, col = “#00000099”)
# SD lines
if (plot.sd) { arrows(x0 = c(v.m, v.m), x1 = c(v.m – v.sd, v.m + v.sd), y0 = c(90, 90), y1 = c(90, 90),
angle = 90, length = 0, lwd = 1) }
# Left label
mtext(left, side = 2, line = -.5)
# Right label
mtext(right, side = 4, line = -.5)
par(mar = old.mar)
}

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.