Как объединить список фреймов с одинаковыми именами столбцов?

У меня есть следующие датафреймы

df1 <- tibble::as.tibble(list(a = c(1,2,3), d = c(10,11,12) ,id = c("a","b","c")))
df2 <- tibble::as.tibble(list(a = c(4,5,6), e = c(13,14,15) ,id = c("a","b","c")))
df3 <- tibble::as.tibble(list(a = c(7,8,9), f = c(16,17,18) ,id = c("a","b","c")))

Я хочу объединить эти кадры данных. Поскольку имя столбца a происходит во всех них, я буду использовать suffix аргумент при слиянии.

Желаемый результат, который я ищу, это

| id | a.df1 | d  | a.df2 | e  | a.df3 | f  |
|----|-------|----|-------|----|-------|----|
| a  | 1     | 10 | 4     | 13 | 7     | 16 |
| b  | 2     | 11 | 5     | 14 | 8     | 17 |
| c  | 3     | 12 | 6     | 15 | 9     | 18 |

Ниже приведен код, который я пытался

test_list <- list(df1, df2, df3)
names(test_list) <- c("df1", "df2", "df3")
seq_along(temp) %>% 
      purrr::reduce(
      ~merge(
      temp[[.x]], 
      temp[[.y]], 
      suffix = c(names(test_list[.x]), names(test_list[.y])))

Однако это приводит к ошибке, заявляющей Error in temp[[.x]] : invalid subscript type 'list, Почему я не могу установить подмножество для фрейма данных в функции слияния

Также есть лучший способ объединить список из нескольких фреймов данных с одинаковыми именами столбцов.

2 ответа

Решение
library(tidyverse)

df1 <- tibble::as.tibble(list(a = c(1,2,3), d = c(10,11,12) ,id = c("a","b","c")))
df2 <- tibble::as.tibble(list(a = c(4,5,6), e = c(13,14,15) ,id = c("a","b","c")))
df3 <- tibble::as.tibble(list(a = c(7,8,9), f = c(16,17,18) ,id = c("a","b","c")))

# create your list and the names
test_list <- list(df1, df2, df3)
names(test_list) <- c("df1", "df2", "df3")

# spot overlapping columns
test_list %>%
  map_df(names) %>%
  gather() %>%
  count(value) %>%
  filter(n > 1 & value != "id") %>%
  pull(value) -> overlaps

map2(test_list, names(test_list), ~{names(.x)[names(.x) %in% overlaps] = paste0(names(.x)[names(.x) %in% overlaps],".",.y); .x}) %>% 
  reduce(function(x,y) left_join(x,y, by="id")) %>%
  select(id, everything())

# # A tibble: 3 x 7
#   id    a.df1     d a.df2     e a.df3     f
#   <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 a         1    10     4    13     7    16
# 2 b         2    11     5    14     8    17
# 3 c         3    12     6    15     9    18

Учитывая список и его имена мы используем map2 обновить имя каждого элемента в позиции 1 (т.е. столбец a).

Тогда мы используем reduce чтобы присоединиться к кадрам данных последовательно, и мы используем select расставить колонны.

Как это выглядит?

t <- merge(df1,df2, by = "id" )
df <- merge(t,df3, by = "id" )
names(df) <- c("id", "a.df1", "a.df2", "a.df3")

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

Функция eat моего пакета safejoin имеет такую ​​особенность, если вы дадите ему список data.frames в качестве второго входа, он рекурсивно присоединит их к первому входу. Мы можем переименовать все столбцы "а" и использовать его.

# devtools::install_github("moodymudskipper/safejoin")
library(safejoin)
dfs <- imap(lst(df1,df2,df3), ~rename_at(.x, "a",paste, .y, sep="."), .y) %>%
  unname()
eat(dfs[[1]], dfs[-1], .by = "id")
# # A tibble: 3 x 7
#   id    a.df1     d a.df2     e a.df3     f
#   <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 a         1    10     4    13     7    16
# 2 b         2    11     5    14     8    17
# 3 c         3    12     6    15     9    18
Другие вопросы по тегам