#load our data; today, we'll be looking at multiple students and their performance across school subjects
exam_scores <- data.frame(
    row.names = c("Student.1", "Student.2", "Student.3"),
      Biology = c(7.9, 3.9, 9.4),
      Physics = c(10, 20, 0),
        Maths = c(3.7, 11.5, 2.5),
        Sport = c(8.7, 20, 4),
      English = c(7.9, 7.2, 12.4),
    Geography = c(6.4, 10.5, 6.5),
          Art = c(2.4, 0.2, 9.8),
  Programming = c(0, 0, 20),
        Music = c(20, 20, 20)
)
exam_scores
NA
#install and load the package we're using to make radar plots
install.packages("fmsb")
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/fmsb_0.7.5.tgz'
Content type 'application/x-gzip' length 366431 bytes (357 KB)
=================
downloaded 126 KB
Warning in install.packages :
  downloaded length 129424 != reported length 366431
Warning in install.packages :
  URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/fmsb_0.7.5.tgz': status was 'Failure when receiving data from the peer'
Error in download.file(url, destfile, method, mode = "wb", ...) : 
  download from 'https://cran.rstudio.com/bin/macosx/contrib/4.1/fmsb_0.7.5.tgz' failed
Warning in install.packages :
  download of package ‘fmsb’ failed
library(fmsb)

Prepare our data to follow the radar plot specifications

#make a dataframe with the max and min values for each school subject
max_min <- data.frame(
  Biology = c(20, 0), Physics = c(20, 0), Maths = c(20, 0),
  Sport = c(20, 0), English = c(20, 0), Geography = c(20, 0),
  Art = c(20, 0), Programming = c(20, 0), Music = c(20, 0)
)
rownames(max_min) <- c("Max", "Min")
# make sure all of our exam scores fit within the max and min
df <- rbind(max_min, exam_scores)
df

Make the radar plot for one student

#extract the column that has to do with Student 2 from our big df, above
student2_data <- df[c("Max", "Min", "Student.2"), ]
radarchart(student2_data)

Awesome! We’ve made our first radar plot, even though it looks a bit bland. Lets make it look a bit better:

#specify a function that takes data and parameters on how to customize it, then returns a beautiful chart
#the function has default values for some variables, like color, in case we didn't specify them
create_beautiful_radarchart <- function(data, color = "#00AFBB", 
                                        vlabels = colnames(data), vlcex = 0.7,
                                        caxislabels = NULL, title = NULL){radarchart(
    data, axistype = 1,
    # Customize the polygon
    pcol = color, pfcol = scales::alpha(color, 0.5), plwd = 2, plty = 1,
    # Customize the grid
    cglcol = "grey", cglty = 1, cglwd = 0.8,
    # Customize the axis
    axislabcol = "grey", 
    # Variable labels
    vlcex = vlcex, vlabels = vlabels,
    caxislabels = caxislabels, title = title,
                                        )}
# use the create_beautiful_radarchart function to pretty-ify Student 2's data
create_beautiful_radarchart(student2_data, color = "#00AFFF", title = "Student 2's Graph", caxislabels = c(0, 25, 50, 75, 100))

op <- par(mar = c(1, 2, 2, 1))

create_beautiful_radarchart(student2_data, color = "#00ABFF", title = "Student 2's Performance")
par(op)

From this, we can see that Student 2 is strong in Physics, Sports, and Music, but didn’t perform well on their Programming assignments. Let’s see how he compares to his peers:

Comparing multiple students

# Reduce plot margin using par()
op <- par(mar = c(1, 2, 2, 2))
# Create the radar charts
create_beautiful_radarchart(
  data = df, caxislabels = c(0, 5, 10, 15, 20),
  color = c("#00AFBB", "#E7B800", "#FC4E07"), title = "Our Student's Data"
)
# Add an horizontal legend
legend(
  x = "bottom", legend = rownames(df[-c(1,2),]), horiz = TRUE,
  bty = "n", pch = 20 , col = c("#00AFBB", "#E7B800", "#FC4E07"),
  text.col = "black", cex = 1, pt.cex = 1
  )
#cex = font size, pt.cex = the size of the little color circles
par(op)

oops, its kinda hard to see each person’s data through all this overlap. Let’s showcase each student’s performance side by side:

# Define colors and titles
colors <- c("#00AFBB", "#E7B800", "#FC4E07")
titles <- c("Student.1", "Student.2", "Student.3")

# Reduce plot margin using par()
# Split the screen in 3 parts
op <- par(mar = c(1, 1, 1, 1))
par(mfrow = c(1,3))

# Create the radar chart
for(i in 1:3){
  create_beautiful_radarchart(
    data = df[c(1, 2, i+2), ], caxislabels = c(0, 5, 10, 15, 20),
    color = colors[i], title = titles[i]
    )
}
par(op)

*finish up demo by demonstrating how to compare each student’s data to an average

LS0tCnRpdGxlOiAiRXhwbG9yaW5nIHRoZSBmdW5jdGlvbmFsaXR5IG9mIHJhZGFyIHBsb3RzIHdpdGggdGhlIEZNU0IgbGlicmFyeSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyfQojbG9hZCBvdXIgZGF0YTsgdG9kYXksIHdlJ2xsIGJlIGxvb2tpbmcgYXQgbXVsdGlwbGUgc3R1ZGVudHMgYW5kIHRoZWlyIHBlcmZvcm1hbmNlIGFjcm9zcyBzY2hvb2wgc3ViamVjdHMKZXhhbV9zY29yZXMgPC0gZGF0YS5mcmFtZSgKICAgIHJvdy5uYW1lcyA9IGMoIlN0dWRlbnQuMSIsICJTdHVkZW50LjIiLCAiU3R1ZGVudC4zIiksCiAgICAgIEJpb2xvZ3kgPSBjKDcuOSwgMy45LCA5LjQpLAogICAgICBQaHlzaWNzID0gYygxMCwgMjAsIDApLAogICAgICAgIE1hdGhzID0gYygzLjcsIDExLjUsIDIuNSksCiAgICAgICAgU3BvcnQgPSBjKDguNywgMjAsIDQpLAogICAgICBFbmdsaXNoID0gYyg3LjksIDcuMiwgMTIuNCksCiAgICBHZW9ncmFwaHkgPSBjKDYuNCwgMTAuNSwgNi41KSwKICAgICAgICAgIEFydCA9IGMoMi40LCAwLjIsIDkuOCksCiAgUHJvZ3JhbW1pbmcgPSBjKDAsIDAsIDIwKSwKICAgICAgICBNdXNpYyA9IGMoMjAsIDIwLCAyMCkKKQpleGFtX3Njb3JlcwpgYGAKYGBge3J9CiNpbnN0YWxsIGFuZCBsb2FkIHRoZSBwYWNrYWdlIHdlJ3JlIHVzaW5nIHRvIG1ha2UgcmFkYXIgcGxvdHMKaW5zdGFsbC5wYWNrYWdlcygiZm1zYiIpCmxpYnJhcnkoZm1zYikKYGBgCiMjIFByZXBhcmUgb3VyIGRhdGEgdG8gZm9sbG93IHRoZSByYWRhciBwbG90IHNwZWNpZmljYXRpb25zCi0gdGhlIGZpcnN0IHJvdyBzcGVjaWZpZXMgdGhlIG1heGltdW0gdmFsdWVzIGZvciBldmVyeSBjb2x1bW4gKGV4LCB0aGUgbWF4aW11bSBncmFkZXMgaW4gYmlvbG9neSwgcGh5c2ljcywgbWF0aCwgZXRjKQotIHRoZSBzZWNvbmQgcm93IHNwZWNpZmllcyB0aGUgbWluIHZhbHVlcyBmb3IgZXZlciBjb2x1bW4KLSB0aGUgdGhpcmQgcm93IGlzIHdoZXJlIHdlIGNhbiBzdGFydCBpbnB1dHRpbmcgZGF0YSBmb3IgZWFjaCBzdHVkZW50CmBgYHtyfQojbWFrZSBhIGRhdGFmcmFtZSB3aXRoIHRoZSBtYXggYW5kIG1pbiB2YWx1ZXMgZm9yIGVhY2ggc2Nob29sIHN1YmplY3QKbWF4X21pbiA8LSBkYXRhLmZyYW1lKAogIEJpb2xvZ3kgPSBjKDIwLCAwKSwgUGh5c2ljcyA9IGMoMjAsIDApLCBNYXRocyA9IGMoMjAsIDApLAogIFNwb3J0ID0gYygyMCwgMCksIEVuZ2xpc2ggPSBjKDIwLCAwKSwgR2VvZ3JhcGh5ID0gYygyMCwgMCksCiAgQXJ0ID0gYygyMCwgMCksIFByb2dyYW1taW5nID0gYygyMCwgMCksIE11c2ljID0gYygyMCwgMCkKKQpyb3duYW1lcyhtYXhfbWluKSA8LSBjKCJNYXgiLCAiTWluIikKYGBgCmBgYHtyfQojIG1ha2Ugc3VyZSBhbGwgb2Ygb3VyIGV4YW0gc2NvcmVzIGZpdCB3aXRoaW4gdGhlIG1heCBhbmQgbWluCmRmIDwtIHJiaW5kKG1heF9taW4sIGV4YW1fc2NvcmVzKQpkZgpgYGAKIyMjIE1ha2UgdGhlIHJhZGFyIHBsb3QgZm9yIG9uZSBzdHVkZW50CmBgYHtyfQojZXh0cmFjdCB0aGUgY29sdW1uIHRoYXQgaGFzIHRvIGRvIHdpdGggU3R1ZGVudCAyIGZyb20gb3VyIGJpZyBkZiwgYWJvdmUKc3R1ZGVudDJfZGF0YSA8LSBkZltjKCJNYXgiLCAiTWluIiwgIlN0dWRlbnQuMiIpLCBdCnJhZGFyY2hhcnQoc3R1ZGVudDJfZGF0YSkKYGBgCkF3ZXNvbWUhIFdlJ3ZlIG1hZGUgb3VyIGZpcnN0IHJhZGFyIHBsb3QsIGV2ZW4gdGhvdWdoIGl0IGxvb2tzIGEgYml0IGJsYW5kLiBMZXRzIG1ha2UgaXQgbG9vayBhIGJpdCBiZXR0ZXI6CgpgYGB7cn0KI3NwZWNpZnkgYSBmdW5jdGlvbiB0aGF0IHRha2VzIGRhdGEgYW5kIHBhcmFtZXRlcnMgb24gaG93IHRvIGN1c3RvbWl6ZSBpdCwgdGhlbiByZXR1cm5zIGEgYmVhdXRpZnVsIGNoYXJ0CiN0aGUgZnVuY3Rpb24gaGFzIGRlZmF1bHQgdmFsdWVzIGZvciBzb21lIHZhcmlhYmxlcywgbGlrZSBjb2xvciwgaW4gY2FzZSB3ZSBkaWRuJ3Qgc3BlY2lmeSB0aGVtCmNyZWF0ZV9iZWF1dGlmdWxfcmFkYXJjaGFydCA8LSBmdW5jdGlvbihkYXRhLCBjb2xvciA9ICIjMDBBRkJCIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2bGFiZWxzID0gY29sbmFtZXMoZGF0YSksIHZsY2V4ID0gMC43LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F4aXNsYWJlbHMgPSBOVUxMLCB0aXRsZSA9IE5VTEwpe3JhZGFyY2hhcnQoCiAgICBkYXRhLCBheGlzdHlwZSA9IDEsCiAgICAjIEN1c3RvbWl6ZSB0aGUgcG9seWdvbgogICAgcGNvbCA9IGNvbG9yLCBwZmNvbCA9IHNjYWxlczo6YWxwaGEoY29sb3IsIDAuNSksIHBsd2QgPSAyLCBwbHR5ID0gMSwKICAgICMgQ3VzdG9taXplIHRoZSBncmlkCiAgICBjZ2xjb2wgPSAiZ3JleSIsIGNnbHR5ID0gMSwgY2dsd2QgPSAwLjgsCiAgICAjIEN1c3RvbWl6ZSB0aGUgYXhpcwogICAgYXhpc2xhYmNvbCA9ICJncmV5IiwgCiAgICAjIFZhcmlhYmxlIGxhYmVscwogICAgdmxjZXggPSB2bGNleCwgdmxhYmVscyA9IHZsYWJlbHMsCiAgICBjYXhpc2xhYmVscyA9IGNheGlzbGFiZWxzLCB0aXRsZSA9IHRpdGxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKX0KYGBgCgoKYGBge3J9CiMgdXNlIHRoZSBjcmVhdGVfYmVhdXRpZnVsX3JhZGFyY2hhcnQgZnVuY3Rpb24gdG8gcHJldHR5LWlmeSBTdHVkZW50IDIncyBkYXRhCmNyZWF0ZV9iZWF1dGlmdWxfcmFkYXJjaGFydChzdHVkZW50Ml9kYXRhLCBjb2xvciA9ICIjMDBBRkZGIiwgdGl0bGUgPSAiU3R1ZGVudCAyJ3MgR3JhcGgiLCBjYXhpc2xhYmVscyA9IGMoMCwgMjUsIDUwLCA3NSwgMTAwKSkKCm9wIDwtIHBhcihtYXIgPSBjKDEsIDIsIDIsIDEpKQpjcmVhdGVfYmVhdXRpZnVsX3JhZGFyY2hhcnQoc3R1ZGVudDJfZGF0YSwgY29sb3IgPSAiIzAwQUJGRiIsIHRpdGxlID0gIlN0dWRlbnQgMidzIFBlcmZvcm1hbmNlIikKcGFyKG9wKQpgYGAKCgpGcm9tIHRoaXMsIHdlIGNhbiBzZWUgdGhhdCBTdHVkZW50IDIgaXMgc3Ryb25nIGluIFBoeXNpY3MsIFNwb3J0cywgYW5kIE11c2ljLCBidXQgZGlkbid0IHBlcmZvcm0gd2VsbCBvbiB0aGVpciBQcm9ncmFtbWluZyBhc3NpZ25tZW50cy4gTGV0J3Mgc2VlIGhvdyBoZSBjb21wYXJlcyB0byBoaXMgcGVlcnM6CgojIyBDb21wYXJpbmcgbXVsdGlwbGUgc3R1ZGVudHMKYGBge3J9CiMgUmVkdWNlIHBsb3QgbWFyZ2luIHVzaW5nIHBhcigpCm9wIDwtIHBhcihtYXIgPSBjKDEsIDIsIDIsIDIpKQojIENyZWF0ZSB0aGUgcmFkYXIgY2hhcnRzCmNyZWF0ZV9iZWF1dGlmdWxfcmFkYXJjaGFydCgKICBkYXRhID0gZGYsIGNheGlzbGFiZWxzID0gYygwLCA1LCAxMCwgMTUsIDIwKSwKICBjb2xvciA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiksIHRpdGxlID0gIk91ciBTdHVkZW50J3MgRGF0YSIKKQojIEFkZCBhbiBob3Jpem9udGFsIGxlZ2VuZApsZWdlbmQoCiAgeCA9ICJib3R0b20iLCBsZWdlbmQgPSByb3duYW1lcyhkZlstYygxLDIpLF0pLCBob3JpeiA9IFRSVUUsCiAgYnR5ID0gIm4iLCBwY2ggPSAyMCAsIGNvbCA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIsICIjRkM0RTA3IiksCiAgdGV4dC5jb2wgPSAiYmxhY2siLCBjZXggPSAxLCBwdC5jZXggPSAxCiAgKQojY2V4ID0gZm9udCBzaXplLCBwdC5jZXggPSB0aGUgc2l6ZSBvZiB0aGUgbGl0dGxlIGNvbG9yIGNpcmNsZXMKcGFyKG9wKQoKI25lZWQgdG8gaW5jcmVhc2UgdGhlIHBhZGRpbmcgYmV0d2VlbiB0aGUgbGVnZW5kIGFuZCB0aGUgZ3JhcGgKYGBgCm9vcHMsIGl0cyBraW5kYSBoYXJkIHRvIHNlZSBlYWNoIHBlcnNvbidzIGRhdGEgdGhyb3VnaCBhbGwgdGhpcyBvdmVybGFwLiBMZXQncyBzaG93Y2FzZSBlYWNoIHN0dWRlbnQncyBwZXJmb3JtYW5jZSBzaWRlIGJ5IHNpZGU6CmBgYHtyfQojIERlZmluZSBjb2xvcnMgYW5kIHRpdGxlcwpjb2xvcnMgPC0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciKQp0aXRsZXMgPC0gYygiU3R1ZGVudC4xIiwgIlN0dWRlbnQuMiIsICJTdHVkZW50LjMiKQoKIyBSZWR1Y2UgcGxvdCBtYXJnaW4gdXNpbmcgcGFyKCkKIyBTcGxpdCB0aGUgc2NyZWVuIGluIDMgcGFydHMKb3AgPC0gcGFyKG1hciA9IGMoMSwgMSwgMSwgMSkpCnBhcihtZnJvdyA9IGMoMSwzKSkKCiMgQ3JlYXRlIHRoZSByYWRhciBjaGFydApmb3IoaSBpbiAxOjMpewogIGNyZWF0ZV9iZWF1dGlmdWxfcmFkYXJjaGFydCgKICAgIGRhdGEgPSBkZltjKDEsIDIsIGkrMiksIF0sIGNheGlzbGFiZWxzID0gYygwLCA1LCAxMCwgMTUsIDIwKSwKICAgIGNvbG9yID0gY29sb3JzW2ldLCB0aXRsZSA9IHRpdGxlc1tpXQogICAgKQp9CnBhcihvcCkKCmBgYAoKKmZpbmlzaCB1cCBkZW1vIGJ5IGRlbW9uc3RyYXRpbmcgaG93IHRvIGNvbXBhcmUgZWFjaCBzdHVkZW50J3MgZGF0YSB0byBhbiBhdmVyYWdlCg==