在数据分析中,往往会遇到各种复杂的数据处理操作:分组、排序、过滤、转置、填充、移动、合并、分裂、去重、找重、填充等操作。这时候R语言就是一个很好的选择:R可以高效地、优雅地解决数据处理操作。(本章节为R语言入门第二部分总结篇:数据操作)
本章内容布局思路:思来想后,想到SQL查询的查询思路可以作为本章节的布局思路
- 1.了解表结构/数据结构
- 2.对表中的一些数据做出修改、替换、甚至生成新字段
- 3.from:数据合并/连接
- 4.where:条件筛选/过滤
- 5.group:分组
6.having和select:呈现不明显- 7.order:排序
- 8.其他补充
目录
1. 初识R语言支持的数据类型
1.1 向量 Vector : c()
1.2 矩阵 Matrix: matrix()
1.3 数据框 DataFrame: data.frame()
1.4 时间序列 XTS: xts()
1.5 因子Factor:factor(补充)
2.查看数据概况
summary()和str()
3.修改/替换/重定义数据
4.数据合并
3.1 向量合并
3.2 cbind列合并(等长)
3.3 rbind行合并
3.4 merge
3.5 补充:集合操作
4.过滤/筛选
4.1 缺失值处理
4.2 数据增减
4.3 数值分段cut
5.分组操作
5.1 aggregate语法
5.2 aggregate分组计算
5.3 aggregate分组计算(formula形式)
6. 排序order
7. 计数table
8. 分裂split
9. 去重与找重unique
10.转置
1. 初识R语言支持的数据类型
开始之前,需要先了解一下R语言支持的数据类型,以及这些常用类型的特点。以下4种类型是最常用的:向量、矩阵、数据框、时间序列。
可参考↓↓
R语言|第2讲:生成数据
R语言快速入门:数据结构 生成数据 数据引用 读取外部数据
- 向量 Vector : c()
- 矩阵 Matrix: matrix()
- 数据框 DataFrame: data.frame()
- 时间序列 XTS: xts()
- 因子Factor:factor(补充)
(图片来自于粉丝日志)
1.1 向量 Vector : c()
代码语言:javascript复制> x <- c(1:10)
> x
[1] 1 2 3 4 5 6 7 8 9 10
1.2 矩阵 Matrix: matrix()
代码语言:javascript复制#矩阵用法
matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE,dimnames = NULL) #表示生成1行,1列的一个矩阵,其中仅仅包含一个元素“NA”
#---示例---#
> matrix(c(1,2,3, 11,12,13), nrow = 2, ncol = 3, byrow = TRUE, dimnames = list(c("row1", "row2"), c("C.1", "C.2", "C.3")))
C.1 C.2 C.3
row1 1 2 3
row2 11 12 13
#nrow = 2和ncol = 3 定义2x3的2行3列矩阵
#byrow = TRUE 是控制矩阵中的数据c(1,2,3, 11,12,13)按照行的顺序排列,默认按照列排列
#dimnames = list(c("row1", "row2"), c("C.1", "C.2", "C.3")) 定义矩阵行名和列名
1.3 数据框 DataFrame: data.frame()
代码语言:javascript复制#其中" <- "是赋值的意思,将向量c(11:15)赋值给对象x
> x <- c(11:15)
> y <- c(1:5)
#将向量x和y合并存储到数据框中,并重命名为xf和yf
> data.frame(xf = x, yf = y)
xf yf
1 11 1
2 12 2
3 13 3
4 14 4
5 15 5
1.4 时间序列 XTS: xts()
代码语言:javascript复制> library(xts)
> x <- c(11:15)
> xts(x,order.by=as.Date('2019-09-14') 1:5)
[,1]
2019-09-15 11
2019-09-16 12
2019-09-17 13
2019-09-18 14
2019-09-19 15
关于xts类型的详细介绍,请参考文章《可扩展的时间序列xts》http://blog.fens.me/r-xts/
代码语言:javascript复制2.查看数据概况
代码语言:javascript复制> data(iris)
> head(iris,10)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
> summary(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50
1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50
Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50
Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
> str(iris)
'data.frame': 150 obs. of 5 variables:
$ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
$ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
$ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
$ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
$ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
3.修改/替换/重定义数据
修改指定单元格,修改指定列,within 关联修改
代码语言:javascript复制leadership$age[leadership$age==99] <- NA
leadership$agecat2 <- NA
leadership <- within(leadership,{
agecat2[age>75] <- "Elder"
agecat2[age>=55 & age<=75] <- "Middle Aged"
agecat2[age<55] <- "Young"}
)
4 数据合并
数据操作中,数据(集)合并是经常被用到。例如:合并来源不同,结构相似的两个表格
3.1 向量合并
代码语言:javascript复制#一维向量合并直接将要合并的变量以","分割放到c()中即可。
> x <- c(11:20)
> y <- c(1:10)
> c(x,y)
[1] 11 12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10
3.2 cbind列合并(等长)
总结:cbind等行数、按列合并(无序)
代码语言:javascript复制#生成测试数据
> ID1 <- c(1:4)
> ID2 <- c(2:5)
> name<-c("A","B","C","D")
> score<-c(8,22,7,6)
> student1<-data.frame(ID1,name)
> student2<-data.frame(ID2,score)
#按照行合并student1和student2
> cbind(student1,student2)
ID1 name ID2 score1
1 A 2 82
2 B 3 223
3 C 4 74
4 D 5 6
3.3 rbind行合并
总结:按行合并,需要注意数据集需要有相同的列字段名
代码语言:javascript复制> #生成测试数据student1
> ID <- c(1:4)
> score <- c(8,22,7,33)
> student1<-data.frame(ID,score)>
#生成测试数据student2
> ID <- c("A","B","C","D")
> score <- c(11,2,55,3)
> student2<-data.frame(ID,score)
#按行合并,需要注意数据集需要有相同的列字段名
> rbind(student1,student2)
ID score1
1 82
2 223
3 74
4 335
A 116
B 27
C 558
D 3
3.4 merge
代码语言:javascript复制#merge语法结构
merge(x, y, by = intersect(names(x), names(y)),
by.x = by, by.y = by, all = FALSE, all.x = all, all.y = all,
sort = TRUE, suffixes = c(".x",".y"), no.dups = TRUE,
incomparables = NULL, ...)
#其中,通过by字段控制连接字段by = "ID"为单字段连接,by = c("ID","NAME",……)为多字段连接;
#通过all=FALSE/TRUE、all.x = TRUE和all.y = TRUE实现内连接、外连接、左连接和右连接
代码语言:javascript复制#———merge用法———#
> #生成测试数据
> ID1 <- c(1:4)
> ID2 <- c(2:5)
> name<-c("A","B","C","D")
> score<-c(8,22,7,6)
> student1<-data.frame(ID1,name)
> student2<-data.frame(ID2,score)
>
> #内连接:保留交叉位置数据
> merge(student1,student2,by.x = "ID1", by.y = "ID2",all=TRUE)
ID1 name score
1 1 A NA
2 2 B 8
3 3 C 22
4 4 D 7
5 5 <NA> 6
> #左连接:保留左边所有数据及交叉y数据
> merge(student1,student2,by.x = "ID1", by.y = "ID2",all.x=TRUE)
ID1 name score
1 1 A NA
2 2 B 8
3 3 C 22
4 4 D 7
> #右连接:保留右边所有数据及交叉x数据
> merge(student1,student2,by.x = "ID1", by.y = "ID2",all.y=TRUE)
ID1 name score
1 2 B 8
2 3 C 22
3 4 D 7
4 5 <NA> 6
3.5 补充:集合操作
集合操作,是对2个向量的操作,处理2个向量之间的数值的关系,找到包含关系、取交集、并集、差集等。、
代码语言:javascript复制# 定义2个向量x,y
> x<-c(3:8,NA);x
[1] 3 4 5 6 7 8 NA
> y<-c(NA,6:10,NA);y
[1] NA 6 7 8 9 10 NA
# 判断x与y重复的元素的位置
> is.element(x, y)
[1] FALSE FALSE FALSE TRUE TRUE TRUE TRUE
# 判断y与x重复的元素的位置
> is.element(y, x)
[1] TRUE TRUE TRUE TRUE FALSE FALSE TRUE
# 取并集
> union(x, y)
[1] 3 4 5 6 7 8 NA 9 10
# 取交集
> intersect(x, y)
[1] 6 7 8 NA
# 取x有,y没有元素
> setdiff(x, y)
[1] 3 4 5
# 取y有,x没有元素
> setdiff(y, x)
[1] 9 10
# 判断2个向量是否相等
> setequal(x, y)
[1] FALSE
代码语言:javascript复制数据连接主要涉及到merge函数和dplyr包中的*_join等函数,另外sqldf函数(SQL)亦可以实现数据连接功能。
参考→《R语言 数据(集)合并与连接/匹配 | 专题2》
4.过滤/筛选
过滤,是对数据集按照某种规则进行筛选,去掉不符合条件的数据,保留符合条件的数据。对于NA值的操作,主要都集中在了过滤操作和填充操作中,因此就不在单独介绍NA值的处理了。
可参考↓↓
R语言 | 第一部分:数据预处理 7.数据筛选和8.抽样
R语言数据管理与dplyr、tidyr | 第4讲 5 dplyr中5.1筛选filter和5.3选择select
R 语言 逻辑运算:TRUE/FALSE | 专题3
4.1 缺失值处理
代码语言:javascript复制# 生成数据框
> df<-data.frame(a=c(1,NA,NA,2,NA),
b=c('B','A','B','B',NA),
c=c(rnorm(2),NA,NA,NA));df
a b c
1 1 B -0.3041839
2 NA A 0.3700188
3 NA B NA
4 2 B NA
5 NA <NA> NA
# 过滤有NA行的数据
> na.omit(df)
a b c
1 1 B -0.3041839
# 过滤,保留b列值为B的数据
> df[which(df$b=='B'),]
a b c
1 1 B -0.3041839
3 NA B NA
4 2 B NA
4.2 数据增减
常见如以下不同方法
代码语言:javascript复制#方法一:减行数或列数
x=x[,-1] #代表删除x数据集中第一列数据
#方法二:dplyr::mutate#数值重定义和赋值
#将Ozone列取负数赋值给new,然后Temp列重新计算为(Temp - 32) / 1.8
mutate(airquality, new = -Ozone, Temp = (Temp - 32) / 1.8)
#方法三:subset筛选变量服从某值的子集
subset(airquality, Temp > 80, select = c(Ozone, Temp))
4.3 数值分段
数值分段,就是把一个连续型的数值型数据,按区间分割为因子类型的离散型数据。
代码语言:javascript复制> x<-1:10;x
[1] 1 2 3 4 5 6 7 8 9 10
# 把向量转换为3段因子,分别列出每个值对应因子
> cut(x, 3)
[1] (0.991,4] (0.991,4] (0.991,4] (0.991,4] (4,7] (4,7] (4,7] (7,10] (7,10] (7,10]
Levels: (0.991,4] (4,7] (7,10]
# 对因子保留2位精度,并支持排序
> cut(x, 3, dig.lab = 2, ordered = TRUE)
[1] (0.99,4] (0.99,4] (0.99,4] (0.99,4] (4,7] (4,7] (4,7] (7,10] (7,10] (7,10]
Levels: (0.99,4] < (4,7] < (7,10]
代码语言:javascript复制
5 分组操作
此处仅讲述aggregate数据分组计算内容,更多分组计算内容
参考→《R语言 分组计算,不止group_by》
- dplyr包中的group_by联合summarize
- group_by和summarise单变量分组计算
- group_by和summarise多变量分组计算
- ddply分组计算示例
5.1 aggregate语法
代码语言:javascript复制aggregate(x, by, FUN)
#x为数据集
#by为分组变量列表
#FUN为计算函数
5.2 aggregate分组计算
代码语言:javascript复制> row_names <- rep(c("A","B","C"),3)
> col_names <- LETTERS[1:3]
> df_matrix <- matrix(c(1:27),nrow = 9,dimnames = list(row_names,col_names))
> df_matrix
A B C
A 1 10 19
B 2 11 20
C 3 12 21
A 4 13 22
B 5 14 23
C 6 15 24
A 7 16 25
B 8 17 26
C 9 18 27
#注意分组变量为列表形式
> aggregate(df_matrix,list(Group = row_names), mean)
Group A B C
1 A 4 13 22
2 B 5 14 23
3 C 6 15 24
5.3 aggregate分组计算补充(formula形式)
可以重点了解一下
代码语言:javascript复制aggregate(formula, data, FUN)
#Formulas, one ~ one, one ~ many, many ~ one, and many ~ many:
#一组对一计算变量函数型分组计算:计算变量~分组变量
> aggregate(weight ~ feed, data = chickwts, mean)
feed weight
1 casein 323.5833
2 horsebean 160.2000
3 linseed 218.7500
4 meatmeal 276.9091
5 soybean 246.4286
6 sunflower 328.9167
#多组对一函数型分组计算:计算变量~分组变量1 分组变量2……
> aggregate(breaks ~ wool tension, data = warpbreaks, mean)
wool tension breaks
1 A L 44.55556
2 B L 28.22222
3 A M 24.00000
4 B M 28.77778
5 A H 24.55556
6 B H 18.77778
#一组对多计算变量,函数型分组计算:cbind(计算变量1,计算变量2)~分组变量1
> aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, mean)
Month Ozone Temp
1 5 23.61538 66.73077
2 6 29.44444 78.22222
3 7 59.11538 83.88462
4 8 59.96154 83.96154
5 9 31.44828 76.89655
#多组对多计算变量,函数型分组计算:cbind(计算变量1,计算变量2)~分组变量1 分组变量2……
> aggregate(cbind(ncases, ncontrols) ~ alcgp tobgp, data = esoph, sum)
alcgp tobgp ncases ncontrols
1 0-39g/day 0-9g/day 9 261
2 40-79 0-9g/day 34 179
3 80-119 0-9g/day 19 61
4 120 0-9g/day 16 24
5 0-39g/day 10-19 10 84
6 40-79 10-19 17 85
7 80-119 10-19 19 49
8 120 10-19 12 18
9 0-39g/day 20-29 5 42
10 40-79 20-29 15 62
11 80-119 20-29 6 16
12 120 20-29 7 12
13 0-39g/day 30 5 28
14 40-79 30 9 29
15 80-119 30 7 12
16 120 30 10 13
6. 排序
#order默认升序,变量前加“-”代表降序
#排序的操作,大多都是基于索引来完成的
#用order()函数来生成索引,再匹配的数据的数值上面。
可参考↓↓
R语言 排序&去重操作 | 专题1
R语言 | 第一部分:数据预处理
代码语言:javascript复制> row_names <- rep(c("A","B","C"),3)
> col_names <- LETTERS[1:3]
> set.seed(1234)
> df_matrix <- matrix(round(rnorm(27,0,1),3),nrow = 9,dimnames = list(NULL,col_names));df_matrix
A B C
[1,] -1.207 -0.890 -0.837
[2,] 0.277 -0.477 2.416
[3,] 1.084 -0.998 0.134
[4,] -2.346 -0.776 -0.491
[5,] 0.429 0.064 -0.441
[6,] 0.506 0.959 0.460
[7,] -0.575 -0.110 -0.694
[8,] -0.547 -0.511 -1.448
[9,] -0.564 -0.911 0.575
> df_frame <- data.frame(group=row_names,df_matrix);df_frame
group A B C
1 A -1.207 -0.890 -0.837
2 B 0.277 -0.477 2.416
3 C 1.084 -0.998 0.134
4 A -2.346 -0.776 -0.491
5 B 0.429 0.064 -0.441
6 C 0.506 0.959 0.460
7 A -0.575 -0.110 -0.694
8 B -0.547 -0.511 -1.448
9 C -0.564 -0.911 0.575
>
> #order,其中默认升序,变量前加“-”代表降序
> #排序的操作,大多都是基于索引来完成的
> #用order()函数来生成索引,再匹配的数据的数值上面。
> df_frame[order(df_frame$A),]
group A B C
4 A -2.346 -0.776 -0.491
1 A -1.207 -0.890 -0.837
7 A -0.575 -0.110 -0.694
9 C -0.564 -0.911 0.575
8 B -0.547 -0.511 -1.448
2 B 0.277 -0.477 2.416
5 B 0.429 0.064 -0.441
6 C 0.506 0.959 0.460
3 C 1.084 -0.998 0.134
> df_frame[order(df_frame$group,-df_frame$A),]
group A B C
7 A -0.575 -0.110 -0.694
1 A -1.207 -0.890 -0.837
4 A -2.346 -0.776 -0.491
5 B 0.429 0.064 -0.441
2 B 0.277 -0.477 2.416
8 B -0.547 -0.511 -1.448
3 C 1.084 -0.998 0.134
6 C 0.506 0.959 0.460
9 C -0.564 -0.911 0.575
7. 计数
计数,是统计同一个值出现的次数。
代码语言:javascript复制# 生成20个随机数的向量
set.seed(1234)
x<-round(rnorm(20)*5);x
# 统计每个值出现的次数
table(x)
hist(x,xlim = c(-10,13),breaks=5)
代码语言:javascript复制
8 数据分裂
分裂计算,是把一个向量按照一列规则,拆分成多个向量的操作。有时候分裂split也被用于分组计算中。
代码语言:javascript复制> row_names <- rep(c("A","B","C"),3)
> col_names <- LETTERS[1:3]
> df_matrix <- matrix(c(1:27),nrow = 9,dimnames = list(NULL,col_names))
> row_names <- rep(c("A","B","C"),3)
> col_names <- LETTERS[1:3]
> df_matrix <- matrix(c(1:27),nrow = 9,dimnames = list(NULL,col_names));df_matrix
A B C
[1,] 1 10 19
[2,] 2 11 20
[3,] 3 12 21
[4,] 4 13 22
[5,] 5 14 23
[6,] 6 15 24
[7,] 7 16 25
[8,] 8 17 26
[9,] 9 18 27
> df_frame <- data.frame(group=row_names,df_matrix);df_frame
group A B C
1 A 1 10 19
2 B 2 11 20
3 C 3 12 21
4 A 4 13 22
5 B 5 14 23
6 C 6 15 24
7 A 7 16 25
8 B 8 17 26
9 C 9 18 27
> df_split <- split(df_frame,row_names);df_split
$A
group A B C
1 A 1 10 19
4 A 4 13 22
7 A 7 16 25
$B
group A B C
2 B 2 11 20
5 B 5 14 23
8 B 8 17 26
$C
group A B C
3 C 3 12 21
6 C 6 15 24
9 C 9 18 27
另外,可以用因子类型来控制分裂。分成2步操作,第一步先分成与数据集同样长度的因子,第二步进行分裂,可以把一个大的向量拆分成多个小的向量。
代码语言:javascript复制> # 生成因子规则
> n <- 3; size <- 5
> fat <- factor(round(n * runif(n * size)));fat
[1] 3 3 1 1 0 1 2 1 3 3 1 1 2 2 2
Levels: 0 1 2 3
> # 生成数据向量
> x <- rnorm(n * size);x
[1] -1.2107366 -1.3102467 -0.4083354 -0.5629753 1.2139442 1.6288760 -0.3160227 -1.8076242 -0.6125961
[10] -2.1066644 1.2053009 1.3294407 -0.6836288 -1.7868047 0.1364916
> # 对向量以因子的规则进行拆分
> split(x, fat)
$`0`
[1] 1.213944
$`1`
[1] -0.4083354 -0.5629753 1.6288760 -1.8076242 1.2053009 1.3294407
$`2`
[1] -0.3160227 -0.6836288 -1.7868047 0.1364916
$`3`
[1] -1.2107366 -1.3102467 -0.6125961 -2.1066644
9. 去重与找重
去重,是把向量中重复的元素过滤掉。找重,是把向量中重复的元素找出来。
可参考↓↓
R语言 | 第一部分:数据预处理
R语言 排序&去重操作 | 专题1
R 语言 逻辑运算:TRUE/FALSE | 专题3
代码语言:javascript复制> x<-c(3:6,5:8);x
[1] 3 4 5 6 5 6 7 8
# 去掉重复元素
> unique(x)
[1] 3 4 5 6 7 8
# 找到重复元素,索引位置
> duplicated(x)
[1] FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE
# 找到重复元素
> x[duplicated(x)]
[1] 5 6
代码语言:javascript复制10.转置
转置是一个数学名词,把行和列进行互换,一般用于对矩阵的操作。
代码语言:javascript复制# 创建一个3行5列的矩阵
> m<-matrix(1:15,ncol=5);m
[,1] [,2] [,3] [,4] [,5]
[1,] 1 4 7 10 13
[2,] 2 5 8 11 14
[3,] 3 6 9 12 15
# 转置后,变成5行3列的矩阵
> t(m)
[,1] [,2] [,3]
[1,] 1 2 3
[2,] 4 5 6
[3,] 7 8 9
[4,] 10 11 12
[5,] 13 14 15