Boxplot labelling outliers returns an error using data rownames

Viewed 104

I am trying to label the outliers in my boxplot using the text function so I can find out from which class the outliers are coming from. I've stored the rownames of my data in variable "rownames" using names(vehData) to get the row names. When I apply this however, I get an error.

ERROR: Error in which(removeOutliers1 == bxpdat$out, arr.ind = TRUE) : 'list' object cannot be coerced to type 'double'

Completely new to R programming. Completely not sure how to fix this or what I am doing wrong Thanks in advance for any help!

library(reshape2)
vehData <-
  structure(
    list(
      Samples = 1:6,
      Comp = c(95L, 91L, 104L, 93L, 85L,
               107L),
      Circ = c(48L, 41L, 50L, 41L, 44L, 57L),
      D.Circ = c(83L,
                 84L, 106L, 82L, 70L, 106L),
      Rad.Ra = c(178L, 141L, 209L, 159L,
                 205L, 172L),
      Pr.Axis.Ra = c(72L, 57L, 66L, 63L, 103L, 50L),
      Max.L.Ra = c(10L,
                   9L, 10L, 9L, 52L, 6L),
      Scat.Ra = c(162L, 149L, 207L, 144L, 149L,
                  255L),
      Elong = c(42L, 45L, 32L, 46L, 45L, 26L),
      Pr.Axis.Rect = c(20L,
                       19L, 23L, 19L, 19L, 28L),
      Max.L.Rect = c(159L, 143L, 158L, 143L,
                     144L, 169L),
      Sc.Var.Maxis = c(176L, 170L, 223L, 160L, 241L, 280L),
      Sc.Var.maxis = c(379L, 330L, 635L, 309L, 325L, 957L),
      Ra.Gyr = c(184L,
                 158L, 220L, 127L, 188L, 264L),
      Skew.Maxis = c(70L, 72L, 73L,
                     63L, 127L, 85L),
      Skew.maxis = c(6L, 9L, 14L, 6L, 9L, 5L),
      Kurt.maxis = c(16L,
                     14L, 9L, 10L, 11L, 9L),
      Kurt.Maxis = c(187L, 189L, 188L, 199L,
                     180L, 181L),
      Holl.Ra = c(197L, 199L, 196L, 207L, 183L, 183L),
      Class = c("van", "van", "saab", "van", "bus", "bus")
    ),
    row.names = c(NA,
                  6L), class = "data.frame")

#Remove outliers 
removeOutliers <- function(data) {
  OutVals <- boxplot(data)$out
  remOutliers <- sapply(data, function(x) x[!x %in% OutVals])
  return (remOutliers)
}

vehDataRemove1 <- vehData[, -1]
vehDataRemove2 <- vehDataRemove1[,-19]
vehData <- vehDataRemove2
vehClass <- vehData$Class

rownames <- names(vehData) #column names

#Begin removing outliers

removeOutliers1 <- removeOutliers(vehData)
bxpdat <- boxplot(removeOutliers1)

#Also tried using vehicles$Class instead of rownames but get the same error
text(bxpdat$group, bxpdat$out, 
     rownames[which(removeOutliers1 == bxpdat$out, arr.ind = TRUE)[,1]],
     pos = 4)

The boxplot looks like this. I am trying to label the outliers based on the x axis e.g. "Comp", "Circ", "D.Circ", "Rad.Ra", "Max.L.Ra" etc.. & by vehicle class "Van", "Bus" .. enter image description here

Crammed text issue when identifying class

enter image description here

2 Answers

If it is the outliers in the 2nd boxplot, it would be:

bxpdat <- boxplot(removeOutliers1)

text(bxpdat$group, bxpdat$out, 
     bxpdat$names[bxpdat$group],
     pos = 4)

enter image description here

Maybe looks better like this, if you adjust the margin and flip the labels:

par(mar=c(8,3.5,3.5,3.5))
bxpdat = boxplot(removeOutliers1,las=2,cex=0.5)
text(bxpdat$group, bxpdat$out, 
         bxpdat$names[bxpdat$group],
         pos = 4,cex=0.5)

enter image description here

I understood the question differently to @StupidWolf. I thought the goal was to replace points indicating outliers with the text of the vehicle class (bus, van or saab). If you simply print the variable name (e.g. Skew.maxis), then you might as well have simply plotted the outliers as points. Unless I'm missing something.

Here is code to answer the question as I understood it, for what it's worth (beginning after defining removeOutliers):

# CHANGE: Create vehClass vector before removing Class from the dataframe
vehClass <- vehData$Class

vehDataRemove1 <- vehData[, -1]
vehDataRemove2 <- vehDataRemove1[,-19]
vehData <- vehDataRemove2


#Begin removing outliers

removeOutliers1 <- removeOutliers(vehData)
bxpdat <- boxplot(removeOutliers1) # use boxplot(vehData) if you plot all the outliers as points

# loop over columns
n_plot <- 1; set.seed(123) # only plot n_plot randomly-chosen outliers 
for(i in 1:ncol(vehData)){
  
  # find out which row indices were removed as outliers
  diffInd <- which(vehData[[i]] %in%  setdiff(vehData[[i]], removeOutliers1[[i]]))
  # if none were, then don't add any outlier text
  if(length(diffInd) == 0) next
  print(i)
  print(paste0("l:", length(diffInd)))
  
  if(length(diffInd) > n_plot){
    diffIndPlot <- sample(diffInd, n_plot, replace = FALSE)
  } else diffIndPlot <- diffInd
  
  text(x = i, y = vehData[[i]][diffIndPlot], 
       labels = paste0(vehClass[diffIndPlot], ": ", vehData[[i]][diffIndPlot]))
}
Related