Объединить/объединить столбцы с тем же именем, но неполные данные
У меня есть два кадра данных, которые имеют несколько столбцов с одинаковыми именами и другие с разными именами. Кадры данных выглядят примерно так:
df1
ID hello world hockey soccer
1 1 NA NA 7 4
2 2 NA NA 2 5
3 3 10 8 8 23
4 4 4 17 5 12
5 5 NA NA 3 43
df2
ID hello world football baseball
1 1 2 3 43 6
2 2 5 1 24 32
3 3 NA NA 2 23
4 4 NA NA 5 15
5 5 9 7 12 23
Как вы можете видеть, в двух общих столбцах ("привет" и "мир") некоторые данные находятся в одном из кадров данных, а остальные - в другом.
То, что я пытаюсь сделать, это (1) объединить 2 кадра данных с помощью "id", (2) объединить все данные из столбцов "привет" и "мир" в обоих кадрах в 1 столбец "привет" и 1 "мир ", и (3) имеют окончательный кадр данных, также содержат все остальные столбцы в двух исходных кадрах (" хоккей "," футбол "," футбол "," бейсбол "). Итак, я хочу, чтобы конечный результат был таким:
ID hello world hockey soccer football baseball
1 1 2 3 7 4 43 6
2 2 5 3 2 5 24 32
3 3 10 8 8 23 2 23
4 4 4 17 5 12 5 15
5 5 9 7 3 43 12 23
Я новичок в R, поэтому только те коды, которые я пробовал, являются вариантами merge
и я пробовал ответ, который я нашел здесь, который был основан на аналогичном вопросе: R: объединение копий одной и той же переменной. Однако мои наборы данных на самом деле намного больше, чем то, что я показываю здесь (около 20 соответствующих столбцов (например, "привет" и "мир") и 100 несовпадающих (например, "хоккей" и "футбол") поэтому я ищу что-то, что не потребует от меня написать все это вручную.
Любая идея, если это можно сделать? Извините, я не могу представить пример своих усилий, но я действительно не знаю, с чего начать:
mydata <- merge(df1, df2, by=c("ID"), all = TRUE)
Для воспроизведения кадров данных:
df1 <- structure(list(ID = c(1L, 2L, 3L, 4L, 5L), hellow = c(2, 5, NA, NA, 9),
world = c(3, 1, NA, NA, 7), football = c(43, 24, 2, 5, 12),
baseball = c(6, 32, 23, 15, 23)), .Names = c("ID", "hello", "world",
"football", "baseball"), class = "data.frame", row.names = c(NA, -5L))
df2 <- structure(list(ID = c(1L, 2L, 3L, 4L, 5L), hellow = c(NA, NA, 10, 4, NA),
world = c(NA, NA, 8, 17, NA), hockey = c(7, 2, 8, 5, 3),
soccer = c(4, 5, 23, 12, 43)), .Names = c("ID", "hello", "world", "hockey",
"soccer"), class = "data.frame", row.names = c(NA, -5L))
Ответы
Ответ 1
Здесь используется подход, который включает melt
ваши данные, слияние расплавленных данных и использование dcast
, чтобы вернуть его в широкую форму. Я добавил комментарии, чтобы понять, что происходит.
## Required packages
library(data.table)
library(reshape2)
dcast.data.table(
merge(
## melt the first data.frame and set the key as ID and variable
setkey(melt(as.data.table(df1), id.vars = "ID"), ID, variable),
## melt the second data.frame
melt(as.data.table(df2), id.vars = "ID"),
## you'll have 2 value columns...
all = TRUE)[, value := ifelse(
## ... combine them into 1 with ifelse
is.na(value.x), value.y, value.x)],
## This is your reshaping formula
ID ~ variable, value.var = "value")
# ID hello world football baseball hockey soccer
# 1: 1 2 3 43 6 7 4
# 2: 2 5 1 24 32 2 5
# 3: 3 10 8 2 23 8 23
# 4: 4 4 17 5 15 5 12
# 5: 5 9 7 12 23 3 43
Ответ 2
Никто не выложил решение dplyr
, так что здесь можно найти краткий вариант в dplyr
. Подход - это просто сделать full_join
который объединяет все строки, затем group
и summarise
чтобы удалить избыточные отсутствующие ячейки.
library(tidyverse)
df1 <- structure(list(ID = 1:5, hello = c(NA, NA, 10L, 4L, NA), world = c(NA, NA, 8L, 17L, NA), hockey = c(7L, 2L, 8L, 5L, 3L), soccer = c(4L, 5L, 23L, 12L, 43L)), row.names = c(NA, -5L), class = c("tbl_df", "tbl", "data.frame"), spec = structure(list(cols = list(ID = structure(list(), class = c("collector_integer", "collector")), hello = structure(list(), class = c("collector_integer", "collector")), world = structure(list(), class = c("collector_integer", "collector")), hockey = structure(list(), class = c("collector_integer", "collector")), soccer = structure(list(), class = c("collector_integer", "collector"))), default = structure(list(), class = c("collector_guess", "collector"))), class = "col_spec"))
df2 <- structure(list(ID = 1:5, hello = c(2L, 5L, NA, NA, 9L), world = c(3L, 1L, NA, NA, 7L), football = c(43L, 24L, 2L, 5L, 12L), baseball = c(6L, 32L, 23L, 15L, 2L)), row.names = c(NA, -5L), class = c("tbl_df", "tbl", "data.frame"), spec = structure(list(cols = list(ID = structure(list(), class = c("collector_integer", "collector")), hello = structure(list(), class = c("collector_integer", "collector")), world = structure(list(), class = c("collector_integer", "collector")), football = structure(list(), class = c("collector_integer", "collector")), baseball = structure(list(), class = c("collector_integer", "collector"))), default = structure(list(), class = c("collector_guess", "collector"))), class = "col_spec"))
df1 %>%
full_join(df2, by = intersect(colnames(df1), colnames(df2))) %>%
group_by(ID) %>%
summarize_all(na.omit)
#> # A tibble: 5 x 7
#> ID hello world hockey soccer football baseball
#> <int> <int> <int> <int> <int> <int> <int>
#> 1 1 2 3 7 4 43 6
#> 2 2 5 1 2 5 24 32
#> 3 3 10 8 8 23 2 23
#> 4 4 4 17 5 12 5 15
#> 5 5 9 7 3 43 12 2
Создан в 2018-07-13 пакетом reprex (v0.2.0).
Ответ 3
Здесь другой подход data.table
, использующий двоичное слияние
library(data.table)
setkey(setDT(df1), ID) ; setkey(setDT(df2), ID) # Converting to data.table objects and setting keys
df1 <- df1[df2][, `:=`(i.hello = NULL, i.world = NULL)] # Full left join
df1[df2[complete.cases(df2)], `:=`(hello = i.hello, world = i.world)][] # Joining only on non-missing values
# ID hello world football baseball hockey soccer
# 1: 1 2 3 43 6 7 4
# 2: 2 5 1 24 32 2 5
# 3: 3 10 8 2 23 8 23
# 4: 4 4 17 5 15 5 12
# 5: 5 9 7 12 23 3 43
Ответ 4
@ответ ananda-mahto более изящный, но вот мое предложение:
library(reshape2)
df1=melt(df1,id='ID',na.rm=TRUE)
df2=melt(df2,id='ID',na.rm=TRUE)
DF=rbind(df1,df2)
# Not needeed, added na.rm=TRUE based on @ananda-mahto valid comment
# DF<-DF[!is.na(DF$value),]
dcast(DF,ID~variable,value.var='value')
Ответ 5
Вот более ориентированный на tidyr
подход, который делает что-то похожее на принятый в настоящее время ответ. Подход состоит в том, чтобы просто складывать кадры данных друг над другом с помощью bind_rows
(который соответствует именам столбцов), gather
все столбцы без ID
с na.rm = TRUE
и затем spread
их обратно. Это должно быть устойчивым к ситуациям, когда условие "если значение NA в" df1 "имеет значение в" df2 "(и наоборот)" не всегда выполняется, по сравнению с опцией summarise
.
library(tidyverse)
df1 <- structure(list(ID = 1:5, hello = c(NA, NA, 10L, 4L, NA), world = c(NA, NA, 8L, 17L, NA), hockey = c(7L, 2L, 8L, 5L, 3L), soccer = c(4L, 5L, 23L, 12L, 43L)), row.names = c(NA, -5L), class = c("tbl_df", "tbl", "data.frame"), spec = structure(list(cols = list(ID = structure(list(), class = c("collector_integer", "collector")), hello = structure(list(), class = c("collector_integer", "collector")), world = structure(list(), class = c("collector_integer", "collector")), hockey = structure(list(), class = c("collector_integer", "collector")), soccer = structure(list(), class = c("collector_integer", "collector"))), default = structure(list(), class = c("collector_guess", "collector"))), class = "col_spec"))
df2 <- structure(list(ID = 1:5, hello = c(2L, 5L, NA, NA, 9L), world = c(3L, 1L, NA, NA, 7L), football = c(43L, 24L, 2L, 5L, 12L), baseball = c(6L, 32L, 23L, 15L, 2L)), row.names = c(NA, -5L), class = c("tbl_df", "tbl", "data.frame"), spec = structure(list(cols = list(ID = structure(list(), class = c("collector_integer", "collector")), hello = structure(list(), class = c("collector_integer", "collector")), world = structure(list(), class = c("collector_integer", "collector")), football = structure(list(), class = c("collector_integer", "collector")), baseball = structure(list(), class = c("collector_integer", "collector"))), default = structure(list(), class = c("collector_guess", "collector"))), class = "col_spec"))
df1 %>%
bind_rows(df2) %>%
gather(variable, value, -ID, na.rm = TRUE) %>%
spread(variable, value)
#> # A tibble: 5 x 7
#> ID baseball football hello hockey soccer world
#> <int> <int> <int> <int> <int> <int> <int>
#> 1 1 6 43 2 7 4 3
#> 2 2 32 24 5 2 5 1
#> 3 3 23 2 10 8 23 8
#> 4 4 15 5 4 5 12 17
#> 5 5 2 12 9 3 43 7
Создан в 2018-07-13 пакетом reprex (v0.2.0).
Ответ 6
Используя tidyverse
мы могли бы использовать coalesce
.
Ни одно из приведенных ниже решений не создает лишние строки, данные остаются примерно одинаковыми по размеру и подобной форме во всей цепочке.
Решение 1
list(df1,df2) %>%
transpose(union(names(df1),names(df2))) %>%
map_dfc(. %>% compact %>% invoke(coalesce,.))
# # A tibble: 5 x 7
# ID hello world football baseball hockey soccer
# <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 1 2 3 43 6 7 4
# 2 2 5 1 24 32 2 5
# 3 3 10 8 2 23 8 23
# 4 4 4 17 5 15 5 12
# 5 5 9 7 12 23 3 43
Пояснения
- Оберните оба кадра данных в
list
-
transpose
его, поэтому каждый новый элемент в корне имеет имя столбца вывода. Поведение по умолчанию для transpose
по умолчанию заключается в том, чтобы принять первый аргумент как шаблон, поэтому, к сожалению, мы должны быть явным, чтобы получить все из них. -
compact
эти элементы, так как все они были длиной 2, но с одним из них был NULL
когда данный столбец отсутствовал с одной стороны. -
coalesce
те, которые в основном означают возврат первого не NA
вы находите, при постановке аргументов бок о бок.
если повторение df1
и df2
во второй строке является проблемой, используйте вместо этого следующее:
transpose(invoke(union, setNames(map(., names), c("x","y"))))
Решение 2
Такая же философия, но на этот раз мы зацикливаемся на именах:
map_dfc(set_names(union(names(df1), names(df2))),
~ invoke(coalesce, compact(list(df1[[.x]], df2[[.x]]))))
# # A tibble: 5 x 7
# ID hello world football baseball hockey soccer
# <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 1 2 3 43 6 7 4
# 2 2 5 1 24 32 2 5
# 3 3 10 8 2 23 8 23
# 4 4 4 17 5 15 5 12
# 5 5 9 7 12 23 3 43
Здесь он однажды используется для тех, кто предпочитает:
union(names(df1), names(df2)) %>%
set_names %>%
map_dfc(~ list(df1[[.x]], df2[[.x]]) %>%
compact %>%
invoke(coalesce, .))
Пояснения
-
set_names
дает имена векторных символов, идентичные его значениям, поэтому map_dfc
может map_dfc
выходные столбцы. -
df1[[.x]]
вернет NULL
когда .x
не является столбцом df1
, мы воспользуемся этим. -
df1
и df2
упоминаются 2 раза каждый, и я не могу думать об этом.
Решение 1 является более чистым в отношении этих пунктов, поэтому я рекомендую его.