日历中的夏天

看着有节,摸着无节,打一生活用品

不知不觉,夏日已慢慢临近。姑娘们飞扬的裙角,小贩叫卖的西瓜,蚊蝇嗡嗡的声音,以及翻过的一页日历,都提醒着你夏天快来了。夏季有着不同的定义:根据中国人的日历,我们所俗称的夏季从立夏开始,到立秋结束;但在气候学上,夏季是指连续五天平均温度超过22摄氏度即算作夏季的开始,若连续五天平均温度低于22度则算作入秋;而天文学上的夏季一般是指六、七、八这三个月。

那么哪一种夏季的定义更合适一些呢?还是用数据可视化来说话吧。这项任务基本上有两个步骤:一是获取某城市的2011年日平均温度数据,二是根据数据绘制日历热图(Calendar-Heatmap)

本文所采用的数据源是Wunderground提供的API。该API所提供的数据极为丰富,除了历史温度数据之外,还有湿度、风向等大量信息可供利用,所以它也被数据源手册所推荐。为了使用这个API我们先申请一个免费帐户,然后利用R语言中的RJSONIO包来提取每天的平均温度。使用这个API要注意的是,其免费帐户限制了每分钟10次调用,超出会中断连接。不知道别人怎么弄的,本人的笨办法是在程序中增加了暂停。这样获得了365个日平均温度。

日历热图是一种有趣的工具,它可以在日历表中显示时间序列数据的变化。在2009年的The Data Expo中,获奖团队就是利用SAS来生成日历热图。多才多艺的R语言当然也可以做到。在《R Graphs Cookbook》这本书中就提到了绘制日历热图的方法,第一种方法是Paul Bleicher所写的一个函数,它是基于grid,lattice,chron这三个扩展包来编写的。第二种方法是使用openair扩展包中的calendarPlot函数。生成的图形就象下面这个样子。
 看起来不错,但是我们还没完。我们希望挑选出平均温度在22度以上的日子,突出显示出来。所以我们采用第三种方法,用ggplot2包来绘制日历热图,图形显示如下(参考了MarginTale的这篇文章)。
 上图数日子是竖着来数的,横轴表示每月的第几周,纵轴表示星期几。灰色部分表示当天平均温度在22度以下,有色彩的区块表示在22度以上。颜色越偏黄则表示温度越高。在2011年,立夏的时间是5月6日,立秋是8月8日,但可以观察到立秋之后仍有很多日子的平均温度在22度以上。这就是我们所俗称的“秋老虎”。如果按照气候学的定义,四月末就有五天以上连续的高温天气,照这样看夏天应该在四月末就开始了,一直延续到十月初结束。而天文学上的夏季则是六、七、八三个月,看到这三个月基本上全是22度以上,而且高温天气集中在七八两个月,这也正是学校放暑假的时间段。这样看来,似乎天文学的夏季定义是比较符合我们人体的感觉的。其它的要么偏短,要么偏长。

写到这里,想起了梁静茹的一首歌:

宁静的夏天
天空中繁星点点
心里头有些思念
思念着你的脸
我可以假装看不见
也可以偷偷的想念
直到让我摸到你那温暖的脸

(最后要说的是,本人并非气象专家,本文也没有考虑到湿度对体感温度的影响,或是其它因素。主要还是向各位介绍R语言中日历热图的绘制以及数据的获取。)
代码如下:

# 加载所需扩展包
library(RCurl)
library(RJSONIO)
require(quantmod)
library(ggplot2)

# 提取武汉市2011年一年的历史数据
date <- seq.Date(from=as.Date('2011-01-01'),
  to=as.Date('2011-12-31'), by='1 day')
date.range <- as.character(format(date,"%Y%m%d"))
n <- length(date.range)
temp <- humi <- rep(0,n)
for (i in 1:n) {
  # 你要用自己申请的API key来代替程序中的yourkey
  url <- 'http://api.wunderground.com/api/yourkey/'
  finalurl <- paste(url,'history_',date.range[i],
    '/q/wuhan.json',sep='')
  web <- getURL(finalurl)
  raw <-fromJSON(web)
  temp[i] <- raw$history$dailysummary[[1]]$meantempm
  humi[i] <- raw$history$dailysummary[[1]]$humidity
  # 在循环内增加一个7秒的暂停,避免连接断开。
  Sys.sleep(7)
}
# 将获得的数据整合为数据框,并将温度和湿度转为数值格式
dataset <- data.frame(temp,humi,date,stringsAsFactors=F)
dataset$temp <- as.numeric(dataset$temp)
dataset$humi <- as.numeric(dataset$humi)

# 用openair包绘制日历热图
install.packages('openair')
library(openair)
calendarPlot(dataset,pollutant='temp',year=2011)

# 用ggplot2包绘制日历热图
# 复制一个新的数据框
dat <- dataset
# 先取得月份,再转为因子格式
dat$month<-as.numeric(as.POSIXlt(dat$date)$mon+1)
dat$monthf<-factor(dat$month,levels=as.character(1:12),
  labels=c("Jan","Feb","Mar","Apr","May","Jun","Jul",
  "Aug","Sep","Oct","Nov","Dec"),ordered=TRUE)
# 得到每周的星期,也转为因子格式
dat$weekday = as.POSIXlt(dat$date)$wday
dat$weekdayf<-factor(dat$weekday,levels=rev(0:6),
  labels=rev(c("Sun","Mon","Tue","Wed","Thu","Fri","Sat")),ordered=TRUE)
# 先得到全年的周序号,然后得到每个月的周序号
dat$week <- as.numeric(format(dat$date,"%W"))
dat<-ddply(dat,.(monthf),transform,monthweek=1+week-min(week))
# 绘图
P <- ggplot(dat, aes(monthweek, weekdayf, fill = temp)) +
  geom_tile(colour='white') +
  facet_wrap(~monthf ,nrow=3) +
  scale_fill_gradient(space="Lab",limits=c(22, max(dat$value)),
    low="red", high="yellow") +
  opts(title = "武汉市2011年气温日历热图") +
  xlab("Week of Month") + ylab("")
P

日历中的夏天》有20个想法

  1. Airline Traffic 的原始论文和 d3.js 给出的实现更漂亮,关键在于月份和月份之间微妙的嵌套布局方式:

    http://mbostock.github.com/d3/ex/calendar.html

    相比之下,以上 calendarPlot() 和 ggplot2 的布局则略微不给力啊。前者是完全没想整合,后者则在某一侧出现了一次以上的凸起。

    另外,对这个图,时序越长效果越好。

    1. 赞d3。被这些基于JS的作图工具熏陶久了,觉得R的图形总是有一股土味儿,欠修饰:)

      另外,呼叫作者和编辑大人,链接中的单词最好用减号-分开,如calendar-heatmap。

  2. 我用chrome阅读,看到的文章在浏览器的标签页中显示的都是

    %post_author_nickname%: 日历中的夏天 | 统计之都 (中国统计学门户网站,免费统计学服务平台)
    这样的,%post_author_nickname%是否是哪里出错了?

  3. 楼主简单演示了日历热图的绘制过程,蛮有意思也备受启发,谢谢楼主的介绍。
    这两天研究了下,由于R的基础薄弱,有两个问题需要请教下:
    1、基于openair包calendarPlot函数绘制的日历图,对中文的支持似乎不太好,比如下面的星期X未完全显示,另外,如楼主第三种方法,calendarPlot函数不能突出显示某一阈值之上的格点,不知道这两个问题通过改变calendarPlot函数某些参数能否改善?
    2、基于ggplot绘制的图形似乎更灵活,但是背景的灰色方格与fill渲染的方格不重叠,而且方格中未能显示日期(1-31)(这个似乎可以再加一层解决),但是在布局上似乎不如calendarPlot函数绘制的更接近于日历形式,能否通过某些参数的改变使得外观和布局上能与calendarPlot函数绘制的日历图一致呢?

    望楼主指点一二,谢谢!

    1. 楼上这位仁兄研究好仔细啊,第一个关于calendarPlot函数研究不多。第二个改变布局应该可以的,在aes确定X轴和Y轴映射的时候,monthweek, weekdayf两个变量可以前后调换一下位置。背景的话可以将那些grid干掉,或是用其它的主题,看起来就清静很多。目前应该想到这么多吧。

      1. 多谢指教!
        我将monthweek, weekdayf置换过位置看过效果,这个能理解。
        R作图很强大,看来需要慢慢修练呢……
        楼主好热心,赞一个!

  4. 还有一个问题想了好久,望楼主再指点下:
    在基于某个包的函数进行绘图时,如果想对图形做些更改或添加元素可以怎么实现呢?比如以openair里的calendarPlot函数为例,想在图形中加一个文本内容,可以怎么做呢?在calendarPlot()参数里加入了mtext()内容,但是没有效果,不知道是不可行还是未加对位置? 另外,图形输出时的margin问题,似乎也不能改变?
    (只是就这个例子琢磨下怎么利用好一个包,没有吹毛求疵的意思)

  5. 作者使用武汉的平均气温做分析当然会得到夏天时间比较长的结论, 如果采用全国的平均气温做分析, 气象学上的夏天区间和日历上的夏天区间可能差别不多.

  6. 这里还有一个严重的问题,前面算一周中第几天的时候认为周日是第一天,后面算是某天是第几周的时候由把周日当作是最后一天归到前一周中去了。

  7. 建立数据集时,日期时间变量一定要使用date,这个名称是固定的。也就是说,如果数据集中没哟这个变量名,绘图会失败。!!!!!!

gaotao进行回复 取消回复

邮箱地址不会被公开。 必填项已用*标注