tidyr
基础用法
gather&&spread
可以将本来扁平的数据框变为宽长的数据框。扁平(两个维度对应一个数据)。
代码语言:javascript复制# 先生成一个原始的数据
> test <- data.frame(geneid = paste0("gene",1:4),
sample1 = c(1,4,7,10),
sample2 = c(2,5,0.8,11),
sample3 = c(0.3,6,9,12))
> test
geneid sample1 sample2 sample3
1 gene1 1 2.0 0.3
2 gene2 4 5.0 6.0
3 gene3 7 0.8 9.0
4 gene4 10 11.0 12.0
很显然, data.frame
生成的是“扁平”的数据框。
> test_gather <- gather(data = test,
key = sample_nm,
value = exp,
- geneid)
> head(test_gather)
geneid sample_nm exp
1 gene1 sample1 1
2 gene2 sample1 4
3 gene3 sample1 7
4 gene4 sample1 10
5 gene1 sample2 2
6 gene2 sample2 5
通过 gather
,并设定key(原先的列),与value(原先的数据),并通过 -
(原先的行),对数据框进行转换。
宽长(一个维度对应一个数据)。
代码语言:javascript复制> test_re <- spread(data = test_gather,
key = sample_nm,
value = exp)
> test_re
geneid sample1 sample2 sample3
1 gene1 1 2.0 0.3
2 gene2 4 5.0 6.0
3 gene3 7 0.8 9.0
4 gene4 10 11.0 12.0
通过spread ,设定key(希望转换的列),value(希望转换的数据)。也就回到了开始创建的数据框test。
separate&&unite
将同一列中的内容分为两列内容。或将两列内容合并为同一列内容。
首先还是可以创建一个数据框。
代码语言:javascript复制> test <- data.frame(x = c( "a,b", "a,d", "b,c"));test
x
1 a,b
2 a,d
3 b,c
使用separate,便可以对一列中的数据达到“分离”的效果。对于待分离的对象(col),不必加上引号;但对于即将创建的新列(into),需要使用引号,由于是两列,这里使用向量创建。sep参数设定读取表格信息时以何符号作为分隔符。
代码语言:javascript复制> test_seprate <- separate(test,col = x, into = c("X", "Y"),sep = ",");test_seprate
X Y
1 a b
2 a d
3 b c
使用unite,可以将两列“合并”为一列。对于即将合并的新列,需要使用引号;但对于想要合并的多个列名,可以不用使用引号。sep 参数设定多列合并后不同数据分隔使用的分割符。
代码语言:javascript复制> test_re <- unite(test_seprate,col = "x",X,Y,sep = ",");test_re
x
1 a,b
2 a,d
3 b,c
引号 yes or not?
到底需不需要引号,对于要处理的列(无论分离还是合并)不用;对于待生成的列则需要。
处理缺失值
创建一个存在NA 的数据框。
代码语言:javascript复制X<-data.frame(X1 = LETTERS[1:5],X2 = 1:5)
X[2,2] <- NA
X[4,1] <- NA
> X
X1 X2
1 A 1
2 B NA
3 C 3
4 <NA> 4
5 E 5
直接去除 drop_na
如果直接对数据框进行 drop_na
其效果和基础包中的 na.omit()
是一样的,会将存在缺失值的行直接删除。如果其后加上参数(列名),则会针对该列进行去除缺失值。
drop_na(X,X2)
> drop_na(X,X2)
X1 X2
1 A 1
2 C 3
3 <NA> 4
4 E 5
替换 replace_na&&fill
通过replace_na,可以将 replace_na(col, value)
,将col 中的NAs 替换为指定的value。
> X$X2 <- replace_na(X$X2,0);X
X1 X2
1 A 1
2 B 0
3 C 3
4 <NA> 4
5 E 5
# 还可以写成
X$X2 <- replace_na(list(X2=0))
通过fill,可以将指定列中的缺失值替换为该缺失值所在行的上一行中的数据。
代码语言:javascript复制> X
X1 X2
1 A 1
2 B NA
3 C 3
4 <NA> 4
5 E 5
> fill(X,X2)
X1 X2
1 A 1
2 B 1
3 C 3
4 <NA> 4
5 E 5
> fill(X,X1,X2)
X1 X2
1 A 1
2 B 1
3 C 3
4 C 4
5 E 5
dplyr
是用于处理数据框的关键的一个包。
代码语言:javascript复制library(dplyr)
test <- iris[c(1:2,51:52,101:102),]
rownames(test) =NULL
必备dplyr技巧
mutate
新增一列。
代码语言:javascript复制mutate(test, new = Sepal.Length * Sepal.Width)
> head(test,3)
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 7.0 3.2 4.7 1.4 versicolor
new
1 17.85
2 14.70
3 22.40
select
按列筛选。
按列号
代码语言:javascript复制select(test,1)
select(test,c(1,5))
按列名
如果想要用向量来存放希望筛选的列名,需要使用函数 one_of
来存放该向量。
select(test,Sepal.Length)
select(test, Petal.Length, Petal.Width)
vars <- c("Petal.Length", "Petal.Width")
select(test, one_of(vars))
其他专属函数
select 被设计了许多特别的函数。
代码语言:javascript复制select(test, starts_with("Petal")) #选中..开头的列
select(test, ends_with("Width")) #选中..结尾的列
select(test, contains("etal")) #选中包含..的列
select(test, matches(".t.")) #选中符合某正则表达的列
select(test, everything()) #选中所有列
select(test, last_col()) #选中最后一列
select(test, last_col(offset = 1)) #选中倒数第二列。offset 表示忽略n个。忽略最后一个即表示选择倒数第二个。
everything
可以实现对列的自定义排序。其语法逻辑为,去掉指定的列后,筛选其他的列。因此我们可以对select 与everything 处理,先筛选某列,接着去掉该列后,对其他列取everything,便可以将先筛选的列顺序提到最前。
select(test,Species,everything())
filter
使用逻辑条件对行筛选。
代码语言:javascript复制filter(test, Species == "setosa")
filter(test, Species == "setosa"&Sepal.Length > 5 )
filter(test, Species %in% c("setosa","versicolor"))
arrange
按照数据框里的某列或某几列,对所有行进行排序。可以使用 desc
产生倒序,或写入多个列使其按照多个列进行排序。
arrange(test, Sepal.Length)#默认从小到大排序
arrange(test, desc(Sepal.Length))#用desc从大到小
arrange(test, desc(Sepal.Width),Sepal.Length) #先按照一列排,再按照一列排
summarize
汇总。使用统计相关参数计算列表内相关内容。如sum, mean, median, min, max。
代码语言:javascript复制summarize(test, mean(Sepal.Length), sd(Sepal.Length))
group_by
group_by 按照某列对数据框进行分组,非常适合联合summarize 使用,获取指定组别不同类型内容的统计数值。
代码语言:javascript复制group_by(test, Species)
tmp = summarise(group_by(test, Species),mean(Sepal.Length), sd(Sepal.Length))
> tmp
# A tibble: 3 x 3
Species `mean(Sepal.Length)` `sd(Sepal.Length)`
<fct> <dbl> <dbl>
1 setosa 5 0.141
2 versicolor 6.7 0.424
3 virginica 6.05 0.354
pull
相当于专门取数据框列的函数:
代码语言:javascript复制> pull(g, sample1)
[1] 4.498195 3.871712 9.152436 3.468464
> identical(pull(g, sample1), g$sample1)
[1] TRUE
小进阶
count
计算向量或数据框中某列的重复值,并返回不同信息及它们重复的次数。
代码语言:javascript复制> count(test,Species)
Species n
1 setosa 2
2 versicolor 2
3 virginica 2
%>%
存在于tidyverse每一个包里。快捷键为 ctrl shift M
。管道操作,类似linux 中的 |
,即将上一步内容的结果重定向作为下一步内容输入的值。
library(dplyr)
x1 = filter(iris,Sepal.Width>3)
x2 = select(x1,c("Sepal.Length","Sepal.Width" ))
x3 = arrange(x2,Sepal.Length)
如果依靠变量的传递,每一步都需要将结果指定若干个中间变量,再将指定的这些中间变量,作为输入值传递给下一个值。但从结果来看,它们并没有必要。
代码语言:javascript复制iris %>%
filter(Sepal.Width>3) %>%
select(c("Sepal.Length","Sepal.Width" ))%>%
arrange(Sepal.Length)
dplyr 处理关系数据
即通过dplyr 包将表格进行连接。在基础包中, merge
及 bind
可以帮助我们连接表格。参见:https://www.yuque.com/mugpeng/rr/nhhess#AoSx0但它们缺陷也很明显, rbind
(bind_rows)或 cbind
(bind_cols)只能非常生硬地将相同列或相同行的表格“压”在一起;而 merge
也只能按照共有部分相连接,两个表格中均不存在的行的内容会被删去。
而dplyr 也提供了更为全面的表格连接的函数—— join
系列。
便于测试,首先生成三个数据集
代码语言:javascript复制test1 <- data.frame(name = c('jimmy','nicker','doodle'),
blood_type = c("A","B","O"))
> test1
name blood_type
1 jimmy A
2 nicker B
3 doodle O
test2 <- data.frame(name = c('doodle','jimmy','nicker','tony'),
group = c("group1","group1","group2","group2"),
vision = c(4.2,4.3,4.9,4.5))
> test2
name group vision
1 doodle group1 4.2
2 jimmy group1 4.3
3 nicker group2 4.9
4 tony group2 4.5
test3 <- data.frame(NAME = c('doodle','jimmy','lucy','nicker'),
weight = c(140,145,110,138))
> test3
NAME weight
1 doodle 140
2 jimmy 145
3 lucy 110
4 nicker 138
inner_join 取交集
inner_join 和merge 的功能很像,都是取交集。使用merge:
代码语言:javascript复制> merge(test1,test3,by.x = "name",by.y = "NAME")
name blood_type weight
1 doodle O 140
2 jimmy A 145
3 nicker B 138
使用inner_join:
代码语言:javascript复制> inner_join(test1,test3,by = c("name"="NAME"))
name blood_type weight
1 jimmy A 145
2 nicker B 138
3 doodle O 140
共同列列名不一致时,inner_join 的语法似乎“有点奇怪”。
left_join&&right_join
左连(按照左边,保留所有左边数据),右连(按照右边,保留所有右边数据)。其中另外一边中缺失的数据用NA 填充。
代码语言:javascript复制> left_join(test2, test1, by = 'name')
name group vision blood_type
1 doodle group1 4.2 O
2 jimmy group1 4.3 A
3 nicker group2 4.9 B
4 tony group2 4.5 <NA>
full_join 取全集
不管左边右边,有的全连上,缺失用NA 补充。
代码语言:javascript复制> full_join(test2, test3, by = c("name"="NAME"))
name group vision weight
1 doodle group1 4.2 140
2 jimmy group1 4.3 145
3 nicker group2 4.9 138
4 tony group2 4.5 NA
5 lucy <NA> NA 110
semi_join 半连接&&anti_join 反连接
半连接返回的是x所有的在y中存在的记录。
代码语言:javascript复制semi_join(x = test1, y = test2, by = 'name')
反连接与半连接相反,返回的是x中所有的在y中不存在的记录。
代码语言:javascript复制anti_join(x = test2, y = test1, by = 'name')
易错点
- 处理“宽长”型数据框时(gather处理生成的),该数据库需要存在某个“索引列”,可以保证其对应唯一的某行内容的信息。(或通过group_by与mutate 自行添加索引)
- 进行separate 时,要注意特殊符号的用法,其可能存在正则用法,需要进行转义。
- 如果分隔出的结果存在0的话,会自动识别为NA。
练习题
6-1
代码语言:javascript复制# 练习6-1
library(tidyverse)
# 1.将iris数据框的前4列gather,然后还原
test <- iris[,1:4]
x_gather <- gather(data=test,key=var, value=num)
head(x_gather)
# 还原
# 错误答案,由于建立的gather 缺乏有效索引,因此会报错。
# 缺乏一个唯一确定该数据的变量。
# x_spread <- spread(test, key=var, value=num)
# 通过mutate 会表格添加一列索引列。
x_spread <- x_gather %>%
group_by(var) %>%
mutate(id=1:n()) %>%
spread(var,num)
x_spread
# 2.将第三列分成两列(以小数点为分隔符)然后合并
head(iris)
# 错误答案由于sep 会识别正则(. 表示任意字符),因此需要对. 转义。
# iris_sep <- separate(iris, Petal.Length, c('Petal.Length_1','Petal.Length_2'),
# sep='.')
# 这里还是会存在一个bug,如果数字. 后面部分为0,分割后会识别为NA。
iris_sep <- iris %>%
separate(Petal.Length, into = c('Petal.Length_1','Petal.Length_2'),
sep='[.]')
iris_sep$Petal.Length_2 <- replace_na(iris_sep$Petal.Length_2,0)
tail(iris_sep)
# NA 都已经消除了。
iris_unite <- unite(iris_sep, col='Petal.Length',Petal.Length_1,Petal.Length_2,
sep='.')
tail(iris_unite)
str(iris_unite)
# 这里发现Petal.Length 格式为chr。
iris_unite['Petal.Length'] = as.numeric(iris_unite['Petal.Length'])
# 3.加载test1.Rdata,将deg数据框按照pvalue从小到大排序
load('test1.Rdata')
str(deg)
deg_sort <- arrange(deg, P.Value)
# 4. 将两个数据框按照probe_id列连接在一起
deg_join <- inner_join(deg, ids, by = 'probe_id')
head(deg_join)