R - 对每个字母因子的列分割字母数字字符观察值,每个观察值为数值

时间:2018-05-23 20:44:33

标签: r regex string

我不太确定如何最好地标题我想做什么。

我有一个如下所示的数据框:

 ID = c(1, 2, 3, 4, 5, 6, 7)
 observation = c("a2", NA, "b3", "c5", NA, "b", "a3")
 df <- data.frame(cbind(ID, observation))

 df

  ID observation
1  1          a2
2  2        <NA>
3  3          b3
4  4          c5
5  5        <NA>
6  6           b
7  7          a3

我想要的输出是一个数据框,它按数字和字母分割观察结果,每个唯一字母都有一个新列,其中每一行包含该字母的相关观察编号。

所需的输出应如下所示:

desired_df <- data.frame(cbind(ID, a = c(2, NA, 0, 0, 0 , 0, 3), 
                                   b = c(0, NA, 3, 0, 0, 0, 0),
                                   c = c(0, NA, 0, 5, 0, 0, 0)))
desired_df

  ID  a  b  c
1  1  2  0  0
2  2 NA NA NA
3  3  0  3  0
4  4  0  0  5
5  5  0 NA NA
6  6  0  0  0
7  7  3  0  0

我尝试通过将观察结果分成带有正则表达式的字母和数字并将结果保存到新列中来尝试解决此问题:

library(stringr)
char <- unlist(str_replace_all(observation, "[[:digit:]]", ""))
num <- unlist(str_extract(observation, "[[:digit:]]"))
df_new <- cbind(ID, char, num)
df_new

  ID char  num
1  1    a    2
2  2 <NA> <NA>
3  3    b    3
4  4    c    5
5  5 <NA> <NA>
6  6    b <NA>
7  7    a    3

然后尝试根据此SO Question

的答案将char转换为二进制形式的因子
df_new <- data.frame(cbind(df, sapply(levels(as.factor((char))), 
function(x) as.integer(x == char))))

  ID char  num  a  b  c
1  1    a    2  1  0  0
2  2 <NA> <NA> NA NA NA
3  3    b    3  0  1  0
4  4    c    5  0  0  1
5  5 <NA> <NA> NA NA NA
6  6    b <NA>  0  1  0
7  7    a    3  1  0  0

然后,我尝试根据此SO question的答案,用该行的df_new1 $ num中的相应值替换每个1个观察值:

df_new2 <- data.frame(with(df_new1, ifelse(df_new1 == 1, df_new1$num, 0)))

df_new2
  ID char num  a  b  c
1  1    0   0  1  0  0
2  0   NA  NA NA NA NA
3  0    0   0  0  2  0
4  0    0   0  0  0  3
5  0   NA  NA NA NA NA
6  0    0  NA  0 NA  0
7  0    0   0  2  0  0

哪个输出错误的结果。我一直在努力解决这个问题。只要列a,b,c中的值正确,我就可以将所有非1值替换为0。

我不确定是否将字母和数字拆分为单独的列,并尝试将字母的二进制观察替换为因子甚至是尝试解决原始问题的最佳方法,并且对任何有效的方法持开放态度。

我的真实数据框是由一个脚本生成的,该脚本从.txt文件中提取模式,其中字母数字观察因文件而异。我需要的东西适用于任何分配给char列的唯一字母。

我很感激任何建议或帮助解决这个问题,因为我是R的新手。我仍然熟悉SO礼仪,并会对如何改进问题和/或可重复的例子表示感谢。

2 个答案:

答案 0 :(得分:2)

您可以使用extract中的tidyrobservation拆分为varvalue列,然后使用spread重新整形表格。请注意,由于<NA>中的NA值,ID == 2现在是它自己的列。 select删除了该列:

library(dplyr)
library(tidyr)

df %>%
  extract(observation, c("var", "value"), regex = "([a-z])?(\\d)?") %>%
  spread(var, value) %>%
  select(-`<NA>`)

<强>结果:

  ID    a    b    c
1  1    2 <NA> <NA>
2  2 <NA> <NA> <NA>
3  3 <NA>    3 <NA>
4  4 <NA> <NA>    5
5  5 <NA> <NA> <NA>
6  6    3 <NA> <NA>

答案 1 :(得分:1)

由于您提到非数字值可以是0NA

library(tidyverse)
df %>%
  nest(-ID) %>%
  mutate(data = map(data, ~data.frame(key = gsub("\\d", "", unlist(.x)), val = gsub("\\D", "", unlist(.x))))) %>%
  unnest() %>%
  spread(key, val, fill = 0) %>%
  select(-ncol(.)) %>%
  replace(.=="", 0)

  # ID    a     b     c    
  # <fct> <chr> <chr> <chr>
# 1 1     2     0     0    
# 2 2     0     0     0    
# 3 3     0     3     0    
# 4 4     0     0     5    
# 5 5     0     0     0    
# 6 6     3     0     0    
# There were 14 warnings (use warnings() to see them)