У меня есть набор данных, в котором один из столбцов имеет строку в качестве значения. Строка имеет такой аспект: Ф: что угодно; Ф: что угодно; П: что угодно; П: что угодно; С: что угодно; С: что угодно;
Я хочу разделить этот столбец на 3 столбца: один для F, другой для P и еще один для C. Когда имеется более одного F (или P или C), мне нужна отдельная строка для каждого из них (остальная часть столбцы одинаковые).
Как я мог это сделать?
Заранее спасибо!
Пример:
Name Value GOs
Ab1 1000 "F: f1; F: f2; P: p1"
Bb1 2000 "P: p1; F: f1"
Cb1 3000 "C: c1; F: f1"
что я хочу в качестве вывода:
Name Value F P C
Ab1 1000 f1 p1 -
Ab1 1000 f2 p1 -
Bb1 2000 f1 p1 -
Cb1 3000 f1 - c1
Я попробовал с
str_match(a, "F:\\s*(.*?)\\s*;")
но это работает только для первого совпадения, мне нужны все совпадения.
df <- data.frame(Name = c("A","B","C"),Value = c(10,20,30),GOs= c("F: f1; F: f2; P: p1","P: p1; F: f1","C: c1; F: f1"))
«Когда есть более одного F (или P или C)», вы имеете в виду вот так? «Ф: что угодно, П: где угодно», та же строка?
Я имею в виду, что у меня может быть более одной строки для каждой строки, например: «OF: Метаболический; F: транспорт; P: Гидролаза; P: Связывание;»
Пожалуйста, предоставьте примеры данных, например dput(head(YOURDATA))
ОТРЕДАКТИРОВАНО:
library(tidyverse)
df %>%
separate_rows(GOs, sep = "; ") %>%
extract(GOs,
into = c("X","content"),
regex = "(.)\\W+(.+)") %>%
pivot_wider(
names_from = X, values_from = content,
# add to suppress warning:
values_fn = list) %>%
unnest(c("F", "P", "C"))
# A tibble: 4 × 5
Name Value F P C
<chr> <dbl> <chr> <chr> <chr>
1 A 10 f1 p1 NA
2 A 10 f2 p1 NA
3 B 20 f1 p1 NA
4 C 30 f1 NA c1
Данные:
df <- data.frame(Name = c("A","B","C"),
Value = c(10,20,30),
GOs= c("F: f1; F: f2; P: p1","P: p1; F: f1","C: c1; F: f1"))
Обновлено еще раз:
В качестве альтернативы рассмотрим следующую структуру вывода:
df %>%
separate_rows(GOs, sep = "; ") %>%
extract(GOs,
into = c("X","content"),
regex = "(.)\\W+(.+)") %>%
pivot_wider(names_from = X, values_from = content, values_fn = toString)
# A tibble: 3 × 5
Name Value F P C
<chr> <dbl> <chr> <chr> <chr>
1 A 10 f1, f2 p1 NA
2 B 20 f1 p1 NA
3 C 30 f1 NA c1
Это решает проблему разделения столбца, но другая проблема заключается в том, что в одной строке может быть более одного F, например: «F: значение 1; F: значение 2», и мне нужно, чтобы они были в разных ряды.
Пожалуйста, предоставьте примеры данных, например dput(head(YOURDATA))
Если повторяется более одной категории, появляется эта ошибка: Ошибка в unnest(): ! В строке 1 невозможно преобразовать ввод размера 3 в размер 2. Например, я получил эту ошибку с этим набором данных: df <- data.frame( Name = c("A","B"), Value = c(10,20), GOs= c("C:e; F:n; F:D; F:D; P:D; P:h","F:t; F:v; F:l; F:m; P:o") )
См. новое решение в редактировании, которое применимо к обоим образцам данных.
Для обновленного примера:
library(tidyverse)
df <- data.frame(
Name = c("A", "B"),
Value = c(10, 20),
GOs = c("C:e; F:n; F:D; F:D; P:D; P:h", "F:t; F:v; F:l; F:m; P:o")
)
df |>
separate_longer_delim(GOs, delim = ";") |>
separate_wider_delim(GOs, delim = ":", names_sep = ":") |>
mutate(across(everything(), str_squish), row = row_number()) |>
pivot_wider(names_from = `GOs:1`, values_from = `GOs:2`, values_fn = list) |>
unnest(c("C", "F", "P"))
#> # A tibble: 11 × 6
#> Name Value row C F P
#> <chr> <chr> <int> <chr> <chr> <chr>
#> 1 A 10 1 e <NA> <NA>
#> 2 A 10 2 <NA> n <NA>
#> 3 A 10 3 <NA> D <NA>
#> 4 A 10 4 <NA> D <NA>
#> 5 A 10 5 <NA> <NA> D
#> 6 A 10 6 <NA> <NA> h
#> 7 B 20 7 <NA> t <NA>
#> 8 B 20 8 <NA> v <NA>
#> 9 B 20 9 <NA> l <NA>
#> 10 B 20 10 <NA> m <NA>
#> 11 B 20 11 <NA> <NA> o
Created on 2024-04-18 with reprex v2.1.0
Если повторяется более одной категории, появляется эта ошибка: Ошибка в unnest(): ! В строке 1 невозможно преобразовать ввод размера 3 в размер 2. Например, я получил эту ошибку с этим набором данных: df <- data.frame( Name = c("A","B"), Value = c(10,20), GOs= c("C:e; F:n; F:D; F:D; P:D; P:h","F:t; F:v; F:l; F:m; P:o") )
Я скорректировал его для обновленного примера. Мне нужно было добавить номер строки. При необходимости вы можете удалить это с помощью |> select(-row)
.
Чтобы избежать изменения формы, которое может быть медленным, вы можете сначала извлечь имена столбцов и значения, используя gregexpr/regmatches
. С помощью столбцов unique
создайте array
без строк и merge
его к элементам списка, затем merge
все вместе, наконец cbind
.
> r <- lapply(c(cn='(.)(?=\\:)', v='(?<=\\:\\s)(.\\d)'), \(x)
+ regmatches(df$GOs, gregexpr(x, df$GOs, perl=TRUE)))
> u <- unique(unlist(r$cn))
> a <- array(dim=c(0, length(u)), dimnames=list(NULL, u))
> Map(cbind, Name=df$Name, Value=df$Value,
+ Map(\(x, y) split(x, y), r$v, r$cn) |>
+ lapply(do.call, what='data.frame') |>
+ lapply(merge, a, all=TRUE)) |>
+ Reduce(f=rbind)
Name Value F P C
1 Ab1 1000 f1 p1 <NA>
2 Ab1 1000 f2 p1 <NA>
3 Bb1 2000 f1 p1 <NA>
4 Cb1 3000 f1 <NA> c1
Данные:
> dput(df)
structure(list(Name = c("Ab1", "Bb1", "Cb1"), Value = c(1000L,
2000L, 3000L), GOs = c("F: f1; F: f2; P: p1", "P: p1; F: f1",
"C: c1; F: f1")), class = "data.frame", row.names = c(NA, -3L
))
Не могли бы вы отредактировать свой вопрос, включив в него воспроизводимые данные образцов?