У меня есть набор данных, в котором есть данные, введенные в один столбец, которые должны были быть помещены в три отдельных столбца. Я надеюсь разделить его на три столбца на основе обратной косой черты, но каждое разделение должно включать префикс символа, который находится только в первой части строки, и суффикс символа, который находится только в конце.
Итак, что-то вроде «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)
Я думаю, что для решения может потребоваться регулярное выражение, но я полностью открыт для решения, которое этого не делает!





Вариант tidyr с использованием separate
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>
Обратите внимание, что ожидаемый результат не соответствует вашим входным данным. Например, для строки 1 ваш ожидаемый результат для code_3 дает "PC664F", тогда как соответствующая входная строка — "664F". code_2 для той же строки есть "212.5(C)F", тогда как входная строка — "212.5(C)". Я предполагаю, что это ошибки.
Благодаря комментарию @andrew_reece я (думаю, я) теперь понимаю ваш вопрос. Вот вариант
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)
Объяснение: Сначала мы separate префиксы и суффиксы из code, затем separate отдельные компоненты из основной code части. Мы изменяем форму с широкой на длинную, удаляем NA записи и объединяем каждый компонент code с prefix и suffix перед изменением формы обратно с длинной на широкую.
Это воспроизводит ожидаемый результат, за исключением code_2 в строке 1.
В качестве альтернативного подхода может быть полезнее хранить коды пре- и суффиксов в столбце list, а не хранить их в расширенном формате с дополнительными столбцами code_1, code_2 и т. д. Это имеет то преимущество, что вам не придется жестко кодировать количество кодов, которые у вас есть в столбце code; следующий подход будет работать для количества кодов Любые в code и только предполагает, что
code определяют prefixcode - это 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
Обратите внимание, что codes_as_list теперь является столбцом list с правильными префиксами/суффиксами, что упрощает работу с элементами с помощью механизмов purrr::map.
@andrew_reece А, теперь вижу и понимаю! Это будет немного сложнее, чем один звонок separate. Я изучаю это сейчас...
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>
Только что заметил, что это не совсем так. В тех случаях, когда нет третьего code, суффикс будет повторяться. Сейчас смотрю обновление.
Очень длинное базовое решение 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, которые мы хотим добавить к каждой части строки, разделите строку на "/" и рассчитайте количество добавляемых столбцов (max_len). Используя mapply, мы вставляем pre и post в каждую часть строки соответственно и заполняем пробелы NA.
Вот еще вариант с separate и str_extract_all. Мы создаем шаблон («pat»), который использует поиск по регулярному выражению для сопоставления позиции между /, за которой следует число ([0-9]), и вторым шаблоном для сопоставления позиции символа перед /. Используя str_replace_all, вставьте позицию, совпадающую с 'pat', с первыми двумя символами (substr) строки, а также вставьте позицию перед / с последним символом строки, затем используйте 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>
Я думаю, что OP говорит, что префикс (например, PC, VC) и суффикс (например, F, M) должны быть сохранены для каждой из отдельных частей. Это соответствует ожидаемому результату OP, за исключением
code_2в строке 1.