【问题标题】:Understanding dates and plotting a histogram with ggplot2 in R了解日期并在 R 中使用 ggplot2 绘制直方图
【发布时间】:2012-06-01 23:56:08
【问题描述】:

主要问题

在尝试使用 ggplot2 制作直方图时,我无法理解为什么日期、标签和中断的处理不像我在 R 中所期望的那样工作。

我正在寻找:

  • 我的约会频率的直方图
  • 在匹配栏下方居中的刻度线
  • %Y-b 格式的日期标签
  • 适当的限制;最小化网格空间边缘和最外层条之间的空白空间

我已经uploaded my data to pastebin 让这个可以重现。我创建了几列,因为我不确定最好的方法:

> dates <- read.csv("http://pastebin.com/raw.php?i=sDzXKFxJ", sep=",", header=T)
> head(dates)
       YM       Date Year Month
1 2008-Apr 2008-04-01 2008     4
2 2009-Apr 2009-04-01 2009     4
3 2009-Apr 2009-04-01 2009     4
4 2009-Apr 2009-04-01 2009     4
5 2009-Apr 2009-04-01 2009     4
6 2009-Apr 2009-04-01 2009     4

这是我尝试过的:

library(ggplot2)
library(scales)
dates$converted <- as.Date(dates$Date, format="%Y-%m-%d")

ggplot(dates, aes(x=converted)) + geom_histogram()
+      opts(axis.text.x = theme_text(angle=90))

产生this graph。不过,我想要%Y-%b 格式,所以我四处寻找并尝试了以下基于this SO

ggplot(dates, aes(x=converted)) + geom_histogram()
+    scale_x_date(labels=date_format("%Y-%b"),
+    breaks = "1 month")
+    opts(axis.text.x = theme_text(angle=90))

stat_bin: binwidth defaulted to range/30. Use 'binwidth = x' to adjust this.

这给了我this graph

  • 正确的 x 轴标签格式
  • 频率分布已改变形状(binwidth 问题?)
  • 刻度线不会在条形下方居中显示
  • xlim 也发生了变化

我研究了scale_x_date 部分的ggplot2 documentation 中的示例,当我将它与相同的x 轴数据一起使用时,geom_line() 似乎正确地中断、标记和居中刻度。我不明白为什么直方图不同。


根据 edgester 和 gauden 的回答进行更新

我最初认为gauden的回答帮助我解决了我的问题,但现在仔细观察后感到困惑。注意代码后两个答案的结果图之间的差异。

假设两者:

library(ggplot2)
library(scales)
dates <- read.csv("http://pastebin.com/raw.php?i=sDzXKFxJ", sep=",", header=T)

根据@edgester 下面的回答,我能够做到以下几点:

freqs <- aggregate(dates$Date, by=list(dates$Date), FUN=length)
freqs$names <- as.Date(freqs$Group.1, format="%Y-%m-%d")

ggplot(freqs, aes(x=names, y=x)) + geom_bar(stat="identity") +
       scale_x_date(breaks="1 month", labels=date_format("%Y-%b"),
                    limits=c(as.Date("2008-04-30"),as.Date("2012-04-01"))) +
       ylab("Frequency") + xlab("Year and Month") +
       theme_bw() + opts(axis.text.x = theme_text(angle=90))

这是我根据高登回答的尝试:

dates$Date <- as.Date(dates$Date)
ggplot(dates, aes(x=Date)) + geom_histogram(binwidth=30, colour="white") +
       scale_x_date(labels = date_format("%Y-%b"),
                    breaks = seq(min(dates$Date)-5, max(dates$Date)+5, 30),
                    limits = c(as.Date("2008-05-01"), as.Date("2012-04-01"))) +
       ylab("Frequency") + xlab("Year and Month") +
       theme_bw() + opts(axis.text.x = theme_text(angle=90))

基于 edgester 方法的绘图:

基于高登方法的情节:

注意以下几点:

  • gauden 2009 年 12 月和 2010 年 3 月的情节中的差距; table(dates$Date) 显示数据中有 19 个 2009-12-01 实例和 26 个 2010-03-01 实例
  • edgester 的情节从 2008 年 4 月开始,到 2012 年 5 月结束。根据 2008 年 4 月 1 日数据中的最小值和 2012 年 5 月 1 日的最大值,这是正确的。由于某种原因,高登的情节从 2008 年 3 月开始,但不知何故仍设法在 2012 年 5 月结束。在计算了 bin 并阅读了月份标签之后,对于我的生活,我无法弄清楚哪个情节有额外的或缺少直方图的 bin!

对这里的差异有什么想法吗? edgester 的创建单独计数的方法


相关参考文献

顺便说一句,这里还有其他位置,其中包含有关日期和 ggplot2 的信息,供路人寻求帮助:

  • Started here 在 learnr.wordpress,一个流行的 R 博客。它说我需要将我的数据转换为 POSIXct 格式,我现在认为这是错误的并且浪费了我的时间。
  • Another learnr post 在 ggplot2 中重新创建了一个时间序列,但并不真正适用于我的情况。
  • r-bloggers has a post on this,但它似乎已经过时了。简单的 format= 选项对我不起作用。
  • This SO question 正在玩弄休息和标签。我尝试将我的Date 向量视为连续的,但认为它效果不佳。看起来它一遍又一遍地覆盖相同的标签文本,所以这些字母看起来有点奇怪。分布是正确的,但有一些奇怪的休息。根据接受的答案,我的尝试是这样的 (result here)。

【问题讨论】:

  • 查看lubridate 包。
  • @gsk3 我听说过它,但我的理解是它有助于格式化、间隔、递增等。你认为我的问题在于 lubridate 可以帮助解决的问题吗?我认为这是正确使用 ggplot2 的语法。
  • 我不明白你的问题。您是否尝试过提出问题,然后在同一篇文章中回答?如果是这样,请将您的问题重新表述为问题,然后自己回答。 (在 SO 上积极鼓励这样做。)
  • 请提出一个新问题,因为您刚刚切换了原始数据集。这个问题读起来非常混乱。请接受一个答案并为任何有帮助的答案投票。
  • @edgester:我可能会重写这个问题。很难保持简洁。问题是 ggplot2 与日期/时间混淆。我想说明有多少种理论方法可以尝试使这项工作发挥作用,以及每种方法存在的问题。

标签: r datetime ggplot2 histogram


【解决方案1】:

我认为关键是您需要在 ggplot 之外进行频率计算。将 aggregate() 与 geom_bar(stat="identity") 一起使用以获得没有重新排序因子的直方图。下面是一些示例代码:

require(ggplot2)

# scales goes with ggplot and adds the needed scale* functions
require(scales)

# need the month() function for the extra plot
require(lubridate)

# original data
#df<-read.csv("http://pastebin.com/download.php?i=sDzXKFxJ", header=TRUE)

# simulated data
years=sample(seq(2008,2012),681,replace=TRUE,prob=c(0.0176211453744493,0.302496328928047,0.323054331864905,0.237885462555066,0.118942731277533))
months=sample(seq(1,12),681,replace=TRUE)
my.dates=as.Date(paste(years,months,01,sep="-"))
df=data.frame(YM=strftime(my.dates, format="%Y-%b"),Date=my.dates,Year=years,Month=months)
# end simulated data creation

# sort the list just to make it pretty. It makes no difference in the final results
df=df[do.call(order, df[c("Date")]), ]

# add a dummy column for clarity in processing
df$Count=1

# compute the frequencies ourselves
freqs=aggregate(Count ~ Year + Month, data=df, FUN=length)

# rebuild the Date column so that ggplot works
freqs$Date=as.Date(paste(freqs$Year,freqs$Month,"01",sep="-"))

# I set the breaks for 2 months to reduce clutter
g<-ggplot(data=freqs,aes(x=Date,y=Count))+ geom_bar(stat="identity") + scale_x_date(labels=date_format("%Y-%b"),breaks="2 months") + theme_bw() + opts(axis.text.x = theme_text(angle=90))
print(g)

# don't overwrite the previous graph
dev.new()

# just for grins, here is a faceted view by year
# Add the Month.name factor to have things work. month() keeps the factor levels in order
freqs$Month.name=month(freqs$Date,label=TRUE, abbr=TRUE)
g2<-ggplot(data=freqs,aes(x=Month.name,y=Count))+ geom_bar(stat="identity") + facet_grid(Year~.) + theme_bw()
print(g2)

【讨论】:

  • 刚看到这个。我计划完成它......但似乎只使用我已经提供的数据会容易得多。你没有这样做是有原因的吗?它同时具有 %Y-%b%Y-%m-%d 一组值,应该可以使用它们?
  • 请参阅我的问题中的更新部分。我能够应用你对聚合的使用来做我想做的事情。看一看;我认为您不需要 df$Count 向量或您为获得可用结果所做的其他一些事情。现在我只想知道如何根据日期范围设置限制。我也不需要lubridate
  • 我为后代提供了虚拟数据。当 pastebin 条目消失时,StackOverflow 问题可能仍然存在。在这种情况下,我的回答仍然会照原样工作。你是对的,lubridate 只需要第二个图,而不是第一个。
  • 您没有更改 pastebin 中的数据,但您确实在 R 代码中添加了更改它。您添加了原始问题中没有的“价格”变量。你已经改变了足够多的问题,最好开始一个新问题。现在很难理解整个问题。
  • 啊。是的,我做到了。但请注意为什么我这样做了。我清楚地引用了 ggplot2 文档,其中包含确切的变量使用。我只是想生成另一个变量,这样我就可以绘制直方图以外的东西。然后我展示了使用 geom_line() 会产生一个 x 轴并根据需要进行缩放,而直方图则不会。我肯定会考虑拆分这个——我应该专门问一个关于线图和直方图之间的 scale_x_date 处理吗?
【解决方案2】:

更新

版本 2:使用日期类

我更新了示例以演示对齐标签和设置绘图限制。我还证明了as.Date 在一致使用时确实有效(实际上它可能比我之前的示例更适合您的数据)。

目标情节 v2

代码 v2

这里是(有点过分的)注释代码:

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

dates <- read.csv("http://pastebin.com/raw.php?i=sDzXKFxJ", sep=",", header=T)
dates$Date <- as.Date(dates$Date)

# convert the Date to its numeric equivalent
# Note that Dates are stored as number of days internally,
# hence it is easy to convert back and forth mentally
dates$num <- as.numeric(dates$Date)

bin <- 60 # used for aggregating the data and aligning the labels

p <- ggplot(dates, aes(num, ..count..))
p <- p + geom_histogram(binwidth = bin, colour="white")

# The numeric data is treated as a date,
# breaks are set to an interval equal to the binwidth,
# and a set of labels is generated and adjusted in order to align with bars
p <- p + scale_x_date(breaks = seq(min(dates$num)-20, # change -20 term to taste
                                   max(dates$num), 
                                   bin),
                      labels = date_format("%Y-%b"),
                      limits = c(as.Date("2009-01-01"), 
                                 as.Date("2011-12-01")))

# from here, format at ease
p <- p + theme_bw() + xlab(NULL) + opts(axis.text.x  = theme_text(angle=45,
                                                                  hjust = 1,
                                                                  vjust = 1))
p

版本 1:使用 POSIXct

我尝试了一个解决方案,该解决方案在 ggplot2 中执行所有操作,不使用聚合进行绘制,并在 2009 年初和 2011 年底之间设置 x 轴上的限制。

目标情节 v1

代码 v1

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

dates <- read.csv("http://pastebin.com/raw.php?i=sDzXKFxJ", sep=",", header=T)
dates$Date <- as.POSIXct(dates$Date)

p <- ggplot(dates, aes(Date, ..count..)) + 
    geom_histogram() +
    theme_bw() + xlab(NULL) +
    scale_x_datetime(breaks = date_breaks("3 months"),
                     labels = date_format("%Y-%b"),
                     limits = c(as.POSIXct("2009-01-01"), 
                                as.POSIXct("2011-12-01")) )

p

当然,它可以与轴上的标签选项一起玩,但这是为了在绘图包中使用一个干净的简短例程来完成绘图。

【讨论】:

  • 谢谢。一些问题。 1)即使在阅读了文档之后,我也不明白日期和日期时间之间的区别。 2) 为什么 as.POSIXct 向量可以工作,但 as.Date 不能? 3) 同样,为什么不使用c(as.Date(), as.Date()) 设置限制但 as.POSIXct 可以?谢谢!
  • 我一直在玩这个,它似乎也受到标签/中断与条不对齐的影响。所有条目都只是几个月,所以本质上这是离散的。当我使用任何形式的scale_x_date(或日期时间)时,我会收到一个缺失的 binwidth 注释,并且我的刻度/标签与条不对齐。怎么可能做到这一点?
  • @Hendy 我已经用一个新示例更新了绘图,使用日期格式并利用日期在内部存储为自 1970 年 1 月 1 日以来的天数。天数适合您的数据结构并允许 (a) 绘图中的轻松转换 (b) 轴上标签的完美对齐 (c) 直观的来回转换,用于分箱、设置轴限制和标签。我希望这会有所帮助。
  • 是的,我认为关键是 1) 具有 binwidth = 中断和 2) 基于 min(dates$num) 在中断上所做的移位/偏移。仍然不确定为什么这是必要的,但它确实有效。顺便说一句,我会用我的解决方案更新我的问题,但是 dates$num 的东西和 ..count.. 不是必需的。即便如此,您的回答是理解这一点的关键。谢谢!
  • 很高兴它有帮助! :) 当然你不想给它打勾,在那种情况下? ;)
【解决方案3】:

标题“基于高登方法的绘图”下的错误图是由 binwidth 参数引起的: ... + Geom_histogram (binwidth = 30, color = "white") + ... 如果我们把 30 的值改成小于 20 的值,比如 10,就会得到所有的频率。

在统计学中,数值比显示更重要,平淡的图形对非常漂亮但有错误的图片更重要。

【讨论】:

  • 我不知道如何解释你的统计课......我的情节在某些方面不准确吗?我对月度数据感兴趣,因此月度 binwidths 非常有意义。为什么要降到10?问题实际上是关于 为什么 ggplot2 正在做它正在做的事情,而不是关于如何充分减少 binwidth 以免看到它。我们这些试图创建按月分类的情节的人似乎有些困惑,我认为这无助于解决这个问题。
  • 另外,您是否使用geom_histogram(binwidth = 10) 运行代码??仅凭这种变化的结果肯定是不正确的。最好上传一个代码块,这样我就可以理解你的意思了。
【解决方案4】:

我知道这是一个老问题,但对于任何在 2021 年(或之后)提出这个问题的人来说,使用 geom_histogram()breaks= 参数并创建一个小快捷函数来完成所需的操作会更容易顺序。

dates <- read.csv("http://pastebin.com/raw.php?i=sDzXKFxJ", sep=",", header=T)

dates$Date <- lubridate::ymd(dates$Date)

by_month <- function(x,n=1){
  seq(min(x,na.rm=T),max(x,na.rm=T),by=paste0(n," months"))
}

ggplot(dates,aes(Date)) +
  geom_histogram(breaks = by_month(dates$Date)) +
  scale_x_date(labels = scales::date_format("%Y-%b"),
               breaks = by_month(dates$Date,2)) + 
  theme(axis.text.x = element_text(angle=90))

【讨论】:

  • 只是想知道:是否有可能从两个 dates$Date 引用中消除“dates$”部分?我试过但失败了。
  • @wint3rschlaefer,你可以用with()包围整个东西,比如with(dates,...),你用上面的ggplot命令替换...,然后删除dates$
猜你喜欢
  • 2015-11-23
  • 1970-01-01
  • 1970-01-01
  • 2020-04-27
  • 2018-04-03
  • 1970-01-01
  • 1970-01-01
  • 2022-08-13
  • 2021-05-07
相关资源
最近更新 更多