R - разница между двумя наборами во фрейме данных

У меня есть двухфакторные столбцы, я хочу создать третий столбец, который сообщает мне, что у второго есть, а у первого нет. Он очень похож на этот Почта, но у меня проблемы с переходом с df на использование функции setdiff(). Например:

library(dplyr)
y1 <- c("a.b.","a.","b.c.d.")
y2 <- c("a.b.c.","a.b.","b.c.d.")
df <- data.frame(y1,y2)

Колонка y1 содержит a.b., а колонка y2 - a.b.c.. Я хочу, чтобы столбец третей возвращал c. или просто c.

> df
      y1     y2  col3
1   a.b.  a.b.c.  c.
2     a.    a.b.  b.
3 b.c.d.  b.c.d.  

Я думаю, что это должна быть комбинация strsplit и setdiff, но я не могу заставить ее работать.

Я попытался преобразовать factor в character, затем попытался применить strsplit() к результатам, но результат мне кажется странным. Похоже, что в списке создан список, что затрудняет переход к setdiff().

#convert factor to character
df <- df %>% mutate_if (is.factor, as.character)
lapply(df$y1,function(x)(strsplit(x,split = "[.]")))

> lapply(df$y1,function(x)(strsplit(x,split = "[.]")))
[[1]]
[[1]][[1]]
[1] "a" "b"


[[2]]
[[2]][[1]]
[1] "a"


[[3]]
[[3]][[1]]
[1] "b" "c" "d"

А как насчет df%>% rowwise ()%>% mutate (col3 = gsub (y1, "", y2)). Проблема в том, что если y1 имеет лишние символы, y2 не работает. но просто идея потенциально более простого решения

Sarah 18.04.2018 03:12

На самом деле это дает правильные результаты. Мне действительно нужно показать, что отличается в y2, чего нет в y1. Я думаю, что все остальные решения делают то же самое. Вы можете указать это как решение вместо комментария.

jmich738 18.04.2018 03:42

Одна проблема с использованием df %>%rowwise()%>% mutate(col3 = gsub(y1,"",y2)) заключается в том, что если порядок изменить, он не будет работать. Подумайте, есть ли у y1a.b, а у y2 есть b.a.c.

Ronak Shah 18.04.2018 03:46
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
3
724
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Обновлять

Возникла проблема, когда разница состояла более чем из 1 символа, создавалась дополнительная строка. Чтобы преодолеть это, мы paste все элементы вместе для каждого различия. Это также избавляет нас от шага unlist.

df$col3 <- mapply(function(x, y) paste0(setdiff(y, x), collapse = ""),
   strsplit(as.character(df$y1), "\\."), strsplit(as.character(df$y2), "\\."))

Оригинальный ответ

Мы можем использовать mapply и разделить оба столбца на "." используя strsplit, а затем определите разницу между ними, используя setdiff.

df$col3 <- mapply(function(x, y) setdiff(y, x),
       strsplit(as.character(df$y1), "\\."), strsplit(as.character(df$y2), "\\."))

df
#     y1     y2 col3
#1   a.b. a.b.c.    c
#2     a.   a.b.    b
#3 b.c.d. b.c.d.     

Если мы не хотим, чтобы col3 был в списке, мы можем unlist, однако одна проблема в том, что если мы unlist, он удаляет из него значение character(0). Чтобы сохранить это значение, нам нужно выполнить дополнительную проверку. Взято из здесь.

unlist(lapply(df$col3,function(x) if (identical(x,character(0))) ' ' else x))

#[1] "c" "b" " "

есть ли способ преобразовать col3 в обычный столбец? Когда я запускаю str(df), он возвращает col3 как List of 3

jmich738 18.04.2018 03:19

@ jmich738 добавлен в основной ответ.

Ronak Shah 18.04.2018 03:25

Я пытаюсь применить это ко всему моему набору данных, но кажется, что вывод col3 производит меньше строк, чем исходный df. Я все еще не уверен, в чем проблема.

jmich738 18.04.2018 04:06

@ jmich738 Надеюсь, вы делаете это в два этапа. Сначала выполните шаг mapply, а затем шаг unlist.

Ronak Shah 18.04.2018 04:07

Похоже, причиной проблемы является unlist(). unlist создает дополнительные строки. Я говорю df$col3<- unlist(...), но на моем фактическом наборе данных. Я все еще пытаюсь понять, чем мои выборочные данные отличаются от моих фактических данных.

jmich738 18.04.2018 04:16

проблема возникает, когда разница между двумя наборами превышает 1 символ. Если вы измените y2 <- c("a.b.c.d.","a.b.","b.c.d."), то unlist() создаст дополнительную строку.

jmich738 18.04.2018 04:46

@ jmich738 Извините, я должен был подумать об этом сценарии. В любом случае, обновили ответ, и теперь все должно быть в порядке. Это также уменьшает один шаг.

Ronak Shah 18.04.2018 04:52

ты горишь, друг мой!

jmich738 18.04.2018 04:57

Вы также можете использовать purrr:map2:

df %>%
    mutate_if (is.factor, as.character) %>%
    mutate(col3 = map2(strsplit(y2, "\\."), strsplit(y1, "\\."), setdiff))
#      y1     y2 col3
#1   a.b. a.b.c.    c
#2     a.   a.b.    b
#3 b.c.d. b.c.d.    

Объяснение: Преобразуйте factor в векторы character, используйте setdiff на колонках "." и y2, разделенных на y1. Обратите внимание, что col3 - это list.


Обновлять

Похоже, что unnest отбрасывает записи character нулевой длины из list. Итак, чтобы преобразовать col3 из list в вектор character, вы можете сделать:

df %>%
    mutate_if (is.factor, as.character) %>%
    mutate(col3 = map2(strsplit(y2, "\\."), strsplit(y1, "\\."), setdiff)) %>%
    rowwise() %>%
    mutate(col3 = paste(col3, collapse = "."))
## A tibble: 3 x 3
#  y1     y2     col3
#  <chr>  <chr>  <chr>
#1 a.b.   a.b.c. c
#2 a.     a.b.   b
#3 b.c.d. b.c.d. ""

Идея здесь состоит в том, чтобы объединить записи col3 (если их несколько); использование rowwise() обеспечивает построчный paste.

Для обновленных данных образца из вашего комментария:

y1 <- c("a.b.","a.","b.c.d.")
y2 <- c("a.b.c.e.","a.b.","b.c.d.")
df <- data.frame(y1,y2)
df %>%
    mutate_if (is.factor, as.character) %>%
    mutate(col3 = map2(strsplit(y2, "\\."), strsplit(y1, "\\."), setdiff)) %>%
    rowwise() %>%
    mutate(col3 = paste(col3, collapse = "."))
## A tibble: 3 x 3
#  y1     y2       col3
#  <chr>  <chr>    <chr>
#1 a.b.   a.b.c.e. c.e
#2 a.     a.b.     b
#3 b.c.d. b.c.d.   ""

по какой-то причине, когда я запускаю это, я не получаю строку 3, без различий. Вы бы знали, что это такое?

jmich738 18.04.2018 03:39

@ jmich738 - unnest() удаляет все строки, которые очевидно являются пустыми в списке.

thelatemail 18.04.2018 03:42

@thelatemail хорошо, поэтому, если я запустил его без unnest(), я получу все строки

jmich738 18.04.2018 03:46

@ jmich738 и @thelatemail Вы правы! Я не понимал, что unnest отбрасывает записи character нулевой длины. Пожалуйста, посмотрите мое обновленное решение.

Maurits Evers 18.04.2018 03:54

@MauritsEvers очень близок, но кажется, что если разница больше 1 символа, то результаты будут странными. если выставить y2 <- c("a.b.c.e.","a.b.","b.c.d."), то вывод будет выглядеть как c("c", "e")

jmich738 18.04.2018 04:04

@ jmich738 Ах, бездельник! Ты снова прав. Я сделал еще одну правку. Ключ в том, чтобы обеспечить построчную обработку paste.

Maurits Evers 18.04.2018 05:04

Очень простой, но не строгий способ - заменить все в y1 на "" из y2. Это не будет обрабатывать случаи, когда заказы отличаются или если y1 имеет что-то дополнительное к y2, а не наоборот.

df %>% rowwise() %>% mutate(col3 = gsub(y1,"",y2))

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