R语言数据集行列互换技巧
创新互联建站2013年开创至今,先为萧县等服务建站,萧县等地企业,进行企业商务咨询服务。为萧县企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。
现在给大家介绍的数据处理技巧是长转宽,也就相当于Excel中的转置,不过用R语言实现的长转宽还有数据合并的功能,自然比Excel强大多了。
这里给大家介绍4个函数,其中melt()、dcast()来自reshape2包,巧州gather()、spread()来自tidyr包
一、宽转长——melt()、gather()
[python] view plain copy
mydata-data.frame(
name=c("store1","store2","store3","store4"),
address=c("普陀区","黄浦区","徐汇区","浦东新区"),
sale2014=c(3000,2500,2100,1000),
sale2015=c(3020,2800,3900,2000),
sale2016=c(5150,3600,2700,2500),
sale2017=c(4450,4100,4000,3200)
)
#宽转长——melt
mydata1-melt(
mydata,
id.vars=c("address","name"),#要保留的主字段
variable.name = "Year",#转换后的分类字段名称(维度)
value.name = "Sale" #转换后的度量值名称
)
输出结果
[python] view plain copy
mydata1-melt(
+ mydata,
+ id.vars=c("address","name"),#要保留的主字段
+ variable.name = "Year",#转换后的分类字段名称(维孝森蔽度)
+ value.name = "Sale" #转换后的度量值名称
+ )
mydata1
address name Year Sale
1 普陀区 store1 sale2014 3000
2 黄浦区 store2 sale2014 2500
3 徐汇区 store3 sale2014 2100
4 浦东新区 store4 sale2014 1000
5 普陀区 store1 sale2015 3020
6 黄浦区 store2 sale2015 2800
7 徐汇区 store3 sale2015 3900
8 浦东新区 store4 sale2015 2000
9 普陀区 store1 sale2016 5150
10 黄浦区 store2 sale2016 3600
11 徐汇区 store3 sale2016 2700
12 浦东新区春戚 store4 sale2016 2500
13 普陀区 store1 sale2017 4450
14 黄浦区 store2 sale2017 4100
15 徐汇区 store3 sale2017 4000
16 浦东新区 store4 sale2017 3200
再来看看gather()函数怎么用
[python] view plain copy
#宽转长——gather
mydata1-tidyr::gather(
+ data=mydata,
+ key="Year",
+ value="sale",
+ sale2014:sale2017
+ )
mydata1
name address Year sale
1 store1 普陀区 sale2014 3000
2 store2 黄浦区 sale2014 2500
3 store3 徐汇区 sale2014 2100
4 store4 浦东新区 sale2014 1000
5 store1 普陀区 sale2015 3020
6 store2 黄浦区 sale2015 2800
7 store3 徐汇区 sale2015 3900
8 store4 浦东新区 sale2015 2000
9 store1 普陀区 sale2016 5150
10 store2 黄浦区 sale2016 3600
11 store3 徐汇区 sale2016 2700
12 store4 浦东新区 sale2016 2500
13 store1 普陀区 sale2017 4450
14 store2 黄浦区 sale2017 4100
15 store3 徐汇区 sale2017 4000
16 store4 浦东新区 sale2017 3200
和melt()函数不同,gather()函数需要指定关键字段key,以及关键字段对应的值value,但是gather()函数更加好理解。
二、长转宽——dcast()和spread()
还是用上面的data1数据集,先来看看dcast()函数
[python] view plain copy
#长转宽——dcast
dcast(
data=mydata1,
name+address~Year
#左侧是要保留的字段,右侧是要分割的分类变量,列数等于表达式
#右侧分类变量的类别个数
)
[python] view plain copy
#长转宽——dcast
dcast(
+ data=mydata1,
+ name+address~Year
+ #左侧是要保留的字段,右侧是要分割的分类变量,列数等于表达式
+ #右侧分类变量的类别个数
+ )
Using sale as value column: use value.var to override.
name address sale2014 sale2015 sale2016 sale2017
1 store1 普陀区 3000 3020 5150 4450
2 store2 黄浦区 2500 2800 3600 4100
3 store3 徐汇区 2100 3900 2700 4000
4 store4 浦东新区 1000 2000 2500 3200
dcast()函数的使用规则需要琢磨下才能理解,大家好好看看注释部分,再来看看spread()
[python] view plain copy
#长转宽——spread
tidyr::spread(
data=mydata1,
key=Year,
value=sale
)
[python] view plain copy
#长转宽——spread
tidyr::spread(
+ data=mydata1,
+ key=Year,
+ value=sale
+ )
name address sale2014 sale2015 sale2016 sale2017
1 store1 普陀区 3000 3020 5150 4450
2 store2 黄浦区 2500 2800 3600 4100
3 store3 徐汇区 2100 3900 2700 4000
4 store4 浦东新区 1000 2000 2500 3200
直接调用tidyr::spread,需要指定关键字段key和对应的值value。
但是从理解上来看,我个人更喜欢tidyr包的函数,使用很清晰,大家可以根据实际情况自行选择,好啦,今天的分享结束,下次再见!
reshape2包的进化版—tidyr包
tidyr包的作者是Hadley Wickham。这个包常跟dplyr结合使用。
本文将演示tidyr包中下述四个函数的用法:
gather—宽数据转为长数据。类似于reshape2包中的melt函数
spread—长数据转为宽数据。类似于reshape2包中的cast函数
unit—多列合并为一列
separate—将一列分离为多列
下面使用datasets包中的mtcars数据集做演示。
library(tidyr)
library(dplyr)
head(mtcars)
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
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
为方便处理,在数据集中增加一列car
mtcars$car - rownames(mtcars)
mtcars - mtcars[, c(12, 1:11)]
gather
gather的调用格式为:
gather(data, key, value, ..., na.rm = FALSE, convert = FALSE)
这里,...表示需要聚闷败磨合的指定列。
与reshape2包中的melt函数一样,得到如下结果:
mtcarsNew - mtcars %% gather(attribute, value, -car)
head(mtcarsNew)
car attribute value
1 Mazda RX4 mpg 21.0
2 Mazda RX4 Wag mpg 21.0
3 Datsun 710 mpg 22.8
4 枯洞 Hornet 4 Drive mpg 21.4
5 Hornet Sportabout mpg 18.7
6 Valiant mpg 18.1
tail(mtcarsNew)
car attribute value
347 Porsche 914-2 carb 2
348 Lotus Europa carb 2
349 Ford Pantera L carb 4
350 Ferrari Dino carb 6
351 Maserati Bora carb 8
352 Volvo 142E carb 2
如你所见,除了car列外,其余列聚合成两列,分别命名为attribute和value。
tidyr很好蚂斗的一点是可以只gather若干列而其他列保持不变。如果你想gather在map和gear之间的所有列而保持carb和car列不变,可以像下面这样做:
mtcarsNew - mtcars %% gather(attribute, value, mpg:gear)
head(mtcarsNew)
car carb attribute value
1 Mazda RX4 4 mpg 21.0
2 Mazda RX4 Wag 4 mpg 21.0
3 Datsun 710 1 mpg 22.8
4 Hornet 4 Drive 1 mpg 21.4
5 Hornet Sportabout 2 mpg 18.7
6 Valiant 1 mpg 18.1
spread
spread的调用格式为:
spread(data, key, value, fill = NA, convert = FALSE, drop = TRUE)
与reshape2包中的cast函数一样,得到如下结果:
mtcarsSpread - mtcarsNew %% spread(attribute, value)
head(mtcarsSpread)
car carb mpg cyl disp hp drat wt qsec vs am gear
1 AMC Javelin 2 15.2 8 304 150 3.15 3.435 17.30 0 0 3
2 Cadillac Fleetwood 4 10.4 8 472 205 2.93 5.250 17.98 0 0 3
3 Camaro Z28 4 13.3 8 350 245 3.73 3.840 15.41 0 0 3
4 Chrysler Imperial 4 14.7 8 440 230 3.23 5.345 17.42 0 0 3
5 Datsun 710 1 22.8 4 108 93 3.85 2.320 18.61 1 1 4
6 Dodge Challenger 2 15.5 8 318 150 2.76 3.520 16.87 0 0 3
unite
unite的调用格式如下:
unite(data, col, ..., sep = "_", remove = TRUE)
where ... represents the columns to unite and col represents the c
这里,...表示需要合并的列,col表示合并后的列。
我们先虚构一些数据:
set.seed(1)
date - as.Date('2016-01-01') + 0:14
hour - sample(1:24, 15)
min - sample(1:60, 15)
second - sample(1:60, 15)
event - sample(letters, 15)
data - data.frame(date, hour, min, second, event)
data
date hour min second event
1 2016-01-01 7 30 29 u
2 2016-01-02 9 43 36 a
3 2016-01-03 13 58 60 l
4 2016-01-04 20 22 11 q
5 2016-01-05 5 44 47 p
6 2016-01-06 18 52 37 k
7 2016-01-07 19 12 43 r
8 2016-01-08 12 35 6 i
9 2016-01-09 11 7 38 e
10 2016-01-10 1 14 21 b
11 2016-01-11 3 20 42 w
12 2016-01-12 14 1 32 t
13 2016-01-13 23 19 52 h
14 2016-01-14 21 41 26 s
15 2016-01-15 8 16 25 o
现在,我们需要把date,hour,min和second列合并为新列datetime。通常,R中的日期时间格式为"Year-Month-Day-Hour:Min:Second"。
dataNew - data %%
unite(datehour, date, hour, sep = ' ') %%
unite(datetime, datehour, min, second, sep = ':')
dataNew
datetime event
1 2016-01-01 7:30:29 u
2 2016-01-02 9:43:36 a
3 2016-01-03 13:58:60 l
4 2016-01-04 20:22:11 q
5 2016-01-05 5:44:47 p
6 2016-01-06 18:52:37 k
7 2016-01-07 19:12:43 r
8 2016-01-08 12:35:6 i
9 2016-01-09 11:7:38 e
10 2016-01-10 1:14:21 b
11 2016-01-11 3:20:42 w
12 2016-01-12 14:1:32 t
13 2016-01-13 23:19:52 h
14 2016-01-14 21:41:26 s
15 2016-01-15 8:16:25 o
separate
separate的调用格式为:
separate(data, col, into, sep = "[^[:alnum:]]+", remove = TRUE,
convert = FALSE, extra = "warn", fill = "warn", ...)
我们可以用separate函数将数据恢复到刚创建的时候,如下所示:
data1 - dataNew %%
separate(datetime, c('date', 'time'), sep = ' ') %%
separate(time, c('hour', 'min', 'second'), sep = ':')
data1
date hour min second event
1 2016-01-01 07 30 29 u
2 2016-01-02 09 43 36 a
3 2016-01-03 13 59 00 l
4 2016-01-04 20 22 11 q
5 2016-01-05 05 44 47 p
6 2016-01-06 18 52 37 k
7 2016-01-07 19 12 43 r
8 2016-01-08 12 35 06 i
9 2016-01-09 11 07 38 e
10 2016-01-10 01 14 21 b
11 2016-01-11 03 20 42 w
12 2016-01-12 14 01 32 t
13 2016-01-13 23 19 52 h
14 2016-01-14 21 41 26 s
15 2016-01-15 08 16 25 o
首先,将datetime分为date列和time列。然后,将time列分为hour,min,second列。
在数据分析过程中,利用各种图表进行数据探索是必要的前期工作。描述性统计中就包括了直方图、散点图等工具来探索连续数据,对于分类数据,则可以采用条形图、交叉分组表等工具。Excel中所谓的“数据透视表”,其实就是一个交互式的交叉分组表。在R语言中可以很容易的用table()等函数得到相应的结果。对于一些更为复杂的任务,就需要其它的函数或包来完成。本例先以iris数据集为研究对象示范一些基本函数的用法,再介绍reshape包的强大功能。
iris数据集中有五个变量,其中Species表示鸢尾属花的子类,其它四个变量分别是花瓣和萼片的长度和宽度。你可以用head(iris)来观察原始数据的一些样本。我们的第一个任务是想计算不同种类花在四个指标上的平均值。用到的函数有tapply,by及aggregate。这篇文章对它们有所涉及。
将数据解包后,先用tapply函数尝试,但会发现该函数一次只允许输入一个变量。如果要完全四个变量的计算可能得用到循环。放弃这个函数来试试用by函数,该函数可以一次输入多个变量,但输出结果为一个list格式,还需要用do.call函数进行整合,有点麻烦。最方便友好的还是aggregate函数,直接输出为数据框格式。另外它还允许用公式来设置分组因子。
attach(iris)
names(iris)
tapply(X=Sepal.Length,INDEX=Species,FUN=mean)
temp -by(data=iris[,1:4],INDICES=Species,FUN=mean)
do.call(rbind,temp)
aggregate(x=iris[,1:4],by=list(Species),FUN=mean)
aggregate(.~ Species, data = iris, mean)aggreagate函数表现已然不错,但还不够强大。比如说它没法直接得出表格的边际值,所以下面就请出本场的主角,即reshape包中的两员大将:melt与cast。这两个通常是配合使用,melt专门负责“融合”原始数据,形成长型(long)数据结构。cast则专职将融合后的数据“重铸”为新的形式(让人想起了“铁索连环”)。基本上只要有这两个函数,就能统一解决所有的汇总问题。
还是以上面的问题为例子,先加载reshape包,然后用melt函数进行融合数据,其中参数id指定了用Species为编号变量,measure参数用来指定分析变量(即被融合的变量),本例中只指定了参数id,所以原始数据中未包括在id中的其它变量均指定为分析变量。你可以观察到新的数据iris.melt其实就是堆叠(stack)后的数据。然后我们再用cast来重铸,cast函数中可以使用公式,波浪号左侧变量将纵列显示,右侧变量将以横行显示。margins参数设定了以列作为边际汇总方向。如果希望在计算中只包括两种花,可以使用subset参数。
library(reshape)
iris.melt - melt(iris,id='Species')
cast(Species~variable,data=iris.melt,mean,margins="grand_row")
cast(Species~variable,data=iris.melt,mean,
subset=Species %in% c('setosa','versicolor'),
margins='grand_row')reshape包的作者也是ggplot2包的开发者,这个牛人是个完美主义者,在reshape包推出五年后,他缓或重握哪纳构代码推出了新的reshape2包。这个新包的特性在于:
改进算法,使计算与内存使用效能增强;
用dcast和acast代替了原来的cast函数;
用变量名来设定边际参数;
删除cast中的一些特性,因为他确认plyr包能更好的处理;
所有的melt函数族都增加了处理缺失值的参数。
下面我们以diamonds数据为例,来完成一个略为复杂的任务。我们希望计算不同切工和不同纯净度条件下,钻石的单位平均价格,并加以比较。首先加载reshape2包和ggplot2包,然后取子集。将原始数据融合,以切工、颜色段没和净度为编号变量。再利用dcast函数重铸数据,得到汇总结果。计算出单位价格,最后用条形图表现结果。
library(reshape2)
library(ggplot2)
data - diamonds[1:7]
data.melt - melt(data,id=c('cut','color','clarity'))
diam.sum - dcast(data.melt,cut+clarity~variable,
subset=.(variable %in% c('price','carat')),mean)
diam.sum$average - diam.sum$price/diam.sum$carat
p - ggplot(diam.sum,aes(cut,average,fill=clarity))
p + geom_bar(position='dodge')
除了reshape包以外,R语言中还有stack、unstack、reshape等函数能完成类似的工作,但论功能的强大,还是首推reshape包中的哼哈二将。