【问题标题】:Is there a way to draw a vertical line using geom_abline?有没有办法使用 geom_abline 绘制垂直线?
【发布时间】:2021-09-19 15:50:54
【问题描述】:

(上下文:我正在使用 soc.ca 包,并希望说明整体 MCA 结果与每个特定类结果之间的角度差异。)

我有一个斜率对的数据框,我希望说明它们与 x = 0 和 y = 0 的轴有何不同。这在静止图像中可以正常工作。但是,理想情况下,我希望使用 gganimate 来说明从这些轴旋转的每对斜坡。

为了做到这一点,我想我需要使用 geom_abline。这可以很好地说明不同斜率之间的差异,但不能说明这些斜率与 x = 0 和 y = 0 之间的差异。

我的数据目前具有以下形状:

# set seed
set.seed(24601)

# load packages
library(tidyverse)
library(gganimate)

# generate vector of slopes
slopes <- 
  runif(12,
      -2,
      2)

# generate vector of cases i'm cycling through
cases <- 
  c(rep("a", 2),
    rep("b", 2),
    rep("c", 2),
    rep("d", 2),
    rep("e", 2),
    rep("f", 2)
  )

# generate vector of primary or secondary axes
axes <- 
  (rep(c("primary",
         "secondary"),
       6))

# generate vector of zeroes
zeroes <- 
  rep(0, 12)

# combine into a data frame
df <- 
  data.frame(slopes,
             cases,
             axes,
             zeroes)

静止图像可以如下生成

ggplot(df) +
  geom_abline(aes(slope = slopes,
                  linetype = axes,
                  intercept = 0)) +
  geom_hline(yintercept = 0) +
  geom_vline(xintercept = 0) +
  facet_wrap(~ cases)

而动画目前如下:

ggplot(df) +
  geom_abline(aes(intercept = zeroes,
                  slope = slopes,
                  linetype = axes)) +
  transition_states(cases) +
  scale_x_continuous(limits = c(-1, 1)) +
  scale_y_continuous(limits = c(-1, 1))

理想情况下,我希望轴在每个阶段之间“快速”回到 x = 0 和 y = 0。但是,为了做到这一点,我需要 geom_abline 的斜率在相关情况下为无穷大。

非常欢迎任何关于如何做到这一点的建议!

更新,解决方案

mhovd 向我展示了 geom_spoke,它解决了我遇到的问题。他们在动画方面遇到了几个问题,我想我已经设法解决了:下面是我对他们的代码的调整,使轴以我希望的方式旋转。

library(tidyverse)
library(gganimate)

# Seed
set.seed(24601)

# Given the following slopes
# nb I diverge from the helpful solution here
# by having the x and y slopes as separate variables

x_slopes <- 
  runif(6,
        -2,
        2)

y_slopes <- 
  runif(6,
        -2,
        2)

# What are the angles?
df = data.frame(
  x = 0, # Draw from zero
  y = 0, # Draw from zero
  angle_1 = atan(x_slopes), # generate primary axis slopes
  angle_2 = atan(y_slopes) + pi/2, # generate secondary axis slopes
  radius = 5, # Make sufficiently long lines
  cases =c("a", "b", "c", "d", "e", "f")
) %>% 
  mutate(  angle_3 = angle_1 + pi, # opposite angle for primary axis
           angle_4 = angle_2 + pi, # opposite angle for secondary axis
  )
  
df

# Add horizontal and vertical lines
# again this differs from the solution as i have 
# separate variables for the (originally) horizontal and vertical lines
original_axis_data = data.frame(x = 0, 
                        y = 0, 
                        angle_1 = atan(0), 
                        angle_2 = atan(0) + pi/2,
                        angle_3 = atan(0) + pi,
                        angle_4 = atan(0) + pi*1.5,
                        radius = 5, 
                        cases = "reset")

# combine these objects
df = rbind(df, original_axis_data)

# generate list (from solution)
animation_list = list(
  df %>% filter(cases == "a") %>% mutate(event = 1),
  df %>% filter(cases == "reset") %>% mutate(event = 2),
  df %>% filter(cases == "b") %>% mutate(event = 3),
  df %>% filter(cases == "reset") %>% mutate(event = 4),
  df %>% filter(cases == "c") %>% mutate(event = 5),
  df %>% filter(cases == "reset") %>% mutate(event = 6),
  df %>% filter(cases == "d") %>% mutate(event = 7),
  df %>% filter(cases == "reset") %>% mutate(event = 8),
  df %>% filter(cases == "e") %>% mutate(event = 9),
  df %>% filter(cases == "reset") %>% mutate(event = 10),
  df %>% filter(cases == "f") %>% mutate(event = 11),
  df %>% filter(cases == "reset") %>% mutate(event = 12)
)

# convert to data frame (again from solution)
animation_data = bind_rows(animation_list) %>% 
  mutate(cases = factor(cases))  # To make ggplot respect the order

# animate!
animation_data %>% 
  ggplot(aes(x = x, y = y, radius = radius)) + # i have simplified these aes
  geom_spoke(aes(angle = angle_1)) + # primary axis, original
  geom_spoke(aes(angle = angle_2),
             linetype = "dashed") + # secondary axis, original, linetype explicit
  geom_spoke(aes(angle = angle_3)) + # primary axis plus pi
  geom_spoke(aes(angle = angle_4), 
             linetype = "dashed") + # secondary axis plus pi
  coord_cartesian(ylim=c(-1, 1), xlim = c(-1, 1)) + # Zoom in without removing data like scale_*_continous does
  theme(legend.position = NULL, aspect.ratio = 1) +
  transition_states(event) +
  labs(title = "{closest_state}")

【问题讨论】:

  • 这可能不太可行,但如果您可以使用geom_segment 在绘图之外的两点之间画线,您可以使用它而不是斜率。因此,对于垂直线,您将使用点 x = c(0,0)y = c(-2, 2)
  • @mhovd 困难在于在绘图区域中保留 geom_segments - 如果删除 scale_x_continuous 和 scale_y_continuous 您会看到问题。我认为解决方案可能只是让我重新审视三角学!
  • 美丽的动画!

标签: r ggplot2 gganimate


【解决方案1】:

不使用 slopes,也许您可​​以使用 geom_spoke 使用 angles 来实现您想要的。 直线的倾角为theta = atan(slope)。对角是theta + pi

对于水平线和垂直线,你会分别通过0Inf 的斜率吗?

编辑:我在展示如何构建动画方面尝试得很糟糕,但我无法使其平滑过渡。希望您能够在此基础上再接再厉。

library(tidyverse)
library(gganimate)

# Seed
set.seed(24601)

# Given the following slopes
slopes <- runif(12,-2, 2)

# What are the angles?
df = data.frame(
  x = 0, # Draw from zero
  y = 0, # Draw from zero
  angle1 = atan(slopes), # First segment, actual angle
  angle2 = atan(slopes) + pi, # Second segment, opposite angle
  radius = 5, # Make sufficiently long lines
  cases =c(rep("a", 2), rep("b", 2), rep("c", 2), rep("d", 2), rep("e", 2), rep("f", 2)),
  axes = c("primary", "secondary")
)

# Add horizontal and vertical lines
hline_data = data.frame(x = 0, y  = 0, angle1 = atan(0), angle2 = atan(0) + pi, radius = 5, cases = "reset", axes = "primary")
vline_data = data.frame(x = 0, y  = 0, angle1 = atan(Inf), angle2 = atan(Inf) + pi, radius = 5, cases = "reset", axes = "primary")

df = rbind(df, vline_data, hline_data)

df %>% 
  ggplot(aes(x = x, y = y, radius = radius, col = cases, linetype = axes)) +
  geom_spoke(aes(angle = angle1)) +
  geom_spoke(aes(angle = angle2)) +
  coord_cartesian(ylim=c(-1, 1), xlim = c(-1, 1)) + # Zoom in without removing data like scale_*_continous does
  theme(legend.position = NULL, aspect.ratio = 1)

# Poor attempt at building the animation
animation_list = list(
  df %>% filter(cases == "a") %>% mutate(event = 1),
  df %>% filter(cases == "reset") %>% mutate(event = 2),
  df %>% filter(cases == "b") %>% mutate(event = 3),
  df %>% filter(cases == "reset") %>% mutate(event = 4),
  df %>% filter(cases == "c") %>% mutate(event = 5),
  df %>% filter(cases == "reset") %>% mutate(event = 6),
  df %>% filter(cases == "d") %>% mutate(event = 7),
  df %>% filter(cases == "reset") %>% mutate(event = 8),
  df %>% filter(cases == "e") %>% mutate(event = 9),
  df %>% filter(cases == "reset") %>% mutate(event = 10),
  df %>% filter(cases == "f") %>% mutate(event = 11),
  df %>% filter(cases == "reset") %>% mutate(event = 12)
)

animation_data = bind_rows(animation_list) %>% 
  mutate(cases = factor(cases))  # To make ggplot respect the order

animation_data %>% 
  ggplot(aes(x = x, y = y, radius = radius, linetype = axes, group = event)) +
  geom_spoke(aes(angle = angle1)) +
  geom_spoke(aes(angle = angle2)) +
  coord_cartesian(ylim=c(-1, 1), xlim = c(-1, 1)) + # Zoom in without removing data like scale_*_continous does
  theme(legend.position = NULL, aspect.ratio = 1) +
  transition_states(event) +
  labs(title = "{closest_state}")

reprex package (v2.0.0) 于 2021-07-09 创建

【讨论】:

  • 这正是我所需要的——我不知道geom_spoke,所以我可以用它来解决我的问题!通过使数据具有不同的形状(并且还通过从 aes() 括号中删除 group =,这使事情变得更加复杂),我对动画问题的处理方式略有不同,我将更新我的原始问题以显示解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-24
  • 1970-01-01
  • 2011-02-09
相关资源
最近更新 更多