简介
本文列举了ggplot2中大量几何对象和统计变换的一部分,并把它们按照用途进行了细分。
图层叠加的总体策略
总体来说,图层有三种用途:
- 用以展示数据本身。这一层不必多说,几乎在每一幅图形上都会出现。
- 用以展示数据的统计摘要。我们可以从数据摘要中进一步理解数据,同时对模型作出评价。本层通常绘制在数据层之上。
- 用以添加额外的元数据(metadata)、上下文信息和注解。元数据层展示了背景的上下文信息,也可以为原始数据赋予有现实意义的注解。
基本图形类型
以下几何对象是ggplot2图形的基本组成部分。每种几何对象自身即可独立构建图形,同时也可以组合起来构建更复杂的几何对象。
- geom_area()用于绘制面积图(area plot),即在普通线图的基础上,依y轴方向填充了下方面积的图形。对于分组数据,各组将按照依次堆积的方式绘制。
- geom_bar(stat = “identity”)绘制条形图。需要指定stat = “identity”,因为默认的统计变换将自动对“值”进行计数。而统计变换identity将保持数据不变。
- geom_line()绘制线条图。
- geom_point()绘制散点图。
- geom_path()绘制路径图。
- geom_polygon()绘制多边形,即填充后的路径。
- geom_text()可在指定点处添加标签。它是这些几何对象中唯一一个需要额外图形属性的:它需要指定label参数。
- geom_tile()用来绘制色深图(image plot)或水平图(level plot)。所有的瓦片(tile)构成了对平面的一个规则切分,且往往将fill图形属性映射至另一个变量。
代码举例:
df <- data.frame(x = c(3, 1, 5), y = c(2, 4, 6), label = c("a", "b", "c"))
p <- ggplot(df, aes(x, y)) + xlab(NULL) + ylab(NULL)
p + geom_point() + labs(title = "geom_point") // 散点图
p + geom_bar(stat = "identity") + labs(title = "geom_bar(stat=\"identity\")") // 条形图
p + geom_line() + labs(title = "geom_line") // 线条图
p + geom_area() + labs(title = "geom_area") // 面积图
p + geom_path() + labs(title = "geom_path") // 路径图
p + geom_text(aes(label = label)) + labs(title = "geom_text") //含标签的散点图
p + geom_tile() + labs(title = "geom_tile") // 色深图or水平图
p + geom_polygon() + labs(title = "geom_polygon") // 多边形图
展示数据分布
有一些几何对象可以用于展示数据的分布,具体使用哪种取决于分布的维度、分布是连续型或是离散型,以及我们感兴趣的是条件分布还是联合分布。
有多种方式可以用来进行分布的跨组比较:
- 同时绘制多个小的直方图,facets = . ~ var;
- 使用条件密度图,position = “fill”;
- 使用频率多边形(frequency polygon),geom = “freqpoly”。
depth_dist <- ggplot(diamonds, aes(depth)) + xlim(58, 68)
depth_dist + geom_histogram(aes(y = ..density..), binwidth = 0.1) + facet_grid(cut ~ .)
depth_dist + geom_histogram(aes(fill = cut), binwidth = 0.1, position = "fill")
depth_dist + geom_freqpoly(aes(y = ..density.., colour = cut), binwidth = 0.1)
和分布相关的许多几何对象都是以几何对象(geom)/统计变换(stat)的形式成对出现的。这些几何对象中大多数的本质都是别名(alias):一个基本几何对象结合一个统计变换,即可绘制出想要的图形。
- geom_boxplot = stat_boxplot + geom_boxplot: 箱线图,即一个连续型变量针对一个类别型变量取条件所得的图形。当类别型变量有许多独立的取值时,这种图形比较有用。箱线图也可对连续型变量取条件,前提是数据预先经过巧妙地封箱(binning)处理。下面给出例子:
library(plyr)
qplot(cut, depth, data = diamonds, geom = "boxplot")
qplot(carat, depth, data = diamonds, geom = "boxplot", group = round_any(carat, 0.1, floor), xlim = c(0, 3))
- geom_jitter = position_jitter + geom_point: 通过在离散型分布上添加随机噪声以避免遮盖绘制问题,这是一种较为粗糙的方法。下面的例子:
qplot(class, cty, data = mpg, geom = "jitter")
qplot(calss, drv, data = mpg, geom = "jitter")
- geom_density = stat_density + geom_area: 基于核平滑方法进行平滑后得到的频率多边形。给出下面的例子:
qplot(depth, data = diamonds, geom = "density", xlim = c(54, 70))
qplot(depth, data = diamonds, geom = "density", xlim = c(54, 70), fill = cut, alpha = I(0.2))
处理遮盖绘制问题
散点图是研究两个连续型变量间关系的重要工具。但是当数据量很大时,这些点经常会出现重叠现象,从而掩盖真实的关系。在极端情况下,甚至只能看到数据所在的大致范围,根据这种图形作出的任何结论都是值得怀疑的。这种问题被称为遮盖绘制(overplotting),对付它有许多方法:
- 小规模的遮盖绘制问题可以通过绘制更小的点加以缓解,或者使用中空的符号。下面给个例子,数据是从两个独立的正态分布中抽样所得的200个点:
df <- data.frame(x = rnorm(2000), y = rnorm(2000))
norm <- ggplot(df, aes(x, y))
norm + geom_point()
norm + geom_point(shape = 1)
norm + geom_point(shape = ".")
- 对于更大的数据集产生的更为严重的遮盖绘制问题,我们可以使用alpha让点呈现透明效果。下面给出代码:
norm + geom_point(colour = "black", alpha = 1/3)
norm + geom_point(colour = "black", alpha = 1/5)
norm + geom_point(colour = "black", alpha = 1/10)
- 如果数据存在一定的离散性,我们可以通过在点上增加随机扰动来减轻重叠。特别是在与透明度一起使用时,这种方法很有效。下面给出代码:
td <- ggplot(diamonds, aes(table, depth)) + xlim(50, 70) + ylim(50, 70)
td + geom_point()
td + geom_jitter()
jit <- position_jitter(width = 0.5)
td + geom_jitter(position = jit)
td + geom_jitter(position = jit, colour = "black", alpha = 1/10)
td + geom_jitter(position = jit, colour = "black", alpha = 1/50)
td + geom_jitter(position = jit, colour = "black", alpha = 1/200)
从上面可以得到启发,可以认为遮盖绘制问题是一种二维核密度估计问题,下面再给出两种方法:
- 将点分箱并统计每个箱中点的数量,然后通过某种方式可视化这个数量(直方图的二维推广)。
d <- ggplot(diamonds, aes(carat, price)) + xlim(1, 3) + theme(legend.position = "none")
d + stat_bin2d()
d + stat_bin2d(bins = 10)
d + stat_bin2d(binwidth = c(0.02, 200))
- 使用
stat_density2d
作二维密度估计,并将等高线添加到散点图中,或以着色瓦片(color3d tiles)直接展示密度,或使用大小与分布密度成比例的点进行展示。
d + geom_point() + geom_density2d()
d + stat_density2d(geom = "point", aes(size = ..density..), contour = F) + scale_size_area()
d + stat_density2d(geom = "tile", aes(fill = ..density..), contour = F)
last_plot() + scale_fill_gradient(limits = c(1e-5, 8e-4))
曲面图
ggplot2暂不支持真正的三维曲面图。但具有在二维平面上展现三维曲面的常见工具:等高线图,着色瓦片(colored tiles)以及气泡图。
绘制地图
ggplot2中的maps包只有美国本土等地图,要想绘制中国地图,参考《用R软件绘制中国分省市地图》一文。下面给出绘制美图地图的例子:
library(maps)
data(us.cities)
big_cities <- subset(us.cities, pop > 500000) //美国(2006年1月)五十万人口以上的城市
qplot(long, lat, data = big_cities) + borders("state", size = 0.5)
library(maps)
states <- map_data("state")
arrests <- USArrests
names(arrests) <- tolower(names(arrests))
arrests$region <- tolower(rownames(USArrests))
choro <- merge(states, arrests, by = "region")
choro <- choro[order(choro$order), ]
qplot(long, lat, data = choro, group = group, fill = assault, geom = "polygon") //美国各州人身伤害案件的数量
qplot(long, lat, data = choro, group = group, fill = assault / murder, geom = "polygon") //人身伤害和谋杀类案件的比率
揭示不确定性
不论是从模型所得或是对分布的假设而得,如果已经知道了一些关于数据中不确定性的信息,那么对这些信息加以展示通常是很重要的。ggplot2中有四类几何对象用于这项工作,它们均假设我们对给定x时y的条件分布感兴趣,并且都使用了图形属性ymin和ymax来确定y的值域。请看下表:
变量X类型 | 仅展示区间 | 同时展示区间和中间值 |
---|---|---|
连续型 | geom_ribbon | geom_smooth(stat = “identity” |
离散型 | geom_errorbar | geom_crossbar |
geom_linerange | geom_pointrange |
下面的例子拟合了一个双因素含交互效应回归模型,并且展示了如何提取边际效应(marginal effects)和条件效应(conditional effects),以及如何将其可视化:
d <- subset(diamonds, carat < 2.5 & rbinom(nrow(diamonds), 1, 0.2) == 1)
d$lcarat <- log10(d$carat)
d$lprice <- log10(d$price)
# 剔除整体的线性趋势
detrend <- lm(lprice ~ lcarat, data = d)
d$lprice2 <- resid(detrend)
mod <- lm(lprice2 ~ lcarat * color, data = d)
library(effects)
effectdf <- function(...) {
suppressWarnings(as.data.frame(effect(...)))
}
color <- effectdf("color", mod)
both1 <- effectdf("lcarat:color", mod)
carat <- effectdf("lcarat", mod, default.levels = 50)
both2 <- effectdf("lcarat:color", mod, default.levels = 3)
# 进行数据变换以移除显而易见的效应
qplot(lcarat, lprice, data = d, colour = color) //对x轴和y轴的数据均取以10为底的对数以剔除非线性性
qplot(lcarat, lprice2, data = d, colour = color) //剔除了主要的线性趋势
fplot <- ggplot(mapping = aes(y = fit, ymin = lower, ymax = upper)) + ylim(range(both2$lower, both2$upper))
# 展示模型估计结果中变量color的不确定性
fplot %+% color + aes(x = color) + geom_point() + geom_errorbar() //color的边际效应
fplot %+% both2 + aes(x = color, colour = lcarat, group = interaction(color, lcarat)) + geom_errorbar() + geom_line(aes(group = lcarat)) + scale_colour_gradient() //针对变量carat的不同水平(level),变量color的条件效应
# 展示模型估计结果中变量carat的不确定性
fplot %+% carat + aes(x = lcarat) + geom_smooth(stat = "identity") //caret的边际效应
ends <- subset(both1, lcarat == max(lcarat))
fplot %+% both1 + aes(x = lcarat, colour = color) + geom_smooth(stat = "identity") + scale_colour_hue() + theme(legend.position = "none") + geom_text(aes(label = color, x = lcarat + 0.02), ends) //针对变量color的不同水平,变量caret的条件效应
统计摘要
对于每个x的取值,计算对应y值得统计摘要通常是很有用的。ggplot2是用stat_summary()
来完成。
使用stat_summary()
时,既可以为每个参数单独地指定摘要计算函数,也可以用一个统一的函数对它们进行组合。
添加图形注解
记住一点:这些注解仅仅是额外的数据而已。两种方式:逐个添加或批量添加。下面的例子中,分别用以上两种方式,向经济数据中加入了有关美国总统的信息。
(unemp <- qplot(date, unemploy, data = economics, geom = "line", xlab = "", ylab = "No. unemployed (1000s)"))
presidential <- presidential[-(1:3), ]
yrng <- range(economics$unemploy)
xrng <- range(economics$date)
unemp + geom_vline(aes(xintercept = as.numeric(start)), data = presidential)
library(scales)
unemp + geom_rect(aes(NULL, NULL, xmin = start, xmax = end, fill = party), ymin = yrng[1], ymax = yrng[2], data = presidential, alpha = 0.2) + scale_fill_manual(values = c("blue", "red"))
last_plot() + geom_text(aes(x = start, y = yrng[1], label = name), data = presidential, size = 3, hjust = 0, vjust = 0)
caption <- paste(strwrap("Unemployment rates in the US have varied a lot over the years", 40), collapse="\n")
unemp + geom_text(aes(x, y, label = caption), data = data.frame(x = xrng[2], y = yrng[2]), hjust = 1, vjust = 1, size = 4)
highest <- subset(economics, unemploy == max(unemploy))
unemp + geom_point(data = highest, size = 3, colour = "red", alpha = 0.5)
- geom_text 可添加文字叙述或为点添加标签。(使用取子集的方式)抽取部分观测添加标签可能会非常有用————往往希望标注出离群点或其他重要的点。
- geom_vline,geom_hline: 向图形添加垂直线或水平线。
- geom_abline: 向图形添加任意斜率和截距的直线。
- geom_rect 可强调图形中感兴趣的矩形区域。
- geom_line,geom_path 和 geom_segment 都可以添加直线。
含权数据
这里以2000年美国人口普查中,中西部各州的统计数据为例。此数据中主要包含的是比例型数据(例如白种人比例、贫困线以下人口比例、有大学学历的人口比例)和每个郡的基本信息(面积、人口总数、人口密度)。
首先,对于线和点这类简单的几何对象,我们可以根据点的数量调整图形属性size来改变点的大小。
qplot(percwhite, percbelowpoverty, data = midwest) //无权重
qplot(percwhite, percbelowpoverty, data = midwest, size = poptotal / 1e6) + scale_size_area("Population\n(millions)", breaks = c(0.5, 1, 2, 4)) //以人口数量为权重
qplot(percwhite, percbelowpoverty, data = midwest, size = area) + scale_size_area() //以面积为权重
lm_smooth <- geom_smooth(method = lm, size = 1)
qplot(percwhite, percbelowpoverty, data = midwest) + lm_smooth //未考虑权重的最优拟合曲线
qplot(percwhite, percbelowpoverty, data = midwest, weight = popdensity, size = popdensity) + lm_smooth //以人口数量作为权重的最优拟合曲线
qplot(percbelowpoverty, data = midwest, binwidth = 1) //不含权重信息的直方图
qplot(percbelowpoverty, data = midwest, weight = poptotal, binwidth = 1) + ylab("population") //含权重信息的直方图
参考文献
[1][ggplot2:数据分析与图形艺术]