[英]Merging heterogeneous data.frames
我正在嘗試合並R中的兩個data.frames
d1 <- data.frame(Id=1:3,Name=c("Yann","Anne","Sabri"),Age=c(21,19,31),Height=c(178,169,192),Grade=c(15,12,18))
d2 <- data.frame(Id=c(1,3,4),Name=c("Yann","Sabri","Jui"),Age=c(28,21,15),Sex=c("M","M","F"),City=c("Paris","Paris","Toulouse"))
我想通過Id
進行合並,並在最終的data.frame
僅保留Id
, Name
, Age
, Sex
和Grade
列。
我想出了一個冗長的代碼來完成這項工作,但還有更好的方法嗎?
dm <- data.frame(Id=unique(c(d1$Id,d2$Id)))
dm.d1.rows <- sapply(dm$Id, match, table = d1$Id)
dm.d2.rows <- sapply(dm$Id, match, table = d2$Id)
for(i in c("Name", "Age","Sex","Grade")) {
if(i %in% colnames(d1) && is.factor(d1[[i]]) || i %in% colnames(d2) && is.factor(d2[[i]])) dm[[i]]<- factor(rep(NA,nrow(dm)),
levels=unique(c(levels(d1[[i]]),levels(d2[[i]]))))
else dm[[i]]<- rep(NA,nrow(dm))
if(i %in% colnames(d1)) dm[[i]][!is.na(dm.d1.rows)] <- d1[[i]][na.exclude(dm.d1.rows)]
if(i %in% colnames(d2)) dm[[i]][!is.na(dm.d2.rows)] <- d2[[i]][na.exclude(dm.d2.rows)]
}
這是通過tidyverse的一個想法,使用函數coalesce
。 此函數基本上將NA
值替換為另一個(指定)列的值。 - 您可以在此處找到函數coalesce
更多信息和實現
coalesce
官方文檔:給定一組向量,coalesce()在每個位置找到第一個非缺失值。 這受到SQL COALESCE函數的啟發,它對NULL執行相同的操作。
library(tidyverse)
d1 %>%
full_join(d2, by = c('Id', 'Name')) %>%
mutate(Age = coalesce(Age.x, Age.y)) %>%
select(Id, Name, Age, Sex, Grade)
這使,
Id Name Age Sex Grade 1 1 Yann 21 M 15 2 2 Anne 19 <NA> 12 3 3 Sabri 31 M 18 4 4 Jui 15 F NA
同樣,在data.table語法中,
library(data.table)
#Convert to data.tables
d1_t <- setDT(d1)
d2_t <- setDT(d2)
merge(d1_t, d2_t, by = c('Id', 'Name'), all = TRUE)[,
Age := ifelse(is.na(Age.x), Age.y, Age.x)][,
c('Age.x', 'Age.y', 'City', 'Height') := NULL][]
這使,
Id Name Grade Sex Age 1: 1 Yann 15 M 21 2: 2 Anne 12 <NA> 19 3: 3 Sabri 18 M 31 4: 4 Jui NA F 15
就個人而言,我是sqldf
忠實粉絲,它允許您使用SQL查詢來創建/操作數據幀。 在你的情況下,下面的陳述應該做的伎倆。
d1 <- data.frame(Id=1:3,Name=c("Yann","Anne","Sabri"),Age=c(21,19,31),
Height=c(178,169,192),Grade=c(15,12,18))
d2 <- data.frame(Id=c(1,3,4),Name=c("Yann","Sabri","Jui"),Age=c(28,21,15),
Sex=c("M","M","F"),City=c("Paris","Paris","Toulouse"))
d3 = sqldf("SELECT d1.Id, d1.Name, d1.Age, d2.Sex , d1.Grade
FROM d1
LEFT JOIN d2 ON d1.Id = d2.Id
UNION
SELECT d2.Id, d2.Name, coalesce(d1.Age, d2.Age) , d2.Sex, coalesce(d1.Grade, NULL)
FROM d2
LEFT JOIN d1 ON d2.Id = d1.Id")
特別是對於更復雜的數據幀合並/操作,使用sqldf
/ SQL非常有用。
編輯:使用工作sqldf
/ R環境修復SQL語句,產生如下表:
Id Name Age Sex Grade
1 Yann 21 M 15
2 Anne 19 <NA> 12
3 Sabri 31 M 18
4 Jui 15 F NA
在基地R:
d1 <- data.frame(Id=1:3,Name=c("Yann","Anne","Sabri"),Age=c(21,19,31),Height=c(178,169,192),Grade=c(15,12,18),stringsAsFactors = F)
d2 <- data.frame(Id=c(1,3,4),Name=c("Yann","Sabri","Jui"),Age=c(28,21,15),Sex=c("M","M","F"),City=c("Paris","Paris","Toulouse"),stringsAsFactors = F)
nms <- c("Id","Name", "Age", "Sex", "Grade")
. <- merge(d2,d1,all=TRUE,sort=FALSE)[nms]
aggregate(.,list(.$Id), function(x) c(na.omit(x),NA)[1])[-1]
# Id Name Age Sex Grade
# 1 1 Yann 28 M 15
# 2 2 Anne 19 <NA> 12
# 3 3 Sabri 21 M 18
# 4 4 Jui 15 F NA
注意stringsAsFactors = F
,在應用此解決方案之前,您需要將因子轉換為字符。
這可能不是一個理想的答案,但這里是一個使用sapply
的非合並,非連接選項,因為我們想要僅使用一列來組合兩個數據幀
#Name the cols which you want in the final data frame
cols <- c("Id", "Name", "Age", "Sex","Grade")
#Get all unique id's
ids <- union(d1$Id, d2$Id)
#Loop over each ID
data.frame(t(sapply(ids, function(x) {
#Get indices in d1 where Id is present
d1inds <- d1$Id == x
#Get indices in d2 where Id is present
d2inds <- d2$Id == x
#If the Id is present in both d1 AND d2
if (any(d1inds) & any(d2inds))
#Combine d2 and d1 and select only cols column
#This is based on your expected output that in case if the ID is same
#we want to prefer Name and Age column from d2 rather than d1
return(cbind(d2[d2inds, ], d1[d1inds, ])[cols])
#If you want to prefer d1 over d2, we can do
#return(cbind(d1[d1inds, ], d2[d2inds, ])[cols])
#If the Id is present only in d1, add a "Sex" column with NA
if (any(d1inds))
return(cbind(d1[d1inds, ], "Sex" = NA)[cols])
#If the Id is present only in d2, add a "Grade" column with NA
else
return(cbind(d2[d2inds, ], "Grade" = NA)[cols])
})))
# Id Name Age Sex Grade
#1 1 Yann 28 M 15
#2 2 Anne 19 NA 12
#3 3 Sabri 21 M 18
#4 4 Jui 15 F NA
數據
d1 <- data.frame(Id=1:3,Name=c("Yann","Anne","Sabri"),Age=c(21,19,31),
Height=c(178,169,192),Grade=c(15,12,18), stringsAsFactors = FALSE)
d2 <- data.frame(Id=c(1,3,4),Name=c("Yann","Sabri","Jui"),Age=c(28,21,15),
Sex=c("M","M","F"),City=c("Paris","Paris","Toulouse"), stringsAsFactors = FALSE)
您可以使用我的包safejoin ,進行完全連接並使用dplyr::coalesce
處理沖突。 我們還使用dplyr::one_of
因此我們不必手動選擇列。
# devtools::install_github("moodymudskipper/safejoin")
library(safejoin)
keep <- c("Id", "Name", "Age", "Sex", "Grade")
safe_full_join(select(d1,one_of(keep)), select(d2,one_of(keep)),
by = c("Id","Name"), conflict = coalesce, check="")
# Id Name Age Grade Sex
# 1 1 Yann 21 15 M
# 2 2 Anne 19 12 <NA>
# 3 3 Sabri 31 18 M
# 4 4 Jui 15 NA F
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.