拆分列,将结果列转换为因子

问题描述

我的目标是将包含 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 日创建