1

Using the mtcars dataset and plot function with argument type= 'h', I would like to label observations with mtcars$wt >4 only, but did not manage. I tried:

 plot(mtcars$wt,type = 'h',ylim = c(0,6))
    abline(h=4)
    text(mtcars$wt,row.names(mtcars[mtcars$wt >4,]),pos =3,col='blue',cex=0.6))

But all bars get labelled:

enter image description here

I also tried the solution provided at Add labels to plot for specific values in R , but did not manage to make it work.

It would be great if the labels could also be on top of the bar with a 45 degree angle to avoid overlapping

ForEverNewbie
  • 357
  • 2
  • 10

2 Answers2

1

There may be several ways to fix this. I tried this:

plot(mtcars$wt,type = 'h',ylim = c(0,6))
abline(h=4)
labs <- ifelse(mtcars$wt >4, row.names(mtcars), "")
text(mtcars$wt,labs,pos =3,col='blue',cex=0.6)

For the second part of your question, I would have to use ggplot and ggrepel, it may not be exactly what you after, but it can avoid overlapping:

library(ggrepel)
library(tidyverse)
mtcars$x = 1:length(mtcars$wt)
mtcars %>% 
  ggplot(aes(x= x, xend =x, y = 0, yend= wt))+
  geom_segment() +
  geom_text_repel(aes(x= x, y = wt, label = labs), 
                      angle = 45
) 

enter image description here

Zhiqiang Wang
  • 6,206
  • 2
  • 13
  • 27
1

The text function arguments have a specific order (type ?text into the console), and when you don't use argument names the function fills the arguments in the order you've given. It seems you forgot to define y= or so.

Try this:

plot(mtcars$wt, type='h', ylim=c(0,6))
abline(h=4)
## here your old call with argument names
# text(x=mtcars$wt, y=row.names(mtcars[mtcars$wt >4,]), pos =3, col='blue', cex=0.6)
text(x=which(mtcars$wt > 4), y=mtcars$wt[mtcars$wt > 4], 
     labels=row.names(mtcars[mtcars$wt > 4,]), pos=3, col='blue', cex=0.6)

enter image description here

However, the labels are a bit lumped together. Here we could use Map that applies each argument one by one to the text function, and where we are able to add an adjustment vector to x and y arguments.

plot(mtcars$wt,type='h',ylim=c(0,6))
abline(h=4)
Map(function(x, y, labels) text(x, y, labels, pos=3, col="blue", cex=.6),
    x=which(mtcars$wt > 4) + c(0, -2.8, 0, 2.2),
    y=mtcars$wt[mtcars$wt > 4] + c(0, -.1, .1, -.1),
    labels=row.names(mtcars[mtcars$wt > 4,])
)

enter image description here

When we have more labels this might still look confusing to readers. Then we could use arrows which positions are defined in startpoints x0, y0 and endpoints x1, y1 and where we use the values we already have.

plot(mtcars$wt,type='h',ylim=c(0,8))
abline(h=4)
xpos. <- which(mtcars$wt > 4)
ypos. <- mtcars$wt[mtcars$wt > 4]
Map(function(x, y, labels) text(x, y, labels, pos=3, col="blue", cex=.6),
    x=xpos. + c(0, -6, 0, 6), y=ypos. + c(0, 1, 2, 1), 
    labels=row.names(mtcars[mtcars$wt > 4,])
)
arrows(x0=xpos. + c(0, -6, 0, 6), y0=ypos.+ c(0, 1, 2, 1), x1=xpos., y1=ypos.+.2,
       code=0, col="blue")

enter image description here

To rotate the labels, we can use srt= option, e.g. srt=45 for 45°.

plot(mtcars$wt,type='h',ylim=c(0,8))
abline(h=4)
text(x=which(mtcars$wt > 4), y=mtcars$wt[mtcars$wt > 4], 
     labels=row.names(mtcars[mtcars$wt > 4,]), pos=3, col='blue', cex=0.6, srt=45)

Note: Better use another device than the preview window, such as png() or pdf(), because otherwise everything shifts annoyingly all the time. See answers to this question on how to do this:

Now, have fun with tinkering! :)

jay.sf
  • 60,139
  • 8
  • 53
  • 110
  • 1
    Works great thank you @jay.sf, but in the event there are more nearby observations to be labelled perhaps the `map` function might not hold, so I thought having labels on a 45 degree angle, if possible, would be the ideal solution while still using `plot()` . – ForEverNewbie Jun 01 '20 at 05:36
  • 1
    Very welcome @ForEverNewbie Please see my updated answer for your follow-up questions. – jay.sf Jun 01 '20 at 06:02