问题描述
我的目标是将包含 1 和 0 的字符列拆分为各自的列。我希望新列的类型为“因子”。我找到了一种拆分列的好方法(使用 dplyr 的“mutate”和 reshape2 的“colsplit”),但无法找到一种有效的方法让所有结果列都属于“因子”类型。
这是我的问题的一个最小示例:
library(dplyr)
library(reshape2)
# Data frame to be processed
df = tribble(
~x,~y,~z,"Alpha","1111","Alp","Beta","1001","Bet"
)
# Vector Containing Names for columns
names = c("A","B","C","D")
df %>%
mutate_at("y",colsplit,names = names,pattern = "")
输出:
# A tibble: 2 x 3
x y$A $B $C $D z
<chr> <int> <int> <int> <int> <chr>
1 Alpha 1 1 1 1 Alp
2 Beta 1 0 0 1 Bet
此示例生成正确的表,但我希望新列是因子(下游脚本需要)。我还希望新列的名称不包含有关旧列的信息,我认为这是 colsplit
的一个特性——指的是 y$
和 $
部分。
目前,让所有列都成为因子的唯一方法是手动编辑它们,这是相当低效的。
我尝试过的其他解决方案:
我也尝试使用 tidyverse 中的 separate
解决这个问题,但无法正确划分。我不明白它使用的是什么正则表达式。例如,这段代码:
df %>%
separate("y",into = names,sep = "")
结果如下:
# A tibble: 2 x 6
x A B C D z
<chr> <chr> <chr> <chr> <chr> <chr>
1 Alpha "" 1 1 1 Alp
2 Beta "" 1 0 0 Bet
哪个似乎是在选择字符串之前的第一个空格?我不太确定这是怎么回事。
此外,实际上,列 y
中的字符串可以是不同的长度(但在正在处理的数据集中将具有一致的大小——例如,y 可以是 100 个字符长,它会是每行 100 长)。
解决方法
我不太确定你的要求,但你可以做这样的事情(稍微改变你的 df,以表明不需要对 colnames 进行硬编码)
df = tribble(
~x,~y,~z,"Alpha","1111346767909","Alp","Beta","1001","Bet"
)
# A tibble: 2 x 3
x y z
<chr> <chr> <chr>
1 Alpha 1111346767909 Alp
2 Beta 1001 Bet
df %>%
separate(y,into = paste0('y_',seq_len(1 + max(nchar(.$y)))-1),sep = '',fill = 'right' ) %>%
select(!ends_with('_0')) %>%
mutate(across(starts_with('y_'),factor))
# A tibble: 2 x 15
x y_1 y_2 y_3 y_4 y_5 y_6 y_7 y_8 y_9 y_10 y_11 y_12 y_13 z
<chr> <fct> <fct> <fct> <fct> <fct> <fct> <fct> <fct> <fct> <fct> <fct> <fct> <fct> <chr>
1 Alpha 1 1 1 1 3 4 6 7 6 7 9 0 9 Alp
2 Beta 1 0 0 1 NA NA NA NA NA NA NA NA NA Bet
,
您可以使用 splitstackshape::cSplit
:
library(dplyr)
splitstackshape::cSplit(df,'y',stripWhite = FALSE) %>%
mutate(across(starts_with('y'),factor)) %>%
rename_with(~names,starts_with('y'))
# x z A B C D
#1: Alpha Alp 1 1 1 1
#2: Beta Bet 1 0 0 1
,
这是一种使用 dplyr
& tidyr
library(dplyr)
library(tidyr)
# Create a names vector that dynamic base on length of y index from 1 to max length
# As "" feed to separate as separator so the first matched is an empty char
# for this case we add a column to be dropped later into the names list.
names <- c("drop",seq_len(max(sapply(df$y,nchar))))
df %>%
separate("y",into = names,sep = "",fill = "warn") %>%
mutate_if(.predicate = is.character,.funs = factor) %>%
select(-drop)
#> # A tibble: 2 x 6
#> x `1` `2` `3` `4` z
#> <fct> <fct> <fct> <fct> <fct> <fct>
#> 1 Alpha 1 1 1 1 Alp
#> 2 Beta 1 0 0 1 Bet
或者另一种只将 names
中出现的列转换为因子的方法
df %>%
separate("y",sep = "") %>%
mutate_at(vars(one_of(names)),.funs = factor) %>%
select(-drop)
#> # A tibble: 2 x 6
#> x `1` `2` `3` `4` z
#> <chr> <fct> <fct> <fct> <fct> <chr>
#> 1 Alpha 1 1 1 1 Alp
#> 2 Beta 1 0 0 1 Bet
由 reprex package (v2.0.0) 于 2021 年 5 月 15 日创建