.4统计变换(statistics)统计变换是以某种方式对数据信息进行汇总,几何对象控制生成的图像类型。“一个几何对象包含了一种统计变换,一个统计变换包含了一个几何对象”这句话更准确地来说是“每一个几何对象都有一个默认的统计变换,并且每一个统计变换也都有一个默认的几何对象”。 查看可用统计变换: ls('package:ggplot2', pattern='^stat_.+')## [1] 'stat_bin' 'stat_bin_2d' 'stat_bin_hex' ## [4] 'stat_bin2d' 'stat_binhex' 'stat_boxplot' ## [7] 'stat_contour' 'stat_count' 'stat_density' ## [10] 'stat_density_2d' 'stat_density2d' 'stat_ecdf' ## [13] 'stat_ellipse' 'stat_function' 'stat_identity' ## [16] 'stat_qq' 'stat_quantile' 'stat_smooth' ## [19] 'stat_spoke' 'stat_sum' 'stat_summary' ## [22] 'stat_summary_2d' 'stat_summary_bin' 'stat_summary_hex'## [25] 'stat_summary2d' 'stat_unique' 'stat_ydensity' 举个例子来说明每一个几何对象都会有一个默认的统计变换。如果我们想要绘制ggplot2中的数据集diamonds中的carat直方图: ggplot(diamonds,aes(carat))+geom_histogram(binwidth=0.1) 这里geom_histogram意味着我们选择的几何对象是直方图,而绘制一个直方图就需要确定条形图的高度和组的中心值,所以必须要对数据进行统计变换,这里的统计变换包括count(观测值计数),density(观测值密度),x(组的中心位置),然后默认将条形图的高度赋值为观测值的频数(count),当然如果想要用密度代替也是可以的: ggplot(diamonds,aes(carat))+geom_histogram(aes(y=..density..),binwidth=0.1) 同样每个统计变换都有默认的几何对象,封箱(bin)统计变换默认使用条状几何对象(bar geom)来绘制直方图,下面的语句所绘制的图像和之前绘制的直方图是一样的。 ggplot(diamonds,aes(carat))+stat_bin(binwidth = 0.1) ggplot(diamonds,aes(carat))+stat_bin(aes(y=..density..),binwidth = 0.1) 既然是默认的统计变换/几何对象,那么是可以改变默认值的,这样会画出新奇甚至怪异的图像。 ggplot(diamonds,aes(carat))+stat_bin(aes(ymax=..density..),binwidth = 0.1,geom='area') ggplot(diamonds,aes(carat))+stat_bin(aes(ymax=..count..),binwidth = 0.1,geom='step') 统计变换对原始数据进行某种计算,然后在图上表示出来,例如对散点图上加一条回归线。可以使用stat=来设定统计变换方式。 ggplot(newDiamonds, aes(x=carat, y=price))+geom_point()+scale_y_log10()+stat_smooth() 这里就不按颜色、切工来分了,不然ggplot会按不同的分类变量分别做回归,图就很乱,如果我们需要这样做,我们可以使用分面,这个将在后面介绍。这里,aes所提供的参数,就通过ggplot提供,而不是提供给geom_point,因为ggplot里 的参数,相当于全局变量,geom_point()和stat_smooth()都知道x,y的映射,如果只提供给geom_point(),则相当于是 局部变量,geom_point知道这种映射,而stat_smooth不知道,当然你再给stat_smooth也提供x,y的映射,不过共用的映射, 还是提供给ggplot好。 在ggplot2中,每种几何类型都有对应的(默认)统计类型,反之亦然,两者不分家,下面对二者进行组合说明: 看一下几何函数geom_point和统计函数stat_identity的参数: # 函数说明,非运行代码geom_point(mapping = NULL, data = NULL, stat = 'identity', position = 'identity', na.rm = FALSE, ...)stat_identity(mapping = NULL, data = NULL, geom = 'point', position = 'identity', width = NULL, height = NULL, ...) 有4个参数是一样的:映射(mapping)、数据(data)、位置(position)和点点点(Dot-dot-dot: …)。H.W.特别强调了mapping和data参数的先后顺序在几何/统计类型设定函数和ggplot函数中的差别:ggplot函数先设定数据,再 设定映射;而几何/统计类型函数则相反,因为确定作图或统计之前一般都已经有数据,只需指定映射即可。如果不写参数名,它们的用法是这样的: # 示例,非运行代码ggplot(数据, 映射)geom_xxx(映射, 数据)stat_xxx(映射, 数据 '点点点'参数是R语言非常特殊的一个数据类型,用在函数的参数用表示任意参数,在这里表示传递给图层的任意参数如color, shape, alpha等。 前面我们一直用geom_point来做散点图,其实完全可以用stat_identity来做,得到的图形是完全相同的: # 取ggplot2的diamonds数据集的一部分数据:set.seed(100)d.sub <- diamonds[sample(nrow(diamonds),="">->500),]g <- ggplot(d.sub,="" aes(x="carat," y="">->price))theme_set(theme_bw())g + stat_identity() + ggtitle('geom_identity()')g + geom_point() + ggtitle('geom_point()')
4.5坐标系统(coordinate)坐标系统控制坐标轴,可以进行变换,例如XY轴翻转,笛卡尔坐标和极坐标转换,以满足我们的各种需求。 坐标轴翻转由coord_flip()实现: ggplot(newDiamonds)+geom_bar(aes(x=cut, fill=cut))+coord_flip() 而转换成极坐标可以由coord_polar()实现: ggplot(newDiamonds)+geom_bar(aes(x=factor(1), fill=cut))+coord_polar(theta='y') 这也是为什么之前介绍常用图形画法时没有提及饼图的原因,饼图实际上就是柱状图,只不过是使用极坐标而已,柱状图的高度,对应于饼图的弧度,饼图并不推荐,因为人类的眼睛比较弧度的能力比不上比较高度(柱状图) 还可以画靶心图: ggplot(newDiamonds)+geom_bar(aes(x=factor(1), fill=cut))+coord_polar() 以及风玫瑰图(windrose): ggplot(newDiamonds)+geom_bar(aes(x=clarity, fill=cut))+coord_polar()head(newDiamonds)## carat cut color clarity depth table price x y z## 49345 0.71 Very Good H SI1 62.5 60 2096 5.68 5.75 3.57## 50545 0.79 Premium H SI1 61.8 59 2275 5.97 5.91 3.67## 15434 1.03 Ideal F SI1 62.4 57 6178 6.48 6.44 4.03## 44792 0.50 Ideal E VS2 62.2 54 1624 5.08 5.11 3.17## 34614 0.27 Ideal E VS1 61.6 56 470 4.14 4.17 2.56## 27998 0.30 Premium E VS2 61.7 58 658 4.32 4.34 2.67 4.6图层(Layer)4.6.1图层对象geom_xxx和stat_xxx可以指定数据,映射、几何类型和统计类型,一般来说,有这些东西我们就可以作图了。但实际情况是这些函数不可以直接出图,因为它不是完整的ggplot对象: g <- geom_point(mapping="aes(x=carat," y="price)," data="">->d.sub)class(g)## [1] 'LayerInstance' 'Layer' 'ggproto'g## mapping: x = carat, y = price ## geom_point: na.rm = FALSE## stat_identity: na.rm = FALSE## position_identity 图层只是存储类型为environment的R语言对象,它只有建立在ggplot结构的基础上才会成为图形,在这里哪怕是一个空的ggplot对象框架都很有用。这好比仓库里的帐篷,如果你找不到地方把它们支起来,这些东西顶多是一堆货物: ggplot() + gclass(ggplot() + g)## [1] 'gg' 'ggplot' 前面说过多个图层的相加是有顺序的,图层和ggplot对象的加法也是有顺序的,如果把ggplot对象加到图层上就没有意义。这种规则同样适用于映射和ggplot的相加: g + ggplot()## NULLclass(aes(x=carat, y=price) + ggplot(d.sub))## [1] 'NULL'class(ggplot(d.sub) + aes(x=carat, y=price))## [1] 'gg' 'ggplot' 4.6.2图层的位置调整参数当前版ggplot2常用的有5种:
前面有讲到用法,此处就不再贴图了!代码如下: g <- ggplot(d.sub,="" aes(x="cut," y="price," fill="">->color))g + geom_bar(stat='summary', fun.y='mean', position='stack')g + geom_bar(stat='summary', fun.y='mean', position='fill')g + geom_bar(stat='summary', fun.y='mean', position='dodge')g + geom_bar(stat='summary', fun.y='mean', position='jitter') 对于抖动效果,加在散点图上比较有效果: g <- ggplot(d.sub,="" aes(x="cut," y="price," fill="">->color))g + geom_point(position='identity')g + geom_point(position='jitter') 所以怎么调整还得看图形的需要。再看看x轴数据连续的图形: g <- ggplot(d.sub,="" aes(x="price," fill="cut," color="">->cut))g + stat_density(position='stack')g + stat_density(position='fill')g + stat_density(position='identity')g + stat_density(position='identity', fill='transparent') 4.6.3图层组合图层的组合不是连续使用几个几何或统计类型函数那么简单。ggplot函数可以设置数据和映射,每个图层设置函数(geom_xxx和stat_xxx) 也都可以设置数据和映射,这虽然给组合图制作带来很大便利,但也可能产生一些混乱。如果不同图层设置的数据和映射不同,将会产生什么后果?得了解规则。 简单组合: 不同的图层使用同一套数据,只是几何类型或统计类型有差别。这是最简单也是最常用的,用ggplot函数设置好数据和映射,把几个图层加起来即可: datax <- data.frame(x="">->1:10, y=rnorm(10)+1:10)g <- ggplot(datax,="" aes(x="x," y="">->y))g + geom_point() + geom_line()g + geom_point() + geom_smooth(method='lm') ggplot2的图层设置函数对映射的数据类型是有较严格要求的,比如geom_point和geom_line函数要求x映射的数据类型为数值向量,而 geom_bar函数要使用因子型数据。如果数据类型不符合映射要求就得做类型转换,在组合图形时还得注意图层的先后顺序: datax <- data.frame(x="">->1:10, y=rnorm(10)+1:10)g <- ggplot(datax,="" aes(x="factor(x)," y="y))" +="">->'x')g + geom_bar(stat='identity', fill='gray') + geom_line(aes(group=1), size=2) + geom_point(color='red')g + geom_bar(stat='identity', fill='gray') + geom_smooth(aes(group=1), method='lm', se=FALSE, size=2) 上面第一个图除了花哨一点外没有任何科学意义,如果放在论文中会被骂得狗学喷头:一套数据重复作图还都是一个意思,是不是脑子有病?但这里只是说明作图方 法。作图过程应先作柱形图,因为它要求x映射是因子型数据。x映射为因子的数据作散点图的调整步骤相对简单。如果先作散点图,把坐标轴从数值向量(连续 型)改为因子型相当麻烦。 不同映射的组合: 映射反映的是数据变量。多数情况下一个图形中使用的是同一个数据集,只是变量不同。通常情况下x,y轴至少有一个是相同的,可以用不同图层叠加不同的数据变量: g <- ggplot(d.sub,="" aes(x="carat))">-> ylab('depth (blue) / table (red)')g + geom_point(aes(y=depth), color='blue') + geom_point(aes(y=table), color='red') 但是为什么要这么做呢?预先处理一下数据再作图会更好,图标都已经帮你设好了: library(reshape2)head(d.sub)## carat cut color clarity depth table price x y z## 16601 1.01 Very Good D SI1 62.1 59 6630 6.37 6.41 3.97## 13899 0.90 Ideal D SI1 62.4 55 5656 6.15 6.19 3.85## 29792 0.30 Ideal D SI1 61.6 56 709 4.34 4.30 2.66## 3042 0.30 Very Good G VS1 62.0 60 565 4.27 4.31 2.66## 25272 2.06 Premium I SI2 61.0 61 13912 8.18 8.10 5.02## 26093 1.56 Very Good G VVS1 59.7 59 15334 7.48 7.57 4.49datax <- melt(d.sub,="" id.vars="">->'carat', measure.vars=c('depth', 'table'))head(datax)## carat variable value## 1 1.01 depth 62.1## 2 0.90 depth 62.4## 3 0.30 depth 61.6## 4 0.30 depth 62.0## 5 2.06 depth 61.0## 6 1.56 depth 59.7tail(datax)## carat variable value## 995 0.91 table 56## 996 0.37 table 58## 997 1.27 table 58## 998 0.70 table 58## 999 0.71 table 62#3 1000 1.24 table 56ggplot(datax, aes(x=carat, y=value, color=variable)) + geom_point() 不同类型数据的组合: 如果在geom_xxx函数中改变数据会怎么样呢?不同类型的数据一般不会有完全相同的变量,否则就不是“不同类型”了,所以映射也会相应做修改。下面把 钻石数据diamonds和汽车数据mtcars这两个风牛马不相及的数据放在一起看看。(首先声明:下面的方法只是演示,图形没有任何科学意义。科学图 形应该能让观众直观地了解数据,而不是让明白者糊涂让糊涂者脑残。有不少人喜欢用双坐标作混合数据图,个人认为那是很愚昧的做法。) diamonds数据我们在前面已经了解过了,先看看R datasets包里面的mtcars数据: data(mtcars, package='datasets')str(mtcars) ## 'data.frame': 32 obs. of 11 variables:## $ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...## $ cyl : num 6 6 4 6 8 6 8 4 4 6 ...## $ disp: num 160 160 108 258 360 ...## $ hp : num 110 110 93 110 175 105 245 62 95 123 ...## $ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...## $ wt : num 2.62 2.88 2.32 3.21 3.44 ...## $ qsec: num 16.5 17 18.6 19.4 17 ...## $ vs : num 0 0 1 1 0 1 0 1 1 1 ...## $ am : num 1 1 1 0 0 0 0 0 0 0 ...## $ gear: num 4 4 4 3 3 3 3 4 4 4 ...## $ carb: num 4 4 1 1 2 1 4 2 2 4 ... head(mtcars, 4) ## mpg cyl disp hp drat wt qsec vs am gear carb## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 g <- ggplot(data="d.sub," aes(x="carat," y="price," color="">->cut))layer <- geom_point(aes(x="carb," y="mpg)," mtcars,="" color="">->'black')(g <- g="" +="">-> 图中数据点是正确的,但坐标轴标题却对不上号。看看ggplot对象的数据、映射和图层: head(g$data, 4)## carat cut color clarity depth table price x y z## 16601 1.01 Very Good D SI1 62.1 59 6630 6.37 6.41 3.97## 13899 0.90 Ideal D SI1 62.4 55 5656 6.15 6.19 3.85## 29792 0.30 Ideal D SI1 61.6 56 709 4.34 4.30 2.66## 3042 0.30 Very Good G VS1 62.0 60 565 4.27 4.31 2.66g$mapping## * x -> carat## * y -> price## * colour -> cutg$layers## [[1]]## mapping: x = carb, y = mpg ## geom_point: na.rm = FALSE## stat_identity: na.rm = FALSE## position_identity 数据和映射都还是ggplot原来设置的样子,layer图层设置的都没有存储到ggplot图形列表对象的data和mapping元素中,而是放在了图层中,但图层中设定的数据不知道跑哪里。 如果再增加一个图层,把坐标轴标题标清楚: newlayer <- geom_point(aes(y="">->depth))(g <- g="" +="" newlayer="" +="">->'carb(black) / carat') + ylab('mpg(black) / depth')) newlayer重新指定了y映射,但没碰原来ggplot对象设置的x和color映射,从获得的图形来看y数据改变了,x和color还是原ggplot对象的设置。查看一下映射和图层: g$mapping## * x -> carat## * y -> price## * colour -> cutg$layers## [[1]]## mapping: x = carb, y = mpg ## geom_point: na.rm = FALSE## stat_identity: na.rm = FALSE## position_identity ## ## [[2]]## mapping: y = depth ## geom_point: na.rm = FALSE## stat_identity: na.rm = FALSE## position_identity 可以这么理解:ggplot2图层作图时依次从ggplot对象和图层中获取数据/映射,如果两者映射有重叠,后者将替换前者,但只是在该图层中进行替换而不影响ggplot对象。 如果ggplot对象的映射比图层的映射多,而图层又使用了不同的数据,这是什么情况?看看: g + geom_point(aes(x=carb, y=mpg), mtcars)## Error in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, : ## 参数值意味着不同的行数: 32, 0 由于图层继承了ggplot对象的color映射,但又找不到数据,所以没法作图。解决办法是把原有的映射用NULL取代,或者设为常量(非映射): g + geom_point(aes(x=carb, y=mpg, color=NULL), mtcars)g + geom_point(aes(x=carb, y=mpg), mtcars, color='red') photoshop流行的原因在于PS 3.0时引入图层的概念,ggplot的牛B之处在于使用+号来叠加图层,这堪称是泛型编程的典范。在前面散点图上,我们已经见识过,加上了一个回归线拟合的图层。有了图层的概念,使用ggplot画起图来,就更加得心应手。做为图层的一个很好的例子是蝙蝠侠logo,batman logo由6个函数组成,在下面的例子中,先画第一个函数,之后再加一个图层画第二个函数,不断重复这一过程,直到六个函数全部画好。 # batman logo是由六个函数构成的。# 利用ggplot2将多个线画到同一幅图案里面。 require(ggplot2)f1 <> function(x) { y1 <->->3*sqrt(1-(x/7)^2) y2 <->->3*sqrt(1-(x/7)^2) y <> c(y1,y2) d <- data.frame(x="">->y) d <- d[d$y=""> -3*sqrt(33)/7,] return(d)}x1 <->->3, 7, 0.001), seq(-7, -3, 0.001))d1 <> f1(x1)p1 <- ggplot(d1,aes(x,y))="" +="" geom_point(color="">->'red')print(p1)-> x2 <->->4,4, 0.001)y2 <->->2)-(3*sqrt(33)-7)*x2^2/112-3 + sqrt(1-(abs(abs(x2)-2)-1)^2)#only work with ggplot2 <=>=>0.8.9#p2 <- p1="" +="" geom_point(aes(x="x2,y=y2)," color="">->'yellow')# in ggplot2 0.9.0, should be:d2 <- data.frame(x2="x2," y2="">->y2)p2 <- p1="" +="" geom_point(data="d2," aes(x="x2,y=y2)," color="">->'yellow')print(p2) x3 <->->0.75,1,0.001), seq(-1,-0.75,0.001))y3 <->->9-8*abs(x3)#p3 <- p2+geom_point(aes(x="x3,y=y3)," color="">->'green')d3 <- data.frame(x3="x3," y3="">->y3)p3 <- p2+geom_point(data="d3," aes(x="x3,y=y3)," color="">->'green')print(p3) x4 <->->0.5,0.75,0.001), seq(-0.75,-0.5,0.001))y4 <->->3*abs(x4)+0.75#p4 <- p3+geom_point(aes(x="x4,y=y4)," color="">->'steelblue')d4 <- data.frame(x4="">->y4)p4 <- p3+geom_point(data="d4," aes(x="x4,y=y4)," color="">->'steelblue')print(p4) x5 <->->0.5,0.5,0.001)y5 <->->2.25,length(x5))#p5 <- p4+geom_point(aes(x="">->y5))d5 <- data.frame(x5="">->y5)p5 <- p4+geom_point(data="d5," aes(x="">->y5))print(p5) x6 <->->3,-1,0.001), seq(1,3,0.001))y6 <->->6 * sqrt(10)/7 + (1.5 - 0.5 * abs(x6)) * sqrt(abs(abs(x6)-1)/(abs(x6)-1)) - 6 * sqrt(10) * sqrt(4-(abs(x6)-1)^2)/14#p6 <- p5+geom_point(aes(x="x6,y=y6)," colour="">->'blue')d6 <- data.frame(x6="">->y6)p6 <- p5+geom_point(data="d6,aes(x=x6,y=y6)," colour="">->'blue')print(p6) p <->->theme_bw()print(p) 本公众号精彩历史文章: 04:如何在R软件中求一致性指数( Harrell'concordance index:C-index)? 05:Nomogram 绘制原理及R&SAS实现. 06 : Lasso方法简要介绍及其在回归分析中的应用 07 : 最优模型选择中的交叉验证(Cross validation)方法 08 : 用R语言进行分位数回归(Quantile Regression) 09 : 样本数据中异常值(Outliers)检测方法及SPSS & R实现 10 : 原始数据中几类缺失值(Missing Data)的SPSS及R处理方法 11 : [Survival analysis] Kaplan-Meier法之SPSS实现 12 : [Survival analysis] COX比例风险回归模型在SPSS中的实现 13 : 用R绘制地图:以疾病流行趋势为例 14 : 数据挖掘方法:聚类分析简要介绍 及SPSS&R实现 15 : 医学研究中的Logistic回归分析及R实现 16 : 常用的非参数检验(Nonparametric Tests)总结 17 : 高中生都能看懂的最小二乘法原理 18 : R语言中可实现的常用统计假设检验总结(侧重时间序列) 19 : 如何根据样本例数、均数、标准差进行T-Test和ANOVA 20 : 统计学中自由度的理解和应用 21 : ROC和AUC介绍以及如何计算AUC 22 : 支持向量机SVM介绍及R实现 23 : SPSS如何做主成分分析? 24 : Bootstrap再抽样方法简介 25 : 定量测量结果的一致性评价及 Bland-Altman 法的应用 26 : 使用R绘制热图及网络图 27 : 几种常用的双坐标轴图形绘制 28 : 遗失的艺术—诺谟图(Nomogram) 29 : Nomogram 绘制原理及R&SAS实现(二) 30 : WOE:信用评分卡模型中的变量离散化方法 32 : 重复测量的多因素方差分析SPSS实现操作过程 |
|