首页 文章

带有可变条宽的条形图作为x轴上的日期范围

提问于
浏览
2

我想制作一个条形图,其中响应变量(重量变化)是在不同长度的时间段内测量的,由开始和结束日期定义 . 条形的宽度应对应于周期的长度 . 我的数据的一个小例子:

wtchange.data <- structure(list(start.date = structure(1:3, .Label = c("2015-04-01", 
    "2015-04-15", "2015-04-30"), class = "factor"), end.date = structure(1:3, .Label = c("2015-04-15", 
    "2015-04-30", "2015-05-30"), class = "factor"), wtchange = c(5L, 
    10L, 15L), se = c(1.2, 2.5, 0.8)), .Names = c("start.date", "end.date", 
    "wtchange", "se"), class = "data.frame", row.names = c(NA, -3L
    ))

wtchange.data
#   start.date   end.date wtchange  se
# 1 2015-04-01 2015-04-15        5 1.2
# 2 2015-04-15 2015-04-30       10 2.5
# 3 2015-04-30 2015-05-30       15 0.8

wtchange.data$start.date <- as.Date(wtchange.data$start.date)
wtchange.data$end.date <- as.Date(wtchange.data$end.date)

试图使用 geom_bar

library(ggplot2)
ggplot(wtchange.data, aes(x = start.date, y = wtchange)) +
  geom_bar(stat = "identity", color = "black") +
  geom_errorbar(aes(ymin = wtchange-se, ymax = wtchange+se), width = 1)

(不允许> 2个链接,<10声望,所以遗憾的是不能显示第一个情节)

主要的问题是,当定义绘图区域的美学时( x = start.date, y = wtchange ),我只能使用一个变量(在本例中为start.date)作为x轴,但我真的需要以某种方式同时使用start.date和end .date用于分隔与每个句点对应的条形宽度 . 该图应该看起来像这样(在Paint中绘制):
enter image description here

第二个问题是杆应该没有间隙地接触,但是我不确定它是否可能,因为杆必须具有不同的宽度,所以你不能为所有杆设置一个杆宽 . 是否可以手动设置每个条的宽度?


编辑:谢谢Henrik的链接 . 我取得了一些进一步的进展 . 我计算了日期中点,使条形中心位于:

wtchange.data$date.midpoint <- wtchange.data$start.date +
(wtchange.data$end.date - wtchange.data$start.date)/2

然后计算使用条宽度的周期长度:

wtchange.data$period.length <- wtchange.data$end.date - wtchange.data$start.date

更新后的图形代码现在是:

ggplot(wtchange.data, aes(x = date.midpoint, y = wtchange)) +
  geom_bar(stat = "identity", color = "black", width = wtchange.data$period.length) +
  geom_errorbar(aes(ymin = wtchange-se, ymax = wtchange+se), width = 1)

enter image description here

剩下的唯一问题是在一个地方的酒吧之间仍然存在小的差距 . 我想这是由于R将日期差计算方法计算到最接近的天数?

1 回答

  • 1

    你是对的:它是结束日期和开始日期之间差异的计算,这是差距的原因 . 在计算宽度和中点时,我们需要使用 numeric 句点而不是 difftime (请参阅下面的说明) .

    # length of periods, width of bars as numeric
    df$width <- as.numeric(df$end.date - df$start.date) 
    
    # mid-points
    df$mid <- df$start.date + df$width / 2
    
    # dates for breaks 
    dates <- unique(c(df$start.date, df$end.date))
    
    ggplot(df, aes(x = mid, y = wtchange)) +
      geom_bar(stat = "identity", color = "black", width = df$width) +
      geom_errorbar(aes(ymin = wtchange - se, ymax = wtchange + se), width = 1) +
      scale_x_date(breaks = dates)
    

    enter image description here


    对应 geom_rect 代码:

    # mid-points
    df$mid <- df$start.date + as.numeric(df$end.date - df$start.date) / 2
    
    # dates for breaks 
    dates <- unique(c(df$start.date, df$end.date))
    
    ggplot(df, aes(x = mid, y = wtchange)) +
      geom_rect(aes(xmin = start.date, xmax = end.date, ymin = 0, ymax = wtchange), color = "black") +
      geom_errorbar(aes(ymin = wtchange - se, ymax = wtchange + se), width = 1) +
      scale_x_date(breaks = dates)
    

    geom_step 对油墨的要求略低:

    # need to add an end date to the last period
    df2 <- tail(df, 1)
    df2$start.date <- df2$end.date
    df2 <- rbind(df, df2)
    
    # mid-points
    df$mid <- df$start.date + as.numeric(df$end.date - df$start.date) / 2
    
    ggplot() +
      geom_step(data = df2, aes(x = start.date, y = wtchange)) +
      geom_errorbar(data = df, aes(x = mid, ymin = wtchange - se, ymax = wtchange + se), width = 1) +
      scale_x_date(breaks = dates) +
      ylim(0, 16) +
      theme_bw()
    

    enter image description here


    On the "difftime issue":

    Date 的值可以在内部表示为小数天(请参阅 ?Date?Ops.Date ;尝试: Sys.Date() ; Sys.Date() + 0.5 ; Sys.Date() + 0.5 + 0.5 ) . 但是,将 difftime 对象添加到 Date 时, difftime 对象会在最近的整天进行舍入(请参阅 ?Ops.Date 中的 x 参数) .

    让我们使用您的开始日期 2015-04-15 和结束日期 2015-04-30 来检查计算:

    mid <- (as.Date("2015-04-30") - as.Date("2015-04-15")) / 2
    mid
    # Time difference of 7.5 days
    
    str(mid)
    # Class 'difftime'  atomic [1:1] 7.5
    # ..- attr(*, "units")= chr "days"
    
    # calculate the midpoint using the difftime object
    as.Date("2015-04-15") + mid
    # [1] "2015-04-23"
    
    # calculating midpoint using numeric object yields another date...
    as.Date("2015-04-15") + as.numeric(mid)
    # [1] "2015-04-22"
    
    # But is "2015-04-15" above in fact fractional, i.e. "2015-04-22 point 5"?
    # Let's try and add 0.5
    as.Date("2015-04-15") + as.numeric(mid) + 0.5
    # [1] "2015-04-23"
    # Yes.
    

    因此,我们使用 numeric 期间,而不是 difftime 期间 .

相关问题