Ggplot2: `position_nudge` В зависимости от `linewidth`

Я ищу способ объединить geom_segment() с geom_point() и определить position так, чтобы он всегда был связан с linewidth.

Репрекс

library(ggplot2)

# Sample data for geom_segment
segment_data <- data.frame(
  y = 1:5,
  x = as.POSIXct(c("2024-04-15 08:00:00", "2024-04-15 08:15:00", "2024-04-15 08:30:00", "2024-04-15 08:45:00", "2024-04-15 09:00:00")),
  xend = as.POSIXct(c("2024-04-15 09:00:00", "2024-04-15 09:15:00", "2024-04-15 09:30:00", "2024-04-15 09:45:00", "2024-04-15 10:00:00"))
)

# Sample data for geom_point
point_data <- data.frame(
  y = c(1, 2, 3, 4, 5), # Adjusted y values for points
  x = as.POSIXct(c("2024-04-15 08:05:00", "2024-04-15 08:20:00", "2024-04-15 08:35:00", "2024-04-15 08:50:00", "2024-04-15 09:05:00"))
)

linewidth <- 2

ggplot() +
  geom_segment(segment_data, mapping = aes(x = x, xend = xend, y = y),
               linewidth = linewidth) + 
  geom_point(point_data, mapping = aes(x = x, y = y), 
             shape = 25, color = "black", fill = "red", size = 5,
             position = position_nudge(y = linewidth/25))

Выход

Это дает желаемый график, где точки по существу являются индикаторами вдоль линий сегмента:

В этом примере linewidth равно 2, а подталкивание — linewidth/25, что очень произвольно. Я ожидал, что linewidth/2 будет достаточно.

Проблема

Как я могу определить значение y для position_nudge(), чтобы это имело место независимо от значения linewidth?

Спасибо за ваше внимание.

Я думаю, что сложность здесь в том, что linewidth параметризуется в пространстве графика (например, в пикселях), а position_nudge параметризуется в пространстве координат (например, значения y), поэтому вам понадобится что-то, что соединяет эти два в контексте вашего конкретного графика. . В зависимости от диапазона данных, диапазона координат и разрешения графика каждая единица y будет охватывать разное количество пикселей.

Jon Spring 15.04.2024 22:07

@JonSpring, спасибо за понимание и ответ. Я знал, что это как-то связано с этим, но не мог подобрать правильную терминологию. В идеале я хотел бы включить это в пакет, который я пишу, и позволить пользователям легко размещать индикаторы-маркеры вдоль своих сегментов лучшим способом, чем «продолжать попытки, пока все не будет выглядеть правильно», если это возможно.

rsh52 15.04.2024 22:10

Другой подход может заключаться в замене geom_segment на geom_rect и geom_point(shape = 25) на geom_polygon, а затем построить их на основе указанной вами константы масштабирования. Тогда вы сможете составить план за один раз, но с большим количеством предварительных ошибок.

Jon Spring 15.04.2024 23:41
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

TL;DR: Как говорит Джон Спринг, вам потребуется сочетание единиц данных и абсолютных единиц, чтобы сделать это правильно. Вот формула, которая вам нужна для смещения shape = 25: 0.55 * unit(fontsize, "pt") + unit((lwd * .pt) / .stroke * 0.5, "pt").

Теперь к реальным примерам; первое: давайте настроим таким же образом:

library(ggplot2)

# Sample data for geom_segment
segment_data <- data.frame(
  y = 1:5,
  x = as.POSIXct(c("2024-04-15 08:00:00", "2024-04-15 08:15:00", "2024-04-15 08:30:00", "2024-04-15 08:45:00", "2024-04-15 09:00:00")),
  xend = as.POSIXct(c("2024-04-15 09:00:00", "2024-04-15 09:15:00", "2024-04-15 09:30:00", "2024-04-15 09:45:00", "2024-04-15 10:00:00"))
)

# Sample data for geom_point
point_data <- data.frame(
  y = c(1, 2, 3, 4, 5), # Adjusted y values for points
  x = as.POSIXct(c("2024-04-15 08:05:00", "2024-04-15 08:20:00", "2024-04-15 08:35:00", "2024-04-15 08:50:00", "2024-04-15 09:05:00"))
)

linewidth <- 2

Затем мы сделаем два графика. Тот, который мы будем использовать позже, и тот, который включает слой точек.

p <- ggplot() +
  geom_segment(segment_data, mapping = aes(x = x, xend = xend, y = y),
               linewidth = linewidth, colour = "grey50")
p2 <- p + 
  geom_point(point_data, mapping = aes(x = x, y = y), 
             shape = 25, color = "black", fill = "red", size = 5)

Из последнего графика мы собираемся извлечь графический объект слоя (grob). Из grob вы можете извлечь графические параметры, необходимые для расчета, например точку fontsize и отрезок lwd.

Эту формулу я получил методом проб и ошибок, но она должна быть довольно последовательной, когда linewidth и size меняются. Мы просто добавляем это к уже существующим координатам y точек, а затем рисуем как пользовательский слой.

segments <- layer_grob(p2, i = 1)[[1]]
points   <- layer_grob(p2, i = 2)[[1]]

points$y <- points$y + 0.55 * unit(points$gp$fontsize, "pt") + 
  unit((segments$gp$lwd * .pt) / .stroke * 0.5, "pt")

p + annotation_custom(points)

Однако учтите: это не работает ни с какой формой. Вы можете видеть, что над линиями плавают круги.

points$pch <- 21
p + annotation_custom(points)

Created on 2024-04-15 with reprex v2.1.0

Чтобы это работало с другими фигурами, измените 0.55 в формуле, чтобы она соответствовала новой фигуре. Теперь, поскольку вы упомянули, что можете написать для этого пакет, я рекомендую просто написать расширение geom, которое вычисляет size/fontsize и linewidth/lwd точно так же, как geom_segment() и geom_point(), а затем применяет это смещение. Таким образом, вам не придется взламывать его с помощью annotation_custom() и тому подобного, так как из-за граней он может стать корявым.

Это так информативно и полезно! Спасибо, я рассмотрю возможность применения формулы к новой геометрии, как вы описали. Оцените развернутый ответ.

rsh52 15.04.2024 23:17

из текста ggplot2 мне немного неясно, как обернуть обе существующие геометрии в расширение, чтобы добиться этого. Для этого пакета пользователям необходимо будет иметь возможность указывать сегменты и точки в отдельных функциональных командах. Я вижу выгоду, если мы сможем разгрузить логику, annotation_custom() немного ограничен в гибкости.

rsh52 29.04.2024 22:13

Другие вопросы по теме