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

Split Column, convert resulting columns to factors

我的目标是将一个由 1 和 0 组成的字符列拆分成它们自己的列。我希望新列的类型为“因素”。我找到了一种拆分列的好方法(使用 dplyr 的“mutate”和 reshape2 的“colsplit”),但未能找到一种有效的方法来使所有结果列的类型为“factor”。

这是我的问题的一个最小示例:

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 

此示例生成正确的 table,只是我希望新列成为因子(下游脚本需要)。我还希望新列不使用有关旧列的信息命名,我认为这是 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 长)。

这是使用 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", into = names, 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-05-15 创建

我不太确定你的要求,但你可以做这样的事情(稍微改变你的 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', sep = '', 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