You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ggplot2中自动绘制从指定原点出发的射线?(避用三角函数)

在ggplot2中自动绘制从质心出发的射线(无需三角函数)

问题背景

在ggplot2中绘制线段(geom_segment)或折线(geom_line)很简单,但要绘制从指定原点(比如数据质心)出发、经过每个数据点的射线(无限延伸的直线)则需要额外处理。需求是:从红色质心点出发,向每个黑色数据点的方向绘制射线,且避免使用三角函数,实现自动化绘制。

现有代码(线段版本)

library(dplyr)
library(ggplot2) # 原代码中require(ggplot)应为ggplot2

set.seed(123)
n <- 4

dat <- data.frame(
  X = rnorm(n),
  Y = rnorm(n)
)

centroid <- dat %>%
  dplyr::summarise(across(c(X,Y), mean))

ggplot(data = dat, aes(X,Y)) +
  geom_point() +
  geom_point(data = centroid, aes(X, Y), col = "red") +
  geom_segment(x = centroid$X, y = centroid$Y,
               xend = dat$X, yend = dat$Y)

解决方案:通过边界交点生成射线

核心思路是:计算射线与绘图边界的交点,将geom_segment的终点设为该交点,从而模拟射线效果。全程无需三角函数,完全基于线性计算实现自动化。

方法一:手动扩展数据范围

先计算数据的极值并扩展,确定射线的延伸边界,再计算每个方向的交点:

library(dplyr)
library(ggplot2)

set.seed(123)
n <- 4

dat <- data.frame(
  X = rnorm(n),
  Y = rnorm(n)
)

centroid <- dat %>%
  summarise(across(c(X,Y), mean))

# 计算并扩展数据范围,确保射线超出数据点
x_range <- range(dat$X)
y_range <- range(dat$Y)
expand_factor <- 1.2
x_lim <- c(min(x_range) - diff(x_range)*0.1, max(x_range) + diff(x_range)*0.1) * expand_factor
y_lim <- c(min(y_range) - diff(y_range)*0.1, max(y_range) + diff(y_range)*0.1) * expand_factor

# 为每个数据点计算射线终点(与边界的交点)
ray_end <- dat %>%
  mutate(
    # 计算质心到数据点的方向向量
    dx = X - centroid$X,
    dy = Y - centroid$Y,
    # 计算到达x/y边界所需的延伸倍数
    t_x_left = (x_lim[1] - centroid$X)/dx,
    t_x_right = (x_lim[2] - centroid$X)/dx,
    t_y_bottom = (y_lim[1] - centroid$Y)/dy,
    t_y_top = (y_lim[2] - centroid$Y)/dy,
    # 取正倍数中的最大值(射线向外延伸,t必须为正)
    t = pmax(
      ifelse(dx != 0, pmax(t_x_left, t_x_right), 0),
      ifelse(dy != 0, pmax(t_y_bottom, t_y_top), 0)
    ),
    # 处理水平/垂直射线的特殊情况
    t = ifelse(dx == 0 & dy > 0, t_y_top, t),
    t = ifelse(dx == 0 & dy < 0, t_y_bottom, t),
    t = ifelse(dy == 0 & dx > 0, t_x_right, t),
    t = ifelse(dy == 0 & dx < 0, t_x_left, t),
    # 计算最终终点坐标
    X_end = centroid$X + dx * t,
    Y_end = centroid$Y + dy * t
  ) %>%
  select(X_end, Y_end)

# 绘制射线
ggplot(data = dat, aes(X,Y)) +
  geom_point() +
  geom_point(data = centroid, aes(X, Y), col = "red") +
  geom_segment(
    data = ray_end,
    x = centroid$X, y = centroid$Y,
    xend = X_end, yend = Y_end,
    color = "blue", linetype = "solid"
  ) +
  xlim(x_lim) +
  ylim(y_lim)

方法二:自动获取绘图边界

利用ggplot_build获取ggplot自动计算的坐标范围,无需手动设置扩展因子,适配性更强:

library(dplyr)
library(ggplot2)

set.seed(123)
n <- 4

dat <- data.frame(
  X = rnorm(n),
  Y = rnorm(n)
)

centroid <- dat %>%
  summarise(across(c(X,Y), mean))

# 先绘制基础图,获取自动计算的坐标范围
base_plot <- ggplot(data = dat, aes(X,Y)) +
  geom_point() +
  geom_point(data = centroid, aes(X, Y), col = "red")

plot_build <- ggplot_build(base_plot)
x_lim <- plot_build$layout$panel_params[[1]]$x.range
y_lim <- plot_build$layout$panel_params[[1]]$y.range

# 计算射线终点
ray_end <- dat %>%
  mutate(
    dx = X - centroid$X,
    dy = Y - centroid$Y,
    t_x_left = (x_lim[1] - centroid$X)/dx,
    t_x_right = (x_lim[2] - centroid$X)/dx,
    t_y_bottom = (y_lim[1] - centroid$Y)/dy,
    t_y_top = (y_lim[2] - centroid$Y)/dy,
    t = pmax(
      ifelse(dx != 0, pmax(t_x_left, t_x_right), 0),
      ifelse(dy != 0, pmax(t_y_bottom, t_y_top), 0)
    ),
    t = ifelse(dx == 0 & dy > 0, t_y_top, t),
    t = ifelse(dx == 0 & dy < 0, t_y_bottom, t),
    t = ifelse(dy == 0 & dx > 0, t_x_right, t),
    t = ifelse(dy == 0 & dx < 0, t_x_left, t),
    X_end = centroid$X + dx * t,
    Y_end = centroid$Y + dy * t
  ) %>%
  select(X_end, Y_end)

# 叠加射线到基础图
base_plot +
  geom_segment(
    data = ray_end,
    x = centroid$X, y = centroid$Y,
    xend = X_end, yend = Y_end,
    color = "blue", linetype = "solid"
  )

方案说明

  • 无需三角函数:通过方向向量和线性插值计算射线与边界的交点,完全基于代数运算。
  • 自动化处理:对每个数据点自动生成对应的射线终点,包括水平、垂直射线的特殊情况。
  • 灵活适配:两种方法分别适用于需要手动控制边界和依赖ggplot自动布局的场景。

内容的提问来源于stack exchange,提问作者Arnaud Louchart

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 16:15:55