tidyr用于数据处理,可以实现数据长格式和宽格式之间的相互转换,这里所指的长格式数据就是一个观测对象由多行组成,而宽数据格式则是一个观测仅由一行组成。除此之外,tidyr还可以对数据进行拆分和合并,同时也能够对缺失值进行简单的处理。tidyr的转换函数gather(宽到长)和spread(长到宽)所需参数少,逻辑上更易理解,自始至终都围绕着data,key、value三个参数来进行设定,对比其它R语言长宽格式互换的实现方式,个人认为tidyr操作性还是比较突出的。
1. gather实现wide 到 long 转换
tidyr中的gather函数类似于reshape2中的melt函数,可实现将宽格式数据转换为长数据格式。
gather(data, key, value, ..., na.rm = FALSE, convert = FALSE, factor_key = FALSE) |
require(tidyr)
#将mtcars的所有列聚合成两列
gather(mtcars, attr, value)
#聚合mpg和gear之间的列
gather(mtcars, attr, value, mpg:gear)
#仅聚合mpg和wt变量,其余变量保持不变
gather(mtcars, attr, value, mpg, wt)
require(dplyr)
#添加car列到mtcars中
mtcars$car <- rownames(mtcars)
#除了car列,将mtcars的所有列聚合成两列
gather(mtcars, attr, value, -car)
mtcars %>% gather(attr, value, -car)
#聚合mpg和gear之前的列
mtcars %>% gather(attr, value, mpg:gear)
mtcars %>% gather(mpg:gear, key = "attr", value = "value")
#仅聚合gear和carb变量,其余变量保持不变
mtcars %>% gather(attr ,value ,gear ,carb)
mtcars %>% gather(gear ,carb ,key = "attr", value = "value")
2. spread实现long 到wide转换
tidyr中的spread函数类似于reshape2中的cast函数,可实现将长格式数据转换为宽数据格式。
spread(data, key, value, fill = NA, convert = FALSE, drop = TRUE, sep = NULL) |
require(dplyr)
mtcars$car <- rownames(mtcars)
longformat <- gather(mtcars, attr, value, -car)
#还原长格式为原宽格式数据
spread(longformat, attr, value)
longformat %>% spread(attr, value)
#设置sep,新的列名为的形式
spread(longformat, attr, value, sep = '|')
#还原长格式为原宽格式数据,car列的值转换为每一个列
spread(longformat, car, value)
longformat %>% spread(car, value)
df <- data.frame(x = c("a", "b"), y = c(3, 4), z = c(5, 6))
#转换为宽格式再转换为长格式,实际还原为原df,只是变量顺序不同
df %>% spread(x, y) %>% gather(x, y, a:b, na.rm = TRUE)
df <- data.frame(row = rep(c(1, 51), each = 3),
var = c("Sepal.Length", "Species", "Species_num"),
value = c(5.1, "setosa", 1, 7.0, "versicolor", 2))
#对于混合类型的value列,默认convert = FALSE,转换的新列为factor类型
df %>% spread(var, value)
#设置convert = TRUE,保留原类型
df %>% spread(var, value, convert = TRUE)
3. unite合并多列为一列
tidyr中的unite函数可将多列按指定分隔符合并为一列。
unite(data, col, ..., sep = "_", remove = TRUE) |
library(dplyr)
#使用默认的连接符“_”合并vs和am列为新列vs_am,并删除vs和am列
unite(mtcars, vs_am, vs, am)
mtcars %>% unite(vs_am, vs, am)
#使用默认的连接符“_”合并vs和am列为新列vs_am,保留vs和am列
unite(mtcars, vs_am, vs, am, remove = FALSE)
#使用连接符“|”合并vs和am列为新列vs_am,并删除vs和am列
unite(mtcars, vs_am, vs, am, sep = '|')
4. separate分割一列为多列
tidyr中的separate函数可将一列按分隔符分割为多列,类似于reshape2中的colsplit函数,常用于日期时间类型数据的组合和拆分
separate(data, col, into, sep = "[^[:alnum:]]+", remove = TRUE, convert = FALSE, extra = "warn", fill = "warn", ...) |
library(dplyr)
df <- data.frame(x = c(NA, "a.b", "a.d", "b.c"))
#分割为两列,保留NA值
df %>% separate(x, c("A", "B"))
df <- data.frame(x = c("a", "a b", "a b c", NA))
#分割为两列,发出warning并删除多余的列,缺失的列从右以NA填充
df %>% separate(x, c("a", "b"))
#分割为两列,直接删除多余的列,缺失的列从右以NA填充
df %>% separate(x, c("a", "b"), extra = "drop", fill = "right")
#分割两次(设置的列为两列),缺失的列从左以NA填充
df %>% separate(x, c("a", "b"), extra = "merge", fill = "left")
df <- data.frame(date = c("2017-03-08 01:20:20", "2017-03-09 02:30:30", "2017-03-10 03:40:40"))
#分割为year,month,day,hour,minute,second六列
df %>%
separate(date, c("day", "time"), sep = " ") %>%
separate(day, c("year", "month", "day"), sep = "-") %>%
separate(time, c("hour", "minute", "second"), sep = ":")
与separate不同的是extract使用正则表达式regex提取需要分割的列,当输入为NA或者不能匹配regex时,输出为NA。
extract(data, col, into, regex = "([[:alnum:]]+)", remove = TRUE, convert = FALSE, ...) |
library(dplyr)
df <- data.frame(x = c(NA, "a.b", "a.d", "b.c"))
#分割为两列,regex匹配要分割的列
df %>% extract(x, c("a", "b"), regex = "([a-d]+).([a-d]+)")
df %>% extract(x, c("A", "B"), "([[:alnum:]]+).([[:alnum:]]+)")
#分割为两列,regex匹配要分割的列,不能匹配列的输出为NA
df %>% extract(x, c("a", "b"), regex = "([a-d]+).([a-c]+)")
#分割为一列
df %>% extract(x, c("a"), regex = "([a-d]+)")
df %>% extract(x, c("a"))
separate_rows用于将列分割为多行,”... ”用于设置需要分割的列,sep用于设置分隔符,需要注意的是分割多个列时,每个列分割成的行数必须要一致。
separate_rows(data, ..., sep = "[^[:alnum:].]+", convert = FALSE) |
library(dplyr)
df <- data.frame(
x = 1:3,
y = c("a", "d,e,f", "g,h"),
z = c("1", "2,3,4", "5,6"),
stringsAsFactors = FALSE
)
#分割y和z列,转换为行
separate_rows(df, y, z, convert = TRUE)
5. 嵌套和嵌套还原
所谓的嵌套函数可用于将指定列的对应元素‘折叠’为list,缩小原有数据框的大小。Tidyr提供两个函数进行嵌套合并操作,nest()将分组数据框转换为嵌套数据框,即包含列表列的数据框,转换后分组列只会保留每个值的唯一值,与之对应合并列中的所有值会合并为list对象生成新列,这里需要注意的是所有合并列都会合并在一起生成一列,chop()函数稍微有些区别,合并列会分别进行合并,而不是全部整合在一起。
#所有合并列全部一起nest为新列 nest(.data, ..., .key = deprecated()) #合并列分别进行nest chop(data, cols) |
# 将除Species的列进行nest操作,生成的新列的元素都为list对象
# 其中nest()合并为一列,chop()分别对各列进行合并
iris %>% nest(data = -Species)
iris %>% chop(cols = -Species)
nest_vars <- names(iris)[1:4]
iris %>% nest(data = one_of(nest_vars))
iris %>% chop(cols = one_of(nest_vars))
# 分别组合多列进行合并,chop()不支持此操作
iris %>% nest(petal = starts_with("Petal"), sepal = starts_with("Sepal"))
# 先进行分组操作,分组变量之外的变量会进行nest合并
iris %>% group_by(Species) %>% nest()
# 对不同分组建立线性模型
mtcars %>%
group_by(cyl) %>%
nest() %>%
mutate(models = lapply(data, function(df) lm(mpg ~ wt, data = df)))
与之相反,unnest()函数和unchop()可以进行数据框展开,把折叠的list对象展开。
#unnest数据框对象 unnest(data, cols, ..., keep_empty = FALSE, ptype = NULL, names_sep = NULL, names_repair = "check_unique", .drop = deprecated(), .id = deprecated(), .sep = deprecated(), .preserve = deprecated())
unchop(data, cols, keep_empty = FALSE, ptype = NULL) |
df <- tibble(
x = 1:4,
y = list(NULL, 1:2, 3, NULL),
z = list('d', c('a', "b"), "c", NULL)
)
# 展开y和z列,并且删除值都为NULL的行,不全为NULL的行保留
df %>% unnest(c(y, z))
df %>% unchop(c(y, z))
# 展开y和z列,设置keep_empty = TRUE保留所有行
df %>% unnest(c(y, z), keep_empty = TRUE)
df %>% unchop(c(y, z), keep_empty = TRUE)
# 展开y和z列,设置展开后列的类型
df %>% unchop(c(y, z), ptype = tibble(y = character(), z = character()))
# 先使用y列展开,在使用z展开,和直接使用y,z展开不同,y和z会进行笛卡尔积
df %>% unchop(y) %>% unchop(z)
6. 缺失值处理
tidyr包提供了简单的缺失值处理方法,包括替换,填充,删除等。
#使用给定值替换每列的缺失值 replace_na(data, replace = list(), ...) |
library(dplyr)
df <- tibble(x = c(1, 2, NA), y = c("a", NA, "b"))
#以0替换x中的NA,以unknown替换y中的NA
df %>% replace_na(list(x = 0, y = "unknown"))
#以前一个值填充缺失值,默认自上向下填充 fill(data, ..., .direction = c("down", "up")) |
df <- data.frame(x = 1:5, y = c(10, NA, 15, NA, 20))
#自上向下替换NA值
df %>% fill(y)
df %>% fill(y, .direction = "down")
#自下向上替换NA值
df %>% fill(y, .direction = "up")
#填充以创建完整的序列值向量 full_seq(x, period, tol = 1e-06) |
#返回序列1:6
full_seq(c(1, 2, 4, 6), 1)
#period值与原数据间隔不匹配,报错Error: `x` is not a regular sequence.
full_seq(c(1, 2, 4, 6), 2)
#返回序列1:13,间隔为2
full_seq(c(1, 5, 9, 13), 2)
#删除包含缺失值的行 drop_na(data, ...) |
df <- data_frame(x = c(1, 2, NA), y = c("a", NA, "b"))
#删除变量x中NA对应的行
df %>% drop_na(x)
#删除变量y中NA对应的行
df %>% drop_na(y)
#未设置列,删除变量x和y中NA对应的行
df %>% drop_na()
#转换隐式的缺失值为显式的 complete(data, ..., fill = list()) |
df <- data_frame(
group = c(1:2, 1),
item_id = c(1:2, 2),
item_name = c("a", "b", "b"),
value1 = 1:3,
value2 = 4:6
)
#以item_id和item_name中的每个元素扩展原数据框, 组合后的缺失值以NA代替
df %>% complete(item_id, item_name)
df %>% complete(crossing(item_id, item_name))
#以item_id和item_name中的每个元素扩展原数据框, 并以给定值替换缺失值
df %>% complete(item_id, item_name, fill = list(group = 0, value1 = 0, value2 = 0))
#以item_id和item_name中的每个元素扩展原数据框,只返回原数据框中存在的组合
df %>% complete(nesting(item_id, item_name))
#保留group,以item_id和item_name中的每个元素扩展原数据框,只返回原数据框
#中item_id和item_name存在的组合
df %>% complete(group, nesting(item_id, item_name))
df %>% complete(group, nesting(item_id, item_name), fill = list(value1 = 0, value2 = 0))
#保留group,以item_id和item_name中的每个元素扩展原数据框,返回所有item_id
#和item_name存在的组合
df %>% complete(group, crossing(item_id, item_name))
df %>% complete(group, crossing(item_id, item_name), fill = list(value1 = 0, value2 = 0))