Here is one way. I used the data.table package below, so if you do not have it, install it first. If you really prefer the plot to be created using a regular data frame, let me know and I will adjust it accordingly.
library(data.table)
Change If you need a reverse graph, then the following code should work. It works similarly to the previous code.
myStem <- function(leftVals, rightVals, mainDigits, rounding = 10){ data = data.table("x" = leftVals, "ind" = "x") data = rbind(data, data.table("x" = rightVals, "ind" = "y")) data[, main := floor(x/10^mainDigits)] data[, right := round(x - main*10^mainDigits, rounding)] data = data[, ifelse(ind == "x", paste0(-sort(-right), collapse = ", "), paste0(sort(right), collapse = ", ")), by = c("ind", "main")] # For each 'main', Place all the 'right' values in a comma separated string. data = dcast(data, main ~ ind, value.var = "V1") data[, "left|" := ifelse(is.na(x), "", "|")] data[, "right|" := ifelse(is.na(y), "", "|")] data[, x := ifelse(is.na(x), "", x)] data[, y := ifelse(is.na(y), "", y)] data = data[, c("x", "left|", "main", "right|", "y"), with = F] maxLengthY = max(nchar(data$y)) data[, y := unlist(lapply(y, function(z) paste0(z, paste0(replicate(maxLengthY - nchar(z), " "), collapse = ""))))] colnames(data) = rep(" ", ncol(data)) data } # Example myStem(leftVals = c(43.2, 45.3, 48.1, 54.2), rightVals = c(30.2, 34.5, 44.3), 1) 1: 3 | 0.2, 4.5 2: 8.1, 5.3, 3.2 | 4 | 4.3 3: 4.2 | 5