如何拆分列中的所有字符串并在所有新数据中包含前缀

时间:2019-05-13 01:02:31

标签: r regex

我有一个数据集,该数据集的数据在一个列中输入,应该放在三个单独的列中。我希望根据反斜杠将其分为三列,但是每个拆分必须包含仅在字符串的第一部分中找到的字符前缀和仅在末尾找到的字符后缀。

因此,类似“ PC211 / 212.5(C)/ 664F”的前缀为“ PC”,后缀为“ F”。前缀始终为2个字母,后缀始终为1,并且始终为字符。前缀后总是数字代码,后缀前总是数字或结尾括号

我的数据在一个非常大的数据框中,因此我希望能够按列对其进行调用。这是我正在处理的一小部分数据的可重现示例:

df <- data.frame("code" = c("PC211/212.5(C)/664F", "VC23152(A)/23550F", "PC459/460(B)M", "PC187(A)/664F"), stringsAsFactors = FALSE)

我希望它返回如下内容:

df_id_like <- data.frame("code" = c("PC211/212.5(C)/664F", "VC23152(A)/23550F", "PC459/460(B)M", "PC187(A)/664F"), 
"code_1" = c("PC211F", "VC23152(A)F", "PC459M", "PC187F"), 
"code_2" = c("212.5(C)F", "VC23550F", "PC460(B)M", "PC664F"), 
"code_3" = c("PC664F", NA, NA, NA), 
stringsAsFactors = FALSE)

我认为该解决方案可能需要正则表达式,但完全可以接受不需要的解决方案!

4 个答案:

答案 0 :(得分:5)

使用tidyr的{​​{1}}选项

separate

请注意,您的预期输出似乎与输入数据不匹配。例如,对于第1行,library(dplyr) library(tidyr) df %>% separate(code, paste0("code_", 1:3), sep = "/", fill = "right", remove = F) # code code_1 code_2 code_3 #1 PC211/212.5(C)/664F PC211 212.5(C) 664F #2 VC23152(A)/23550F VC23152(A) 23550F <NA> #3 PC459/460(B)M PC459 460(B)M <NA> #4 PC187(A)/664F PC187(A) 664F <NA> 的预期输出为code_3,而相关的输入字符串为"PC664F"。同一行的"664F"具有code_2,而输入字符串为"212.5(C)F"。我认为这些是错误。


更新

感谢@andrew_reece的评论,我(认为我)现在可以理解您的问题了。这是一个选择

"212.5(C)"

说明:我们首先df %>% rowid_to_column("row") %>% separate(code, c("prefix", "main", "suffix"), sep = c(2, -1), remove = F) %>% separate(main, into = paste0("code_", 1:3), sep = "/", fill = "right") %>% gather(key, entry, starts_with("code_")) %>% filter(!is.na(entry)) %>% unite(entry, prefix, entry, suffix, sep = "") %>% spread(key, entry) %>% select(-row) 的{​​{1}}前缀和后缀,然后separate主要的code部分的各个组成部分。我们从宽变宽到长整形,删除separate条目,并将每个code组件与NAcode连接起来,然后再从长变宽。

这将重现您的预期输出,但第1行中的prefix除外。


替代

作为一种替代方法,将带有前缀和后缀的代码存储在suffix列中,而不是使用带有附加列code_2,{{1 }} 等等。这样做的好处是,您不必对列list中的代码数目进行硬编码;以下方法适用于code_1任意个代码,并且仅假定

  1. code_2的前2个字符定义code
  2. code的最后一个字符是code

prefix

请注意,code现在是带有正确前缀/后缀代码的suffix列,这使得使用df %>% separate(code, c("prefix", "main", "suffix"), sep = c(2, -1), remove = F) %>% transmute( code, codes_as_list = pmap( list(prefix, str_split(main, "/"), suffix), function(x, y, z) paste0(x, y, z))) # code codes_as_list #1 PC211/212.5(C)/664F PC211F, PC212.5(C)F, PC664F #2 VC23152(A)/23550F VC23152(A)F, VC23550F #3 PC459/460(B)M PC459M, PC460(B)M #4 PC187(A)/664F PC187(A)F, PC664F 机制对元素的操作变得容易。

答案 1 :(得分:3)

IIUC,这将为您在每个单独的列上提供前缀和后缀:

library(tidyverse)

df %>%
  mutate(prefix = str_extract(code, "^[A-Z]+"),
         suffix = str_extract(code, "[A-Z]$")) %>%
  separate(code, into = c("code_1", "code_2", "code_3"), 
           sep = "/", fill = "right", remove = F) %>%
  mutate_at(vars(matches("_1$")), 
            list(~paste0(., suffix))) %>%
  mutate_at(vars(matches("_2$")), 
            list(~if_else(str_sub(., -1) == suffix, 
                          paste0(prefix, .),
                          paste0(paste0(prefix, .), suffix)))) %>%
  mutate_at(vars(matches("_3$")), 
            list(~if_else(is.na(.), 
                          NA_character_, 
                          paste0(prefix, .)))) %>%
  select(-prefix, -suffix)

                 code      code_1      code_2 code_3
1 PC211/212.5(C)/664F      PC211F PC212.5(C)F PC664F
2   VC23152(A)/23550F VC23152(A)F    VC23550F   <NA>
3       PC459/460(B)M      PC459M   PC460(B)M   <NA>
4       PC187(A)/664F   PC187(A)F      PC664F   <NA>

答案 2 :(得分:2)

这里是separatestr_extract_all的另一个选项。我们创建一个模式('pat'),该模式使用正则表达式环顾四周以匹配/后跟数字([0-9])和第二个模式以匹配/之前的字符位置之间的位置。使用str_replace_all,在字符串的前两个字符(substr)中插入与'pat'匹配的位置,并在字符串的最后一个字符/之前插入位置,然后使用separate在定界符/

处将列分为三部分
library(tidyverse)
#pat <- "(?<=\\/)(?=[0-9]+\\(?[A-Z])"
pat <- "(?<=\\/)(?=[0-9])"
pat2 <- "(?=\\/)"
df %>% 
  mutate(code1 = str_replace_all(code, pat, substr(code, 1, 2)) %>% 
  str_replace_all(pat2, substring(code, nchar(code))))%>%
  separate(code1, into = paste0("code_", 1:3), sep="[/]")
#                 code      code_1      code_2 code_3
#1 PC211/212.5(C)/664F      PC211F PC212.5(C)F PC664F
#2   VC23152(A)/23550F VC23152(A)F    VC23550F   <NA>
#3       PC459/460(B)M      PC459M   PC460(B)M   <NA>
#4       PC187(A)/664F   PC187(A)F      PC664F   <NA>

答案 3 :(得分:1)

非常长的不使用正则表达式的基础R解决方案

pre <- substr(df$code, 1, 2)
post <- substring(df$code, nchar(df$code))
split_string <- strsplit(df$code, "/")
max_len <- max(lengths(split_string))

df[paste0("code", seq_len(max_len))] <- t(mapply(function(x, y, z) {
    if (length(x) >  2)
     c(paste0(x[1], z), paste0(y, x[-c(1, length(x))], z), paste0(y, x[length(x)]), 
        rep(NA, max_len - length(x)))
    else
     c(paste0(x[1], z), paste0(y, x[length(x)]), rep(NA, max_len - length(x))) 
}, split_string, pre, post))


df
#                 code       code1       code2  code3
#1 PC211/212.5(C)/664F      PC211F PC212.5(C)F PC664F
#2   VC23152(A)/23550F VC23152(A)F    VC23550F   <NA>
#3       PC459/460(B)M      PC459M   PC460(B)M   <NA>
#4       PC187(A)/664F   PC187(A)F      PC664F   <NA>

首先找到我们要在字符串的每个部分上添加的每个code的前缀和后缀,在"/"上拆分字符串,然后计算要添加的列数({{1 }})。使用max_len,我们分别将mapplypre粘贴到字符串的每个部分,并用post填充空格。