Vedda
Vedda

Reputation: 7445

How to show direction (heading) of obs in ggplot2

How can I show the direction (heading) of observations using ggplot2? Is there a way to adjust shape=17(triangle) so that it "points" to the next time observations?

Example Code

library(ggplot2)

dat <- data.frame(id = c(1, 1, 2, 2, 3, 3), 
                  time = c(1, 2, 1, 2, 1, 2),
                  x = c(.1, .2, .3, .4, .5, .6),
                  y = c(.6, .25, .4, .33, .2, .51))


ggplot(dat, aes(x, y, color=factor(id))) + 
  geom_point(shape=17) + 
  # geom_line() +
  NULL

enter image description here

Upvotes: 4

Views: 208

Answers (2)

M--
M--

Reputation: 29153

Combining ideas from dario's answer, How to scale a 2D vector and keep direction and Arranging arrows between points nicely in ggplot2

library(dplyr)
library(tidyr)
library(ggplot2)

dat %>% 
  pivot_wider(names_from = time, values_from = c(x, y)) %>% 
  group_by(id) %>% 
  mutate(x_v = x_2 - x_1, y_v = y_2 - y_1) %>% 
  mutate_at(vars("x_v", "y_v"), 
            list(units =~ (. / sqrt((x_v)^2 + (y_v)^2))/1000)) %>% 
  ggplot(aes(x=x_1, y=y_1, colour = factor(id))) + 
  geom_segment(aes(xend = x_1 + x_v_units, yend = y_1 + y_v_units),
              show.legend = F,
               arrow = arrow(length = unit(.3,"cm"), type="closed", angle = 20)) +
  geom_point(data = (dat %>% filter(time == 2)), aes(x, y), shape=15, size=2) +
  labs(x="x", y="y", colour="id") +
  theme_bw()

Data:

dat <- data.frame(id = c(1, 1, 2, 2, 3, 3), 
                  time = c(1, 2, 1, 2, 1, 2),
                  x = c(.1, .2, .3, .4, .5, .6),
                  y = c(.6, .25, .4, .33, .2, .51))

Upvotes: 2

dario
dario

Reputation: 6483

We can use ggplot2::geom_segment after we reshape the data using dplyr and tidyr::pivot_wider:

dat <- data.frame(id = c(1, 1, 2, 2, 3, 3), 
                  time = c(1, 2, 1, 2, 1, 2),
                  x = c(.1, .2, .3, .4, .5, .6),
                  y = c(.6, .25, .4, .33, .2, .51))
library(dplyr)
library(tidyr)
library(ggplot2)
dat %>% 
  pivot_wider(names_from = time, values_from = c(x, y)) %>% 
  ggplot(aes(x=x_1, y=y_1, color=factor(id))) + 
  geom_segment(aes(xend = x_2, yend = y_2),
               arrow = arrow(length = unit(.3,"cm"))) +
  labs(x="x", y="y", color="id")

ggplot with arrows

Edit:

but I just want the arrow pointing without lines.

I'm not sure how we should handle the second point for each id (since it has not direction) but if we want to omit them from the plot we can do:

library(dplyr)
library(tidyr)
library(ggplot2)
dat %>% 
  group_by(id) %>% 
  arrange(id, time) %>% 
  mutate(x_2 = x + 0.0001 * (lead(x) - x),
         y_2 = y + 0.0001 * (lead(y) - y)) %>%
  filter(!is.na(x_2)) %>% 
  ggplot(aes(x=x, y=y, color=factor(id))) + 
  geom_segment(aes(xend = x_2, yend = y_2),
               arrow = arrow(length = unit(.3,"cm"))) +
  labs(x="x", y="y", color="id")

no lines or dots

Or if we want the arrows to point to the next measurement, independently of the color we can use the code below (now there is only the last point missing because of no direction):

library(dplyr)
library(tidyr)
library(ggplot2)
dat %>% 
  arrange(id, time) %>% 
  mutate(x_2 = x + 0.0001 * (lead(x) - x),
         y_2 = y + 0.0001 * (lead(y) - y)) %>%
  filter(!is.na(x_2)) %>% 
  ggplot(aes(x=x, y=y, color=factor(id))) + 
  geom_segment(aes(xend = x_2, yend = y_2),
               arrow = arrow(length = unit(.3,"cm"))) +
  labs(x="x", y="y", color="id")

If we want to keep the 'last' measures we could add them in another geom_point layer...

Upvotes: 3

Related Questions