[英]Merge two dataframes with dplyr::left_join and multiple conditions
[英]R: Combining lapply and left_join to conditionally merge dataframes
我希望那里的某個人可以幫助我找到我在R中使用我的代碼時遇到的令人沮喪的問題的根源。我有一個由數據幀組成的列表,我想要將每個元素連接到一個兩個其他數據幀(稱為A和B)。 要連接的這些輔助數據幀中的哪一個取決於元素在列表中的位置。 為了我的目的,我希望每個奇數元素都被左連接到A,並且每個偶數元素都被左連接到B.
library(dplyr)
DF <- data.frame(Num = c("1","2"), Let = c("a","b"), stringsAsFactors = FALSE)
A <- data.frame(Let = c("a","b"), Col = c("Yellow","Red"), stringsAsFactors = FALSE)
B <- data.frame(Let = c("a","b"), Col = c("Green","Blue"), stringsAsFactors = FALSE)
LIST <- list(DF, DF)
到目前為止,我試圖以兩種不同的方式做到這一點。 第一種方法涉及if-else語句。 如果我應用這樣的語句來根據位置分配一個整數值,我會得到預期的結果。 類似地,當我取消if-else語句並簡單地在列表元素上執行一系列左連接時,一切都按預期工作。
lapply(seq_along(LIST), function(x, {ifelse((x %% 2)==0, y[[x]] <- 1, y[[x]] <- 2)}, y = LIST)
lapply(seq_along(LIST), function(x, {left_join(y[[x]], A, by = c("Let"))}, y = LIST)
我遇到問題的地方是當我嘗試組合if-else語句和左連接時。 特別是,我最終得到一個由列表組成的列表,每個列表僅保留原始對應數據幀的第一列。
lapply(seq_along(LIST), function(x, y) {ifelse((x %% 2)==0, left_join(y[[x]], A, by = c("Let")), left_join(y[[x]], B, by = c("Let")))}, y = LIST)
這是我想要獲得的輸出:
[[1]]
Let Num Col
1 a 1 Yellow
2 b 2 Red
[[2]]
Let Num Col
1 a 1 Green
2 b 2 Blue
我確信這個問題有一個荒謬簡單的解決方案。 誰能看到它?
提前致謝! 馬修
PS:我還嘗試了第二種方法,應用子集而不是if-else語句。 然而,我再次陷入困境。 下面的第一行按預期工作,但第二行返回錯誤,好像R無法識別列表索引:
lapply(seq_along(LIST), function(x, y) {left_join(y[[x > 0]], A, by = c("Let"))}, y = LIST)
lapply(seq_along(LIST), function(x, y) {left_join(y[[x == 1]], A, by = c("Let"))}, y = LIST)
Error in y[[x == 1]] : attempt to select less than one element in integerOneIndex
我不完全確定我理解你的問題。
以下解決方案基於lapply(seq_along(LIST), function(x, y) {left_join(y[[x > 0]], A, by = c("Let"))}, y = LIST)
的輸出的再現lapply(seq_along(LIST), function(x, y) {left_join(y[[x > 0]], A, by = c("Let"))}, y = LIST)
你的附言中的lapply(seq_along(LIST), function(x, y) {left_join(y[[x > 0]], A, by = c("Let"))}, y = LIST)
。 請注意,其他lapply
行會拋出錯誤。
library(tidyverse);
map(list(A, B), function(x) left_join(DF, x))
#Joining, by = "Let"
#Joining, by = "Let"
#[[1]]
# Num Let Col
#1 1 a Yellow
#2 2 b Red
#
#[[2]]
# Num Let Col
#1 1 a Green
#2 2 b Blue
我們使用purrr:map
with dplyr::left_join
將A
和B
到DF
。
使用Map
和merge
可以在base R中實現相同的功能:
mapply(function(x) merge(DF, x, by = "Let"), list(A, B), SIMPLIFY = F)
#[[1]]
# Let Num Col
#1 a 1 Yellow
#2 b 2 Red
#
#[[2]]
# Let Num Col
#1 a 1 Green
#2 b 2 Blue
使用base::mapply()
返回有條件合並的數據幀列表。 在這里,我提供兩個輸入:
seq.along( along.with = LIST )
獲取LIST
的元素數量; 和 LIST
本身。 FUN
參數是一個匿名函數,它接受兩個輸入i
和j
- 並在使用base :: merge()執行左連接之前測試LIST
的當前元素是偶數還是奇數。
如果seq.along( along.with = LIST )
第 i 個元素的模運算符的結果等於零,則將左連接B
連接到LIST
第 j 個元素; 如果它不等於零,則在LIST
第 j 個元素上執行左連接A
# load data
DF <- data.frame(Num = c("1","2"), Let = c("a","b"), stringsAsFactors = FALSE)
A <- data.frame(Let = c("a","b"), Col = c("Yellow","Red"), stringsAsFactors = FALSE)
B <- data.frame(Let = c("a","b"), Col = c("Green","Blue"), stringsAsFactors = FALSE)
LIST <- list(DF, DF)
# goal: left join all odd elements in LIST[[j]]
# to `A` and all even elements to `B`
merged.list <-
mapply( FUN = function( i, j )
if( i %% 2 == 0 ){
merge( x = j
, y = B
, by = "Let"
, all.x = TRUE )
} else{
merge( x = j
, y = A
, by = "Let"
, all.x = TRUE )
}
, seq_along( along.with = LIST )
, LIST
, SIMPLIFY = FALSE )
# view results
merged.list
# [[1]]
# Let Num Col
# 1 a 1 Yellow
# 2 b 2 Red
#
# [[2]]
# Let Num Col
# 1 a 1 Green
# 2 b 2 Blue
# end of script #
library( dplyr )
library( purrr )
merged.list <-
map2( .x = seq_along( along.with = LIST )
, .y = LIST
, .f = function( i, j )
if( i %% 2 == 0 ){
left_join( x = j
, y = B
, by = "Let" )
} else{
left_join( x = j
, y = A
, by = "Let" )
})
# view results
merged.list
# [[1]]
# Num Let Col
# 1 1 a Yellow
# 2 2 b Red
#
# [[2]]
# Num Let Col
# 1 1 a Green
# 2 2 b Blue
# end of script #
MauritsEvers可能已經回答了你的問題,但我想我會解決R語法和編程邏輯中的明顯錯誤。 專注於第一次lapply電話:
lapply(seq_along(LIST), function(x, {ifelse((x %% 2)==0, y[[x]] <- 1, y[[x]] <- 2)}, y = LIST)
首先,也許是瑣碎的缺少一個右)
在第一個參數列表lapply
-action。 接下來更基本的是錯誤地使用ifelse
作為編程結構。 ifelse
函數不是為數據對象的串行測試而設計的。 它僅設計為沿單個矢量應用。 if(.){.}else{.}
-function可能應該在該lapply
調用中使用,如果要進行串行選擇的話。
但是,(現在嘗試實現第一段而不是繼續糾正代碼)我認為在LIST
-object上使用邏輯索引(使用R的隱式回收過程)而不是任何循環過程會簡單得多。 (這不是一個整合的解決方案。)此代碼將LIST分為“奇數”和“偶數”組件:
oddList <- LIST[ c(TRUE,FALSE) ] # implicit seq-along by virtue of recycling
evenList <- LIST[ c(FALSE,TRUE) ]
我們可以使用這種類型的結果來制作兩個可以實現既定目標的單線程。 我將LIST對象設為四寬而不是兩寬。
Abig <- Reduce( function(x,y) {merge(x,y,by="Let")}, LIST, init=A)
Warning message:
In merge.data.frame(x, y, by = "Let") :
column names ‘Num.x’, ‘Num.y’ are duplicated in the result
Bbig <- Reduce( function(x,y) {merge(x,y,by="Let")}, LIST, init=B)
Warning message:
In merge.data.frame(x, y, by = "Let") :
column names ‘Num.x’, ‘Num.y’ are duplicated in the result
這只是一個警告,在這里您可以看到它警告的內容:
> Abig
Let Col Num.x Num.y Num.x Num.y
1 a Yellow 1 1 1 1
2 b Red 2 2 2 2
如果你需要那些標記為唯一的重復列名(並且我很瘦,那將是一個好主意),那么:
names(Abig)[ grep("Num", names(Abig)) ] <-
paste0("Num.", seq_along( grep("Num", names(Abig)) ) )
Abig
Let Col Num.1 Num.2 Num.3 Num.4
1 a Yellow 1 1 1 1
2 b Red 2 2 2 2
這個解決方案非常類似於(@MauritsEvers和@aspiringurbandatascientist)已經發布的基於mapply
的解決方案,但它使用了一種不同的方法來join
data.frames。 dplyr::left_join
已被用於滿足目的。
library(dplyr)
# Using mapply and left_join
mapply(function(x,y){
if(y %% 2 == 1){
left_join(x, A, by="Let")
}else {
left_join(x, B, by="Let")
}
}, LIST, seq_along(LIST), SIMPLIFY = FALSE)
# [[1]]
# Num Let Col
# 1 1 a Yellow
# 2 2 b Red
#
# [[2]]
# Num Let Col
# 1 1 a Green
# 2 2 b Blue
為清楚起見,我重復了一些您的示例數據
數據
DF1 <- data.frame(Num1 = c("1","2"), Let = c("a","b"), stringsAsFactors = FALSE)
DF2 <- data.frame(Num2 = c("3","4"), Let = c("a","b"), stringsAsFactors = FALSE)
DF3 <- data.frame(Num3 = c("5","6"), Let = c("a","b"), stringsAsFactors = FALSE)
DF4 <- data.frame(Num4 = c("7","8"), Let = c("a","b"), stringsAsFactors = FALSE)
A <- data.frame(Let = c("a","b"), Col = c("Yellow","Red"), stringsAsFactors = FALSE)
B <- data.frame(Let = c("a","b"), Col = c("Green","Blue"), stringsAsFactors = FALSE)
LIST <- list(DF1, DF2, DF3, DF4)
解
library(dplyr)
library(purrr)
LIST_odd <- LIST[as.logical(seq_along(LIST)%%2)]
LIST_even <- LIST[!as.logical(seq_along(LIST)%%2)]
merge_odd <- reduce(LIST_odd,left_join,.init=A)
# Let Col Num1 Num3
# 1 a Yellow 1 5
# 2 b Red 2 6
merge_even <- reduce(LIST_even,left_join,.init=B)
# Let Col Num2 Num4
# 1 a Green 3 7
# 2 b Blue 4 8
如果你不想使用purrr
那些只用dplyr
和base
給出相同的結果:
Reduce(left_join,LIST_odd,A)
Reduce(left_join,LIST_even,B)
或100%基數:
Reduce(function(x,y) merge(x,y,all.x=TRUE),LIST_odd,A)
Reduce(function(x,y) merge(x,y,all.x=TRUE),LIST_even,B)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.