我有一个数据集,该数据集的数据在一个列中输入,应该放在三个单独的列中。我希望根据反斜杠将其分为三列,但是每个拆分必须包含仅在字符串的第一部分中找到的字符前缀和仅在末尾找到的字符后缀。
因此,类似“ 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)
我认为该解决方案可能需要正则表达式,但完全可以接受不需要的解决方案!
答案 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
组件与NA
和code
连接起来,然后再从长变宽。
这将重现您的预期输出,但第1行中的prefix
除外。
作为一种替代方法,将带有前缀和后缀的代码存储在suffix
列中,而不是使用带有附加列code_2
,{{1 }} 等等。这样做的好处是,您不必对列list
中的代码数目进行硬编码;以下方法适用于code_1
中任意个代码,并且仅假定
code_2
的前2个字符定义code
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)
这里是separate
和str_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
,我们分别将mapply
和pre
粘贴到字符串的每个部分,并用post
填充空格。