Lecture06 : R数据作图
2024-09-20
R语言的前身是S语言, S语言的设计目的就是交互式数据分析、绘图。 所以绘图是R的重要功能1。
R有最初的基本绘图, 这是从S语言继承过来的, 还有一些功能更易用、更强大的绘图系统, 如lattice、ggplot2。
条形图是一种常用的数据可视化工具,适用于以下场景
内置函数barplot()
,此函数较为灵活:
基本用法barplot(height, ...)
,height 是一个数值向量,表示每个条形的高度。… 表示可以传递给函数的其他参数,用于定制条形图的外观和行为。
通常用于直接从数据框中提取数据并绘制条形图。允许你使用一个公式来指定分组变量和响应变量,这样可以更灵活地处理数据。
barplot(formula, data, subset, na.action, ...)
。其中的formula是一个公式,指定了因变量和自变量之间的关系,例如 y ~ x。data即数据框。1
以R内置的Longley
数据集为例,这个数据集包含了从1947年到1962年的宏观经济数据,共有16个观测值和7个变量。
GNP.deflator GNP Unemployed Armed.Forces Population Year Employed
1947 83.0 234.289 235.6 159.0 107.608 1947 60.323
1948 88.5 259.426 232.5 145.6 108.632 1948 61.122
1949 88.2 258.054 368.2 161.6 109.773 1949 60.171
1950 89.5 284.599 335.1 165.0 110.929 1950 61.187
1951 96.2 328.975 209.9 309.9 112.075 1951 63.221
1952 98.1 346.999 193.2 359.4 113.270 1952 63.639
直方图用于估计数据的概率分布。它通过将数据分组到有限数量的“箱子”中,并将这些箱子中的数据点数量可视化为条形图来实现。直方图通常用于连续数据。
使用hist()
函数绘制直方图,基本形式:
# 生成一组正态分布的随机数
data <- rnorm(100)
# 绘制直方图,自动选择箱子数量
hist(data, main = "Histogram of Normal Distribution", xlab = "Values", ylab = "Frequency")
# 显示概率密度而不是频数
hist(data, freq = FALSE, density = 25, col = "lightgreen", main = "Histogram with Density")
# 添加核密度估计曲线
hist(data, freq = FALSE, col = "lightblue", main = "Histogram with Density and KDE")
lines(density(data), col = "red", lwd = 2)
展示一组数据的分布情况的统计图表。箱线图可以显示数据的最小值、第一四分位数(Q1)、中位数(Q2)、第三四分位数(Q3)和最大值,以及可能的异常值。
使用boxplot()
函数绘制箱线图,基本用法如下:
# 生成一组随机数据
set.seed(123)
data1 <- rnorm(50)
data2 <- rnorm(50, mean = 1, sd = 2)
# 使用数据向量创建箱线图
boxplot(data1, main = "Boxplot of Normal Distribution", xlab = "Data", ylab = "Values")
# 使用数据矩阵创建箱线图
data_matrix <- matrix(c(data1, data2), ncol = 2)
boxplot(data_matrix, main = "Boxplot of Two Groups", xlab = "Group", ylab = "Values", names = c("Group 1", "Group 2"))
# 水平箱线图
boxplot(data1, horizontal = TRUE, main = "Horizontal Boxplot", xlab = "Values", ylab = "Data")
也可以采用方程式Y ~ X
的形式,针对数据框绘制箱线图
散点图(Scatter Plot)是一种常用的数据可视化方法,用于展示两个变量之间的关系。它通过在二维平面上绘制点来表示数据,每个点的横坐标代表一个变量的值,纵坐标代表另一个变量的值。散点图非常适合于观察变量之间的相关性,比如正相关、负相关或无相关。
使用场景:
plot()
函数绘制散点图pairs()
函数绘制散点图矩阵plot()函数是一个通用的绘图函数,用于创建二维图表。这个函数非常灵活,支持多种参数来自定义图表的外观
Q-Q图是一种统计图表,用于比较两个概率分布,特别是用于检验数据集是否服从某一理论分布,如正态分布、指数分布等。它通过将两个分布的分位数进行比较,从而评估它们的相似性。在Q-Q图中,如果数据确实服从某一指定分布,则图上的点将大致分布在一条直线上。
适用范围:
curve()函数接受一个函数, 或者一个以x为变量的表达式, 以及曲线的自变量的左、右端点, 绘制函数或者表达式的曲线图
plot()
函数绘制线图在plot函数中使用 type=’l’参数可以作曲线图
除了仍可以用main, xlab, ylab, col等参数外, 还可以用lwd指定线宽度, lty指定虚线,
饼图(Pie Chart)是一种圆形的统计图表,将圆形分割成扇形,每个扇形的角度和面积表示数据的比例。饼图通过将数据分组并显示每个组的比例,可以直观地展示数据的组成结构。
使用场景:
饼图不适用于以下情况:
核密度图是一种用于估计数据分布的图形工具,它通过平滑数据点生成一个连续的概率密度函数,从而显示数据的分布情况。核密度图比直方图更为光滑,因为它不会依赖于具体的分箱选择。核密度图能够很好地显示数据的分布特征,如数据的多峰性和尾部行为。
可以使用plot函数绘制核密度图
在R语言中,图形辅助函数是指那些用于增强和改进图形外观的函数。这些函数可以帮助你添加图形元素,如标题、轴标签、图例、文本注释等,以及调整图形的布局和外观。较为常用的有:
用abline函数在图中增加直线。 可以指定截距a和斜率b, 或为竖线指定横坐标(用参数v), 为水平线指定纵坐标(用参数h)
示例:
用points函数增加散点:points(x, y = NULL, type = "p", ...)
用lines函数增加曲线lines(x, y = NULL, type = "l", ...)
用legend函数增加标注legend(x, y = NULL, legend, fill = NULL, col = par("col"),...)
在plot()函数中用 axes=FALSE可以取消自动的坐标轴。 用box()函数画坐标边框。 用axis函数单独绘制坐标轴。 axis的第一个参数取1,2,3,4, 分别表示横轴、纵轴、上方和右方。 axis的参数at为刻度线位置,labels为标签。
text()在坐标区域内添加文字。 mtext()在边空处添加文字
par()函数是R语言中一个非常重要的函数,它用于设置或查询图形参数。这些参数控制着图形的各个方面,包括图形窗口的大小、布局、字体、颜色、线型等。通过par()函数,你可以自定义图形的外观,以满足特定的可视化需求。
par()最常用的使用场景是在一个图表上显示多个图形。
par函数指定图形参数并返回原来的参数值, 所以在修改参数值作图后通常应该恢复原始参数值。
# 设置图形参数,创建一个2×3的图形布局,并拿到修改前的原始参数
opar <- par(mfrow = c(2,3))
# 绘制mpg与其他参数的散点图
with(mtcars,{
plot(mpg ~ cyl, main = "mpg vs cyl", xlab = "Cylinders", ylab = "Miles per Gallon")
plot(mpg ~ disp, main = "mpg vs disp", xlab = "Displacement", ylab = "Miles per Gallon")
plot(mpg ~ hp, main = "mpg vs hp", xlab = "Horsepower", ylab = "Miles per Gallon")
plot(mpg ~ drat, main = "mpg vs drat", xlab = "Rear Axle Ratio", ylab = "Miles per Gallon")
plot(mpg ~ wt, main = "mpg vs wt", xlab = "Weight", ylab = "Miles per Gallon")
})
# 绘制mpg的箱线图
boxplot(mtcars$mpg, main = "Boxplot of mpg", ylab = "Miles per Gallon")
只要启用了绘图函数会自动选用当前绘图设备, 缺省为屏幕窗口。
png(file='fig-mpg-hp.png', height=600, width=1000)
with(mtcars, plot(mpg, hp, main='汽车油耗与马力关系'))
dev.off()
png
2
基本R的作图结果通常不够美观, 如果要将不同种类图形组合在一起比较困难, 对设计新的图形类型支持也不够好。
Hadley Wickem的ggplot2包是R的一个作图用的扩展包, 它实现了“图形的语法”, 将一个作图任务分解为若干个子任务, 只要完成各个子任务就可以完成作图。如果需要进一步控制图形细节, 只要继续调用其它函数, 就可以控制变量值的表现方式(scale)、图例、配色等。
与基本R中的作图系统相比, ggplot2的作图有规律可循, 作图结果直接达到出版印刷质量, 除了可以按照一些既定模式做出常见种类的图形, 也很容易将不同图形种类组合在一起, 或者设计新颖的图形。
ggplot在绘图上有着“分层”的概念,统一用于所有图形,保证了相同的语法:
运行本章示例之前请确保加载了以下包
我们使用来自gapminder扩展包的gapminder数据集来体验ggplot作图。这个数据集有若干个国家不同年份的一些数据。包括所属洲、期望寿命、人口数、人均GDP。 有1704个观测和6个变量。
首先确保加载数据集:
# A tibble: 6 × 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 Afghanistan Asia 1952 28.8 8425333 779.
2 Afghanistan Asia 1957 30.3 9240934 821.
3 Afghanistan Asia 1962 32.0 10267083 853.
4 Afghanistan Asia 1967 34.0 11537966 836.
5 Afghanistan Asia 1972 36.1 13079460 740.
6 Afghanistan Asia 1977 38.4 14880372 786.
我们将做期望寿命(lifeExp)对人均GDP(gdpPercap)的散点图,并逐步进行完善, 每个国家的每个年份作为一个点。散点图最重要的映射是x轴与y轴两个维度。
首先调用ggplot()函数, 指定数据集,将人均GDP映射到x轴,将期望寿命映射到y轴, 结果保存为一个R变量:
在如上指定了数据和映射后, 只要用geom_xxx()指定一个图形类型, 并与ggplot()的结果用加号连接就可以作图了
作图步骤之间用加号连接,这是ggplot包特有的语法。 例如, 用相同的映射做出散点图并叠加拟合曲线图1:
在以上的图形中, x轴变量(人均GDP)分布非正态,严重右偏, 使得大多数散点重叠地分布在直角坐标系的左下角。 将x轴用对数刻度可以改善, 函数为scale_x_log10():
x轴的标签文字不太友好,我们将其转换为美元符号1:
在ggplot()函数的mapping参数的aes()设定中将变量映射到x、y轴, 颜色、符号、线型等图形元素类型,也可以作为图形设置将某些图形元素设置为固定值。
例如, 用不同颜色表示不同大洲, 就是将continent变量映射到color:
将置信区间阴影的颜色也用不同大洲区分, 方法是在aes()中将color和fill都指定为变量continent。并且更换图形主题
在必要时, 可以在geom_xxx()函数中单独用mapping = aes(<…>)单独指定变量映射。 例如, 下面的程序在geom_point()中将不同大洲映射为不同颜色, 而不影响geom_smooth()中的颜色以及分组:
也可以将一个分类变量映射到不同绘图符号。 例如,将pop(人口数)映射到点的大小,使得图形又多了一个维度:
labs()规定了上方的标题、小标题, x轴、y轴的标题, 右下方的标注(caption)。
geom_text()
和geom_label()
进行标注asia <- gapminder |> filter(continent=="Asia")
china <- asia |> filter(country=="China")
ggplot(gapminder, aes(
x = gdpPercap,
y = lifeExp)) +
geom_point(alpha=0.5,mapping = aes(color = continent,size=scale(pop))) +
geom_smooth() +
scale_x_log10(labels=scales::dollar) +
geom_text(aes(label=asia$country),data=asia) +
geom_label(aes(label=paste(china$country,china$year,sep = "-")),data=china,color="red" )
ggrepel
解决重叠##ggrepel包需单独安装
if(!require(ggrepel)){
install.packages("ggrepel")
}
library(ggrepel)
ggplot(gapminder, aes(
x = gdpPercap,
y = lifeExp)) +
geom_point(alpha=0.5,mapping = aes(color = continent,size=scale(pop))) +
geom_smooth() +
scale_x_log10(labels=scales::dollar) +
geom_text_repel(aes(label=asia$country),data=asia) +
geom_text_repel(aes(label=paste(china$country,china$year,sep = "-")),data=china,color="red")
在R中,使用facet_wrap()
函数和facet_grid()
函数根据选择的分类变量对数据分组,对所有分组后的子集创建并排显示的图形,称为刻面
实际用于作图的数据(Data),其中包含了需要被展示出来的变量,每个变量一个维度。数据对象类型为tibble或数据框,每个变量一列。
几何对象(Geometric object),即图的类型,由geom_开头的几何函数定义。几何对象包括:散点图(geom_point)、折线图(geom_line)、柱状图(geom_bar)、直方图(geom_histogram)、盒形图(geom_boxplot)、密度曲线(geom_density)、……
具体使用的时候,需要知道每个几何对象的适用场景,比如geom_bar默认情况下x和y只需要给一个参数,默认对其进行计数统计(即stat=“count”)。
以下代码在绘制柱状图时同时传入x和y,导致运行时报错(想一想为什么?):
ggplot内置了众多geom_函数,每个都绘制独一无二的图形,比如geom_smooth
用于在图形中添加平滑曲线或拟合线,默认线性回归线:
视觉映射(Aesthetic mappings)的意思是:一个变量的值可以映射到不同的视觉属性,由几何函数中mapping参数定义。包括:横坐标(x)、纵坐标(y)、颜色(colour)、填充(fill)、透明度(alpha)、形状(shape)、大小(size)、边框宽度(stroke)、线条类型(linetype)、分组(group)、…… 。
观察以下代码的执行结果为何不同?为何出现这种不同?
把“blue”放到aes里,是把它作为一个变量进行了映射,但源数据中并不存在名为“blue”的变量,系统默认给映射到了红色,同时还会自动生成图例。
而把“blue”给了aes外边的color,是设置了点的颜色为“bule”。没有出现图例(没有映射),点的颜色被设置为蓝色。
mapping=aes()参数在两个层次进行变量映射,其一是在ggplot()定义的顶层(top level),其二是在各“geom_()”定义的底层,具体而言:
如果未在底层定义mapping,则它默认继承由顶层定义的值(可用?geox_xxx查看)。如果需要,可在各几何对象的底层,声明新需要的mapping。
以下两张图分别在全局和底层定义 aes映射,这种情况下两种写法完全一样
一般情况下我们会把x,y放在全局映射中,把其它图形美学相关的放在geom_xxx函数中的映射中:
这段代码中,我们想把am列映射给点图的形状(shape),但代码执行报错,为什么?
ggplot(data=mtcars,mapping=aes(x=hp,y=mpg)) +
geom_point(mapping=aes(color = cyl,size = wt,shape= am )) +
geom_line()
shape必然接受一个分类数据,而am是一个数值型向量,因此不能映射,必须转为字符型或者因子:
ggplot的图形由一层层叠加的layer组成(有点像photoshop的图层)。每个图层都由两个部分定义:
统计变换和几何图形紧密联系,同等重要。实际上,每个几何图形内部都有默认的统计变换,因此不用额外定义。
以下的代码等价
位置调整(Position adjustments),微调图形对象的位置。默认为原位 (position_identity)
以下三种样式常用于柱状图:
设置方式有:position=“stack”和position=position_stack()的方式,后者可以指定更多的参数。
以下三种样式多用于点图
p1 <- ggplot(diamonds, aes(cut,price,colour=color )) + geom_point()
p2 <- ggplot(diamonds, aes(cut,price,colour=color )) + geom_point(position = "jitter" )
# 可以使用position_jitter()函数方式进一步自定义,默认width=0.4(40%),height=0.4(40%)
p3 <- ggplot(diamonds, aes(cut,price,colour=color )) +
geom_point(position = position_jitter(width = 0.2, height = 0.2) )
p1+p2+p3
简化写法:
注意:jitter是一个较为细微的调整,通常用于分类变量上每组有大量数据的情况,用在连续变量上作用不明显
标尺(scale)控制数据到美学映射的转换细节。可以在其中覆盖默认的相关属性(如坐标轴缩放、标签、图例、颜色…),也可以完全生成新的美学映射。
标尺相关的函数众多,限于篇幅,这里仅讲解1个例子,我们使用mpg
数据集(R内置的另一个汽车相关数据集)
mpg_99 <- mpg |> filter(year == 1999)
mpg_08 <- mpg |> filter(year == 2008)
base_99 <- ggplot(mpg_99, aes(displ, hwy)) + geom_point()
base_08 <- ggplot(mpg_08, aes(displ, hwy)) + geom_point()
base_99 + base_08
比较两张图不难发现,他们的y轴尺度是不一致的,左边的图取值范围比右边大。在横向比较时会带来一些麻烦。为了解决这个问题,可以对右图y轴进行坐标轴缩放:
base_08 <- ggplot(mpg_08, aes(displ, hwy)) +
geom_point() +
scale_y_continuous(limits = c(10, 45))
base_99 + base_08
坐标缩放非常普遍,ggplot提供了lims()内置函数:
ggplot2中坐标系有两种类型。
coord_cartesian():默认的笛卡尔坐标系,其中元素的二维位置由 x 和 y 位置的组合给出。
coord_flip():x 轴和 y 轴翻转的笛卡尔坐标系。
coord_fixed():具有固定长宽比的笛卡尔坐标系。
coord_map()/ coord_quickmap()/ coord_sf():地图投影。
coord_polar():极坐标。
coord_trans():数据经过 stat 处理后,对 x 和 y 位置应用任意变换。
线性坐标系coord_cartesian()也有xlim
和ylim
参数。同样也是限制x和y轴。它和scale有什么区别呢?
Warning: Removed 153 rows containing missing values or values outside the scale range
(`geom_point()`).
可以看到scale方式抛出一个警告:有153个观测值被丢弃了。 两者关键的区别在于限制的工作方式:设置scale比例尺限制时,限制之外的任何数据都将被丢弃;但设置坐标系限制时,我们仍然使用所有数据,但我们只显示绘图的一小部分。设置坐标系限制就像在放大镜下查看绘图一样。
注意:虽然p2手动交换x和y和p3绘制的点一样。但是在调用geom_smooth方法后结果却不一样。
刻面(Facet) 根据某些变量将数据分成子集,在针对各个子集分别作图并排列在一个页面,包括:网格型(facet_grid)和包裹型(facet_wrap)。
使用vars()
设置刻面依据
也可使用方程设置
主题控制绘图中所有非数据元素的显示。可以使用完整主题覆盖所有设置,或者选择使用theme()和element_函数调整单个设置。使用theme_set()可修改活动主题,从而影响所有未来绘图。
使用?theme()
查看能够定义的主题参数
内置的完整主题:theme_grey() theme_gray() theme_bw() theme_linedraw() theme_light() theme_dark() theme_minimal() theme_classic() theme_void() theme_test()
# create data
set.seed(123)
var=rnorm(1000)
# Without theme
plot1 <- qplot(var , fill=I(rgb(0.1,0.2,0.4,0.6)) )
# With themes
plot2 = plot1+theme_bw()+annotate("text", x = -1.9, y = 75, label = "bw()" , col="orange" , size=4)
plot3 = plot1+theme_classic()+annotate("text", x = -1.9, y = 75, label = "classic()" , col="orange" , size=4)
plot4 = plot1+theme_gray()+annotate("text", x = -1.9, y = 75, label = "gray()" , col="orange" , size=4)
plot5 = plot1+theme_linedraw()+annotate("text", x = -1.9, y = 75, label = "linedraw()" , col="orange" , size=4)
plot6 = plot1+theme_light()+annotate("text", x = -1.9, y = 75, label = "light()" , col="orange" , size=4)
plot7 = plot1+theme_dark()+annotate("text", x = -1.9, y = 75, label = "dark()" , col="orange" , size=4)
plot8 = plot1+theme_get()+annotate("text", x = -1.9, y = 75, label = "get()" , col="orange" , size=4)
plot9 = plot1+theme_minimal()+annotate("text", x = -1.9, y = 75, label = "minimal()" , col="orange" , size=4)
不同于刻面facets(将数据分为多个子集进行展示),多图排版是将多个独立的图形按照一定规则分布在一张图上。ggplot本身并不具备这个功能。但有很多第三方包对此进行了支持。本章介绍一个比较流行的包patchwork
(前面的多图排版均由此包完成),它的使用非常简单:+
表示自动排列,|
表示左右排列,/
表示上下排列
图形 | 适用场景 | 代码示例 |
---|---|---|
条形图 | 展示分类变量的频率或比例 | geom_bar(mapping = aes(x = cut, fill = clarity)) |
直方图 | 展示连续变量的分布情况 | geom_histogram(binwidth=.5) |
箱线图 | 展示数据基本统计信息 | ggplot(data = mpg, aes(x = class, y = hwy)) + geom_boxplot() |
点图 | 展示连续变量的分布情况 | ggplot(iris) + geom_point(aes(x=Sepal.Length, y=Sepal.Width)) |
折线图 | 展示随时间变化的数据趋势或两个连续变量之间的关系 | ggplot(data = mtcars, aes(x = mpg, y = disp)) + geom_line(color = "red") |
密度图 | 展示连续变量的概率分布 | ggplot(dat, aes(x=rating)) + geom_density() |
小提琴图 | 展示分类变量下数值变量的分布 | ggplot(iris) + geom_violin(aes(x=Species, y=Petal.Length)) |
热力图 | 展示两个分类变量的交叉频次或比例 | geom_tile(color="gray",size=0.5) |
全面了解ggplot的最佳去处
Springer 出版的《ggplot2:用于数据分析的优雅图形》第三版的在线版本
R Graph Gallery 是一个专门展示用R语言图表集合的网站。包含精心组织的近50种、超过400个图表示例。对于每种图表类型,网站都提供了基础教程,介绍其核心结构和用途。
这本书以浅显而有趣的图形示例将统计学和可视化的历史娓娓道来,是一本难得的以图形为主线、探讨统计学和统计可视化的高质量通识读物。书中内容无不体现作者对现代统计和统计图形的深刻思考,例如“神奇数字”和“作图原则”等章节。这本书既可作为高校师生的统计计算或者统计软件中可视化章节的主要参考书,也可作为严谨学术作图的指导手册。
http://roypub.biovr.cc/data-visualization_zh.pdf
R数据作图