У меня есть два фрейма данных:
set.seed(1)
df1 <- data.frame(k1 = "AFD(1);Acf(2);Vgr7(2);"
,k2 = "ABC(7);BHG(46);TFG(675);")
df2 <- data.frame(site =c("AFD(1);AFD(2);", "Acf(2);", "TFG(677);",
"XX(275);", "ABC(7);", "ABC(9);")
,p1 = rnorm(6, mean = 5, sd = 2)
,p2 = rnorm(6, mean = 6.5, sd = 2))
Первый фрейм данных на самом деле представляет собой список часто очень длинных строк, состоящих из «элементов». Каждый «элемент» состоит из нескольких букв/цифр, за которыми следует число в скобках, за которым следует точка с запятой. В этом примере я только поместите 3 «элемента» в каждую строку, но в моем реальном фрейме данных их от десятков до сотен.
> df1
k1 k2
1 AFD(1);Acf(2);Vgr7(2); ABC(7);BHG(46);TFG(675);
Второй фрейм данных разделяет некоторые «элементы» с df1. Его первый столбец, называемый site, содержит некоторые (не все) «элементы» из первого фрейма данных, иногда «элемент» формирует всю строку, а иногда является частью более длинной строки:
> df2
site p1 p2
1 AFD(1);AFD(2); 4.043700 3.745881
2 Acf(2); 5.835883 5.670011
3 TFG(677); 7.717359 5.711420
4 XX(275); 4.794425 6.381373
5 ABC(7); 5.775343 8.700051
6 ABC(9); 4.892390 8.026351
Я хотел бы отфильтровать весь df2 с помощью df2$site и каждый столбец k из df1 (столбцов много, не все из них содержат k в именах).
Самый простой способ объяснить это — показать, как будет выглядеть желаемый результат.
> outcome
k site p1 p2
1 k1 AFD(1);AFD(2): 4.043700 3.745881
2 k1 Acf(2); 5.835883 5.670011
3 k2 ABC(7); 5.775343 8.700051
Первый столбец фрейма данных outcome соответствует именам столбцов в df1. Второй столбец соответствует site столбцу df2 и содержит только sites из df1 столбцов, которые были найдены в df2$sites. Остальные столбцы взяты из df2.
Я понимаю, что этот вопрос состоит из двух отдельных «проблем», одна из которых связана с поиском, а другая связана с циклом по столбцам df1. Я решил показать задачу целиком на тот случай, если существует решение, которое решает и то, и другое за один раз.
Я могу создать строку для grep, но для каждого столбца отдельно:
# this replaces the semicolons with "|", but does not escape the brackets.
k1_pattern <- df1 %>%
select(k1) %>%
deframe() %>%
str_replace_all(";","|")
И тогда я не уверен, как его использовать. Это (ниже) не сработало, может быть, потому, что я не избегал скобок, но я борюсь с этим:
k1_result <- df2 %>%
filter(grepl(pattern = k1_pattern, site))
Но даже если бы это сработало, оно бы работало только с одним столбцом из df1, а у меня их много, и я хотел бы выполнить эту операцию для всех столбцов df1 одновременно.
Я могу создать список sites для поиска в df2 из столбцов в df1:
k1_sites<- df1 %>%
select(k1) %>%
deframe() %>%
strsplit(., "[;]") %>%
unlist()
Но разделитель здесь потерян, и %in% нельзя использовать, так как совпадение иногда будет частичным.
library(dplyr)
df2 %>%
mutate(site_list = strsplit(site, ";")) %>%
rowwise() %>%
filter(length(intersect(site_list,
unlist(strsplit(x = paste0(c(t(df1)), collapse = ""),
split = ";")))) != 0) %>%
select(-site_list)
#> # A tibble: 3 x 3
#> # Rowwise:
#> site p1 p2
#> <chr> <dbl> <dbl>
#> 1 AFD(1);AFD(2); 3.75 7.47
#> 2 Acf(2); 5.37 7.98
#> 3 ABC(7); 5.66 9.52
library(dplyr)
library(tidyr)
df1 %>%
rownames_to_column("id") %>%
pivot_longer(-id, names_to = "k", values_to = "site") %>%
separate_rows(site, sep = ";") %>%
filter(site != "") %>%
select(-id) -> df1_k
df2 %>%
tibble::rownames_to_column("id") %>%
separate_rows(site, sep = ";") %>%
full_join(., df1_k, by = c("site")) %>%
group_by(id) %>%
fill(k, .direction = "downup") %>%
filter(!is.na(id) & !is.na(k)) %>%
summarise(k = first(k),
site = paste0(site, collapse = ";"),
p1 = first(p1),
p2 = first(p2), .groups = "drop") %>%
select(-id)
#> # A tibble: 3 x 4
#> k site p1 p2
#> <chr> <chr> <dbl> <dbl>
#> 1 k1 AFD(1);AFD(2); 3.75 7.47
#> 2 k1 Acf(2); 5.37 7.98
#> 3 k2 ABC(7); 5.66 9.52
@ Wera, да, я только что это понял (обычно я не читаю весь вопрос, извините :). Работаю над этим.
Вот путь к длинному формату для точного соответствия (поэтому без регулярного выражения):
library(dplyr)
library(tidyr)
df1_long = df1 |> stack() |>
separate_rows(values, sep = ";") |>
filter(values != "")
df2 |>
mutate(id = row_number()) |>
separate_rows(site, sep = ";") |>
filter(site != "") |>
left_join(df1_long, by = c("site" = "values")) %>%
group_by(id) |>
filter(any(!is.na(ind))) %>%
summarize(
site = paste(site, collapse = ";"),
across(-site, \(x) first(na.omit(x)))
)
# # A tibble: 3 × 5
# id site p1 p2 ind
# <int> <chr> <dbl> <dbl> <fct>
# 1 1 AFD(1);AFD(2) 3.75 7.47 k1
# 2 2 Acf(2) 5.37 7.98 k1
# 3 5 ABC(7) 5.66 9.52 k2
Привет @M--, спасибо! это приближает меня к желаемому результату, но первый столбец из моего объекта outcome, называемый k, отсутствует.