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

#r #merging-data

#r #слияние-данные

Вопрос:

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

Если у меня есть список из двух фреймов данных (filelist), и я хочу «dplyr::left_join» или «объединить» каждый фрейм данных в списке с другими данными из одного фрейма данных, похоже, что впоследствии он не появится в списке. Однако, если я использую одни и те же команды поэтапно и отдельно для каждого элемента фрейма данных списка, я получаю те же предупреждения (из-за отсутствия уровней факторов), но желаемый результат. Например:

некоторые фреймы данных

 df1 <- data.frame(x = 1:3, y=letters[1:3])
df2 <- data.frame(x = 1:5, y=letters[1:5])

# make list of dataframes
filelist <- list(df1,df2)

# new data frame to add to the data frames in the list by indexing "y"
df3 <- data.frame(animal = c(rep("snake", 7)), y=letters[1:7], geno = c("aa", "ab", "ac", "aa", "ac", "ab", "ae"))

# merge df3 into both data frames in the filelist
for (i in 1:length(filelist)) {dplyr::left_join(filelist[[i]], df3, by = "y")}

## Gives the following warning because some factor levels are missing between datasets
Warning message:
Column `y` joining factors with different levels, coercing to character vector 
  

возвращаемый результат совпадает с исходным списком файлов

 > filelist
[[1]]
  x y
1 1 a
2 2 b
3 3 c

[[2]]
  x y
1 1 a
2 2 b
3 3 c
4 4 d
5 5 e
  

Ожидаемый результат (достигается путем объединения каждого элемента списка по отдельности, затем создания нового списка)

 new1 <- dplyr::left_join(filelist[[1]], df3, by = "y")
new2 <- dplyr::left_join(filelist[[2]], df3, by = "y")
newlist <-(new1,new2)
> newlist
[[1]]
  x y animal geno
1 1 a  snake   aa
2 2 b  snake   ab
3 3 c  snake   ac

[[2]]
  x y animal geno
1 1 a  snake   aa
2 2 b  snake   ab
3 3 c  snake   ac
4 4 d  snake   aa
5 5 e  snake   ac
  

Каков наилучший способ сделать это, не удаляя каждый фрейм данных из исходного списка, добавляя новые данные, а затем создавая новый список?

Ответ №1:

Как сказано в предупреждающем сообщении, факторы имеют разные уровни.

Вы можете преобразовать коэффициенты в символы для каждого фрейма данных следующим образом с помощью dplyr :

 df %>% mutate_if(is.factor, as.character) -> df
  

Или гомогенизировать уровни коэффициентов переменной y :

 for (i in 1:length(filelist)) {
  x = factor(unique(c(levels(filelist[[i]]$y),levels(df3$y))))
  levels(filelist[[i]]$y) = x
  levels(df3$y) = x
  filelist[[i]] = dplyr::left_join(filelist[[i]], df3, by = "y")
}
  

Комментарии:

1. Спасибо, гомогенизация была предпочтительным вариантом.

Ответ №2:

Я бы использовал map функцию из purrr пакета, который, как dplyr , является частью tidyverse:

 library(tidyverse)
library(purrr) # loaded when you call tidyverse, but doing it explicitly here

map(filelist, left_join, df3)

[[1]]
  x y animal geno
1 1 a  snake   aa
2 2 b  snake   ab
3 3 c  snake   ac

[[2]]
  x y animal geno
1 1 a  snake   aa
2 2 b  snake   ab
3 3 c  snake   ac
4 4 d  snake   aa
5 5 e  snake   ac

Warning messages:
1: Column `y` joining factors with different levels, coercing to character vector 
2: Column `y` joining factors with different levels, coercing to character vector 
  

Комментарии:

1. Спасибо, это тоже сработало, но мне понравился вариант Pelilican, поскольку он сохранял объединяющую переменную в качестве фактора (ничего особенного, но на одну вещь меньше)

2. вы имеете в виду аргумент «by» в left_join ? Вы можете добавить это, но в этом нет необходимости.

3. Нет, не аргумент «by». Эти методы «map» принудительно преобразовали множитель в символ (согласно предупреждениям), поэтому его просто нужно было бы повторно преобразовать в множитель, вот и все. Но ваше решение тоже сработало просто отлично — извините, новичок, не уверен в назначении этикета «ответил».

4. Да, полностью зависит от вас и от того, как вы хотите что-то делать. Было бы довольно легко сделать это фактором снова с mutate(y = as_factor(y)) . Я стараюсь по возможности избегать циклов for.