У меня есть двухфакторные столбцы, я хочу создать третий столбец, который сообщает мне, что у второго есть, а у первого нет.
Он очень похож на этот Почта, но у меня проблемы с переходом с 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"
На самом деле это дает правильные результаты. Мне действительно нужно показать, что отличается в y2, чего нет в y1. Я думаю, что все остальные решения делают то же самое. Вы можете указать это как решение вместо комментария.
Одна проблема с использованием df %>%rowwise()%>% mutate(col3 = gsub(y1,"",y2)) заключается в том, что если порядок изменить, он не будет работать. Подумайте, есть ли у y1a.b, а у y2 есть b.a.c.





Обновлять
Возникла проблема, когда разница состояла более чем из 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 добавлен в основной ответ.
Я пытаюсь применить это ко всему моему набору данных, но кажется, что вывод col3 производит меньше строк, чем исходный df. Я все еще не уверен, в чем проблема.
@ jmich738 Надеюсь, вы делаете это в два этапа. Сначала выполните шаг mapply, а затем шаг unlist.
Похоже, причиной проблемы является unlist(). unlist создает дополнительные строки. Я говорю df$col3<- unlist(...), но на моем фактическом наборе данных. Я все еще пытаюсь понять, чем мои выборочные данные отличаются от моих фактических данных.
проблема возникает, когда разница между двумя наборами превышает 1 символ. Если вы измените y2 <- c("a.b.c.d.","a.b.","b.c.d."), то unlist() создаст дополнительную строку.
@ jmich738 Извините, я должен был подумать об этом сценарии. В любом случае, обновили ответ, и теперь все должно быть в порядке. Это также уменьшает один шаг.
ты горишь, друг мой!
Вы также можете использовать 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 - unnest() удаляет все строки, которые очевидно являются пустыми в списке.
@thelatemail хорошо, поэтому, если я запустил его без unnest(), я получу все строки
@ jmich738 и @thelatemail Вы правы! Я не понимал, что unnest отбрасывает записи character нулевой длины. Пожалуйста, посмотрите мое обновленное решение.
@MauritsEvers очень близок, но кажется, что если разница больше 1 символа, то результаты будут странными. если выставить y2 <- c("a.b.c.e.","a.b.","b.c.d."), то вывод будет выглядеть как c("c", "e")
@ jmich738 Ах, бездельник! Ты снова прав. Я сделал еще одну правку. Ключ в том, чтобы обеспечить построчную обработку paste.
Очень простой, но не строгий способ - заменить все в y1 на "" из y2. Это не будет обрабатывать случаи, когда заказы отличаются или если y1 имеет что-то дополнительное к y2, а не наоборот.
df %>% rowwise() %>% mutate(col3 = gsub(y1,"",y2))
А как насчет df%>% rowwise ()%>% mutate (col3 = gsub (y1, "", y2)). Проблема в том, что если y1 имеет лишние символы, y2 не работает. но просто идея потенциально более простого решения