R: ggplot с продолжительностью - PullRequest
0 голосов
/ 28 сентября 2018

Вопрос: Ваш совет для обработки продолжительности с ggplot2 (автор: Хэдли Уикхем).В частности: воспроизведите приведенные ниже графики с произвольными перерывами и подходящими надписями.Предпочтение минимального использования пользовательских функций и / или рефакторинга данных.Предложения с пакетами, которые я не цитировал, приветствуются.

Данные сохраняются в секундах (см. df ниже).Я хотел бы отображать читаемые глазом разрывы и метки, например, дни вместо тысяч секунд, где разрывы происходят через 0, 1, 2 ... дней вместо неловких дробей.

Подтверждение усилий: в первом примере, приведенном ниже, рассматриваются длительности в виде целых чисел и достигается цель путем соответствующего индивидуального деления на числа, кратные 60/24/365 и т. Д. Во втором примере используется базовое значение R difftime объектов.Чтобы сделать это правильно в этом случае, мне пришлось использовать функцию strptime и вычесть 1.Я что-то пропустил?В третьем примере используется класс duration из пакета lubridate.Хотя с помощью функций day() и seconds_to_period() указывать метки было довольно просто, я не очень хорошо справлялся с настройкой пользовательских перерывов.В четвертом примере используется класс hms.Мне удалось указать разрывы, но не метки.Любые предложения о том, как написать более короткие строки кода для каждого из приведенных ниже примеров, также приветствуются.

# Data
df = data.frame(x = 1:6, 
    num = c(374400, 343500, 174000, 193500, 197700, 270300))

# base/difftime
df$difftime <- as.difftime(df$num, units = "secs")

# lubridate/duration
library("lubridate")  # devtools::install_github("tidyverse/lubridate") # the dev version fixes a bug
df$duration <- duration(df$num, units = "seconds")

# hms/hms
library("hms")
df$hms <- as.hms(df$num) 

library("ggplot2")
library("scales")

# 1: data is base/numeric
# Pro: no package dependence
# Con: Hard work 
breaks = seq(0, 100*60*60, 20*60*60)
labels = function(x) round(x/60/60/24, 0)
ggplot(data = df, aes(x = x, y = num)) +
    geom_bar(stat = "identity", fill = "lightblue") +
    scale_y_continuous(name = "Duration (Days)", 
                       breaks = breaks,
                       labels = labels) +
    labs(title = "Data stored as numeric (seconds)", 
         subtitle = "breaks = seq(0, 100*60*60, 20*60*60)\nlabels = function(x) round(x/60/60/24, 0)",
         x = NULL) 
ggsave("base-num.png")

enter image description here

# 2: data is base/difftime
# Pro: simple once you get over the ``strftime(x, "%d")`` syntax.
# Unresolved: Why do I need to subtract a day?
labels = function(x) as.integer(strftime(x, "%d"))-1
ggplot(data = df, aes(x = x, y = difftime)) +
    geom_bar(stat = "identity", fill = "lightblue") +
    scale_y_time(name = "Duration (Days)", 
        labels = labels) +
    labs(title = "Data stored as difftime (seconds)", 
         subtitle = "default breaks\nlabels = function(x) as.integer(strftime(x, '%d'))-1",
         x = NULL) 
ggsave("base-difftime.png")

enter image description here

# 3: data is lubridate/duration
# Pro: intuitive combination of day() and seconds_to_period() functions
# Unresolved: a better way to make own breaks?
breaks = as.duration(seq(0, 5, 1)*60*60*24)
labels = function(x) day(seconds_to_period(x))
ggplot(data = df, aes(x = x, y = duration)) +
    geom_bar(stat = "identity", fill = "lightblue") +
    scale_y_continuous(name = "Duration (Days)", 
        breaks = breaks,
        labels = labels) +
    labs(title = "Data stored as duration (seconds)", 
         subtitle = "breaks = as.duration(seq(0, 5, 1)*60*60*24)\nlabels = function(x)lubridate::day(lubridate::seconds_to_period(x))",
         x = NULL) 
ggsave("lubridate-duration.png")

enter image description here

# 4: data is hms/hms
# Pro: Immediately generates plot with acceptable labels
# Unresolved: how to make own labels:  Failed attempts:
labels = 0:(length(breaks)-1)
labels = function(x)lubridate::day(x)

breaks = seq(0, 5, 1)*60*60*24
ggplot(data = df, aes(x = x, y = hms)) +
    geom_bar(stat = "identity", fill = "lightblue") +
    scale_y_continuous(name = "Duration (Seconds)",
        breaks = breaks) +
    labs(title = "Data stored as hms (seconds)", 
         subtitle = "breaks = seq(0, 5, 1)*60*60*24\ndefault labels",
         x = NULL) 
ggsave("hms-hms.png")

enter image description here

РЕДАКТИРОВАТЬ Следуя предложению Аксемана в разделе комментариев, это как объединить ggplot с hms объектами.Для меня это выглядит как самый удобный из четырех, хотя по общему признанию необходимость вычитать 1 неожиданна.Аксеман, вы хотите опубликовать это как ответ?

breaks = hms::hms(days = 0:4)
labels = function(x) lubridate::day(x)-1

enter image description here

1 Ответ

0 голосов
/ 28 сентября 2018

ИМХО, предложенные решения кажутся мне слишком сложными.

Если длительности даны как целые секунды и их нужно построить в дневном масштабе, мой подход - масштабировать их при вызове aes():

df = data.frame(x = 1:6, 
                num = c(374400, 343500, 174000, 193500, 197700, 270300))
library("ggplot2")
ggplot(data = df, aes(x = x, y = num / (24*60*60))) +
  geom_col(fill = "lightblue") +
  labs(title = "Data stored as numeric (seconds)",
       y = "Duration (Days)",
       x = NULL) 

enter image description here

Таким образом, не нужно возиться с перерывами и метками.

Примечание: geom_col() является заменойдля geom_bar(stat = "identity").

...