Поиск элементов одной символьной строки в столбце фрейма данных для ее подмножества

У меня есть два фрейма данных:


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. Я решил показать задачу целиком на тот случай, если существует решение, которое решает и то, и другое за один раз.

НЕУДАЧНОЕ РЕШЕНИЕ 1

Я могу создать строку для 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 одновременно.

НЕУДАЧНОЕ РЕШЕНИЕ 2

Я могу создать список sites для поиска в df2 из столбцов в df1:

k1_sites<- df1 %>% 
  select(k1) %>% 
  deframe() %>% 
  strsplit(., "[;]") %>% 
  unlist()

Но разделитель здесь потерян, и %in% нельзя использовать, так как совпадение иногда будет частичным.

Асинхронная передача данных с помощью sendBeacon в JavaScript
Асинхронная передача данных с помощью sendBeacon в JavaScript
В современных веб-приложениях отправка данных из JavaScript на стороне клиента на сервер является распространенной задачей. Одним из популярных...
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Отказ от ответственности: Эта статья предназначена только для демонстрации и не должна использоваться в качестве инвестиционного совета.
Принципы ООП в JavaScript
Принципы ООП в JavaScript
Парадигма объектно-ориентированного программирования имеет 4 основных принципа,
Пройдите собеседование по Angular: Общие вопросы и ответы экспертов
Пройдите собеседование по Angular: Общие вопросы и ответы экспертов
Можете ли вы объяснить разницу между ngOnInit и конструктором в Angular?
Laravel с Turbo JS
Laravel с Turbo JS
Turbo - это библиотека JavaScript для упрощения создания быстрых и высокоинтерактивных веб-приложений. Она работает с помощью техники под названием...
Типы ввода HTML: Лучшие практики и советы
Типы ввода HTML: Лучшие практики и советы
HTML, или HyperText Markup Language , является стандартным языком разметки, используемым для создания веб-страниц. Типы ввода HTML - это различные...
1
0
79
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Привет @M--, спасибо! это приближает меня к желаемому результату, но первый столбец из моего объекта outcome, называемый k, отсутствует.

Wera 17.11.2022 16:34

@ Wera, да, я только что это понял (обычно я не читаю весь вопрос, извините :). Работаю над этим.

M-- 17.11.2022 16:35
Ответ принят как подходящий

Вот путь к длинному формату для точного соответствия (поэтому без регулярного выражения):

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   

Другие вопросы по теме