6.5 separate() and untie()
separate() 和 untie() 函数则是为了解决以下问题:多个变量挤在了同一列中,或者一个变量分散到了不同列中。
6.5.1 separate()
table3
#> # A tibble: 6 x 3
#> country year rate
#> * <chr> <int> <chr>
#> 1 Afghanistan 1999 745/19987071
#> 2 Afghanistan 2000 2666/20595360
#> 3 Brazil 1999 37737/172006362
#> 4 Brazil 2000 80488/174504898
#> 5 China 1999 212258/1272915272
#> 6 China 2000 213766/1280428583在 table3 中,rate 同时包含了 cases 和 population 两个变量,我们需要把它拆分(separate)为两列,separate() 函数可以将这一混杂的列拆分成多个变量,它包含以下四个主要参数:
* data: 需要调整的数据框
* col: 需要进行拆分的列的列名
* into: 拆分后新生成变量的列名,格式为字符串向量
* sep: 对如何拆分原变量的描述,其可以是正则表达式,如 _ 表示通过下划线拆分,或 [^a-z] 表示通过任意非字符字母拆分,或一个指定位置的整数。默认情况下,sep将认定一个非字符字母进行划分
# 这个例子里,sep 不是必需的
table3 %>%
separate(col = rate,into = c("cases","population"))
#> # A tibble: 6 x 4
#> country year cases population
#> <chr> <int> <chr> <chr>
#> 1 Afghanistan 1999 745 19987071
#> 2 Afghanistan 2000 2666 20595360
#> 3 Brazil 1999 37737 172006362
#> 4 Brazil 2000 80488 174504898
#> 5 China 1999 212258 1272915272
#> 6 China 2000 213766 1280428583整理的图示:

注意,以上输出的tibble中,cases 和 population被设定为字符串类型,使用convert = T将其转换为数值变量
table3 %>%
separate(rate, into = c("cases", "population"), convert = T)
#> # A tibble: 6 x 4
#> country year cases population
#> <chr> <int> <int> <int>
#> 1 Afghanistan 1999 745 19987071
#> 2 Afghanistan 2000 2666 20595360
#> 3 Brazil 1999 37737 172006362
#> 4 Brazil 2000 80488 174504898
#> 5 China 1999 212258 1272915272
#> 6 China 2000 213766 1280428583A seemingly similar function tidyr::separate_rows() separate existing columns based on sep, and then breaks each component into new rows, instead of columns:
df <- tibble(
x = 1:3,
y = c("a", "d,e,f", "g,h"),
z = c("1", "2,3,4", "5,6")
)
df %>% separate_rows(y, z, sep = ",")
#> # A tibble: 6 x 3
#> x y z
#> <int> <chr> <chr>
#> 1 1 a 1
#> 2 2 d 2
#> 3 2 e 3
#> 4 2 f 4
#> 5 3 g 5
#> 6 3 h 6The multiple choice data mentioned in 6.3.2 can easily be solved when using separate_rows():
6.5.2 unite()
unite() 是 separate() 的逆运算——它可以将多列合并为一列.
在 table5 中,原来的 year 变量被拆成了两个列,可以用 unite(),只需要指定要合并后的列名和要合并的列。默认情况下,新列中将用_分隔符
table5
#> # A tibble: 6 x 4
#> country century year rate
#> * <chr> <chr> <chr> <chr>
#> 1 Afghanistan 19 99 745/19987071
#> 2 Afghanistan 20 00 2666/20595360
#> 3 Brazil 19 99 37737/172006362
#> 4 Brazil 20 00 80488/174504898
#> 5 China 19 99 212258/1272915272
#> 6 China 20 00 213766/1280428583
unite(table5, col = year, century, year)
#> # A tibble: 6 x 3
#> country year rate
#> <chr> <chr> <chr>
#> 1 Afghanistan 19_99 745/19987071
#> 2 Afghanistan 20_00 2666/20595360
#> 3 Brazil 19_99 37737/172006362
#> 4 Brazil 20_00 80488/174504898
#> 5 China 19_99 212258/1272915272
#> 6 China 20_00 213766/1280428583sep = "" 可以取消分隔符:
unite(table5, col = year, century, year, sep="")
#> # A tibble: 6 x 3
#> country year rate
#> <chr> <chr> <chr>
#> 1 Afghanistan 1999 745/19987071
#> 2 Afghanistan 2000 2666/20595360
#> 3 Brazil 1999 37737/172006362
#> 4 Brazil 2000 80488/174504898
#> 5 China 1999 212258/1272915272
#> 6 China 2000 213766/1280428583整理的图示:

table6 <- unite(table5,col = year,century,year,sep="")
table5
#> # A tibble: 6 x 4
#> country century year rate
#> * <chr> <chr> <chr> <chr>
#> 1 Afghanistan 19 99 745/19987071
#> 2 Afghanistan 20 00 2666/20595360
#> 3 Brazil 19 99 37737/172006362
#> 4 Brazil 20 00 80488/174504898
#> 5 China 19 99 212258/1272915272
#> 6 China 20 00 213766/12804285836.5.3 Exercises
separate()中的extra和fill参数的作用是什么?用下面两个数据框进行实验:
tibble(x = c("a,b,c", "d,e,f,g", "h,i,j")) %>%
separate(x, into = c("one", "two", "three"))
#> # A tibble: 3 x 3
#> one two three
#> <chr> <chr> <chr>
#> 1 a b c
#> 2 d e f
#> 3 h i j
tibble(x = c("a,b,c", "d,e", "f,g,i")) %>%
separate(x, into = c("one", "two", "three"))
#> # A tibble: 3 x 3
#> one two three
#> <chr> <chr> <chr>
#> 1 a b c
#> 2 d e <NA>
#> 3 f g iextra 用来告诉 separate() 函数如何处理分列过程中多出来的元素(too many pieces,即 into 指定的列数小于原数据中某行可分的元素个数),fill 负责如何处理元素不够的情况(not enough pieces,即into指定的列数大于原数据中某行可分的元素个数)。默认情况下,extra = "drop",separate() 将丢弃多余的元素,并生成一条警告信息:
tibble(x = c("a,b,c", "d,e,f,g", "h,i,j")) %>%
separate(x, into = c("one", "two", "three"), extra = "drop")
#> # A tibble: 3 x 3
#> one two three
#> <chr> <chr> <chr>
#> 1 a b c
#> 2 d e f
#> 3 h i jextra = "merge"将把多余的元素和前一个元素当做一个整体:
tibble(x = c("a, b, c", "d, e, f, g", "h, i, j")) %>%
separate(x, c("one", "two", "three"), extra = "merge")
#> # A tibble: 3 x 3
#> one two three
#> <chr> <chr> <chr>
#> 1 a b c
#> 2 d e f, g
#> 3 h i j对于元素过少的情况,默认的fill = "warn"将会用NA进行填充,但会生成一条警告。fill = "right"会尽可能让靠左的列拥有可用的元素,用NA填充右边的列;fill = "left"正好相反。这两种手动设置都不会产生warning:
tibble(x = c("a, b, c", "d, e, ", "h, i, j")) %>%
separate(x, c("one", "two", "three"), fill = "left")
#> # A tibble: 3 x 3
#> one two three
#> <chr> <chr> <chr>
#> 1 a b "c"
#> 2 d e ""
#> 3 h i "j"
tibble(x = c("a,b,c", "d, e, ", "h, i, j")) %>%
separate(x, c("one", "two", "three"),fill = "right")
#> # A tibble: 3 x 3
#> one two three
#> <chr> <chr> <chr>
#> 1 a b "c"
#> 2 d e ""
#> 3 h i "j"2.
unite()和separate()均有一个remove参数,它的作用是什么?
remove控制是否在unite()或separate()输出的数据框中保留原来的列,默认remove = T。如果想保留原来未合并/分离的格列,可以设置remove = F
table5
#> # A tibble: 6 x 4
#> country century year rate
#> * <chr> <chr> <chr> <chr>
#> 1 Afghanistan 19 99 745/19987071
#> 2 Afghanistan 20 00 2666/20595360
#> 3 Brazil 19 99 37737/172006362
#> 4 Brazil 20 00 80488/174504898
#> 5 China 19 99 212258/1272915272
#> 6 China 20 00 213766/1280428583
table5 %>% unite(col = year_unite,century,year,sep = "",remove = F)
#> # A tibble: 6 x 5
#> country year_unite century year rate
#> <chr> <chr> <chr> <chr> <chr>
#> 1 Afghanistan 1999 19 99 745/19987071
#> 2 Afghanistan 2000 20 00 2666/20595360
#> 3 Brazil 1999 19 99 37737/172006362
#> 4 Brazil 2000 20 00 80488/174504898
#> 5 China 1999 19 99 212258/1272915272
#> 6 China 2000 20 00 213766/1280428583separate() 类似的函数 extract() 的用法
separate()函数的分列操作是基于参数 sep 的,无论是给 sep 传入字符串指定分隔符,还是用数值指定分隔的位置,separate() 必须要有一个分隔符才能正常运作(可以把sep = n看做第 n 个和第 n+1 个元素之间的一个空白分隔符)
extract()用一个正则表达式regex描述要分隔的列col中存在的模式,在正则表达式中的每个子表达式(用()定义)将被认为是into中的一个元素,因此,extract()比separate()使用起来更加广泛灵活。例如下面的数据集无法用separate()分列,因为无法用一个各行的分隔符(的位置)不一样,但用extract()中的正则表达式就很简单:
tibble(x = c("X1", "X20", "AA11", "AA2")) %>%
extract(x, c("variable", "id"), regex = "([A-Z]+)([0-9]+)")
#> # A tibble: 4 x 2
#> variable id
#> <chr> <chr>
#> 1 X 1
#> 2 X 20
#> 3 AA 11
#> 4 AA 2适当设计regex,实现的效果可以与设置sep完全一致:
# example with separators
tibble(x = c("X_1", "X_2", "AA_1", "AA_2")) %>%
extract(x, c("variable", "id"), regex = "([A-Z]+)_([0-9])")
#> # A tibble: 4 x 2
#> variable id
#> <chr> <chr>
#> 1 X 1
#> 2 X 2
#> 3 AA 1
#> 4 AA 2
# example with position
tibble(x = c("X1", "X2", "Y1", "Y2")) %>%
extract(x, c("variable", "id"), regex = "([A-Z])([0-9])")
#> # A tibble: 4 x 2
#> variable id
#> <chr> <chr>
#> 1 X 1
#> 2 X 2
#> 3 Y 1
#> 4 Y 2