部分连接两个数据帧/元组

时间:2018-01-31 12:50:24

标签: r join tidyverse

我有两个数据帧/元组。第一个是具有描述这些国家的若干变量的国家列表。此数据框包含多个缺失值。缺少哪些变量取决于国家。

library(tidyverse)

df1<-data.frame(id=1:10, 
                country=c("A","A","A","A","B","B","C","C","C","C"), 
                var1=c(NA,NA,NA,NA,1,1,2,1,2,1), 
                var2=c(1,1,2,2,NA,NA,1,2,2,2),
                var3=c("NO","YES","NO","YES","NO","NO",NA,NA,NA,NA),
                var4=c(NA,NA,NA,NA,"NO","NO",NA,NA,NA,NA)
               )
df1<-as_tibble(df1)

然后我有第二个数据框(df2)我想加入第一个表:

df2<-data.frame(id=c(2,3,5,6,7,8,9,10),
                country=c("A", "A", "B", "B", "C", "C", "C", "C"),
                var1=c(1,2,2,2,2,1,2,1),
                var2=c(2,1,1,1,1,2,1,1),
                var3=c("NO","NO", "YES", "NO", "NO", "NO", "YES","NO"),
                var4=c("YES", "NO", "NO", "YES", "YES", "NO", "NO", "YES")
               )
df2<-as_tibble(df2)

最后,我想要的是第一个使用第二个数据帧完成缺失值的数据帧。所以我想使用id - 变量加入两个表。但是,此连接应仅“部分”,因为缺少哪些变量取决于国家/地区:例如对于国家/地区“A”,只应填写变量var1var4。在国家/地区“C”中,应从df2填写变量var3var4。 df1包含的案例多于df2。

有谁能告诉我哪个是解决这个问题的最佳解决方案?

非常感谢!

2 个答案:

答案 0 :(得分:3)

这是提供数据的一种潜在解决方案。我在两个数据框中添加了stringsAsFactors = FALSE。看到这些数据,我想你会想要绑定它们而不是加入它们。绑定数据后,我按idcountryindex对其进行了排序。 index表示来自哪个数据帧数据。然后,我按idcountry创建了群组。对于具有两行的组,第一行具有您要填充的目标NA。这些NA保留在四列中(即var1-4)。我在na.locf()包中应用zoo来执行填充过程。对于每个组,第一行来自df1,您希望保留它们。我选择在这里使用distinct()。但slice(1)是另一种选择。

df1 <- data.frame(id=1:10, 
                  country=c("A","A","A","A","B","B","C","C","C","C"), 
                  var1=c(NA,NA,NA,NA,1,1,2,1,2,1), 
                  var2=c(1,1,2,2,NA,NA,1,2,2,2),
                  var3=c("NO","YES","NO","YES","NO","NO",NA,NA,NA,NA),
                  var4=c(NA,NA,NA,NA,"NO","NO",NA,NA,NA,NA),
                  stringsAsFactors = F)

 df2 <- data.frame(id=c(2,3,5,6,7,8,9,10),
                   country=c("A", "A", "B", "B", "C", "C", "C", "C"),
                   var1=c(1,2,2,2,2,1,2,1),
                   var2=c(2,1,1,1,1,2,1,1),
                   var3=c("NO","NO", "YES", "NO", "NO", "NO", "YES","NO"),
                   var4=c("YES", "NO", "NO", "YES", "YES", "NO", "NO", "YES"),
                  stringsAsFactors = F)

 library(dplyr)
 library(zoo)

bind_rows(df1, df2, .id = "index") %>%
arrange(id, country, index) %>%
group_by(id, country) %>%
mutate_at(vars(var1:var4), funs(if(n() > 1) {na.locf(., fromLast = TRUE)} else {.})) %>%
distinct(id, .keep_all = TRUE) %>%
select(-index)



      id country  var1  var2 var3  var4 
   <dbl> <chr>   <dbl> <dbl> <chr> <chr>
 1  1.00 A       NA     1.00 NO    <NA> 
 2  2.00 A        1.00  1.00 YES   YES  
 3  3.00 A        2.00  2.00 NO    NO   
 4  4.00 A       NA     2.00 YES   <NA> 
 5  5.00 B        1.00  1.00 NO    NO   
 6  6.00 B        1.00  1.00 NO    NO   
 7  7.00 C        2.00  1.00 NO    YES  
 8  8.00 C        1.00  2.00 NO    NO   
 9  9.00 C        2.00  2.00 YES   NO   
10 10.0  C        1.00  2.00 NO    YES  

答案 1 :(得分:1)

更新了保留类型但需要一些文字代码的建议。

rename_at(df2, vars(starts_with("var")), ~ paste0("new", .)) %>%
  select(-country) %>%
  right_join(df1, by = "id") %>%
  mutate(
    var1 = if_else(is.na(var1), newvar1, var1),
    var2 = if_else(is.na(var2), newvar2, var2),
    var3 = if_else(is.na(var3), newvar3, var3),
    var4 = if_else(is.na(var4), newvar4, var4)
  ) %>%
  select(-starts_with("newvar"))

使用的另一种方法是在相关列名称上循环(在管道外部):

df3 <- rename_at(df2, vars(starts_with("var")), ~ paste0("new", .)) %>%
  select(-country) %>%
  right_join(df1, by = "id")
for (v in colnames(df1)[ grepl("^var", colnames(df1)) ]) {
  df3[[v]] <- if_else(is.na(df3[[v]]), df3[[ paste0("new", v) ]], df3[[v]])
}
select(df3, -starts_with("newvar"))

编辑:oops,刚刚意识到&#34; var&#34;列是混合类型。如果所有内容都相同,则以下答案有效,但不在此处。使用前面的代码会保留类型。

如果你重命名&#34; var&#34; df2中的变量,您可以对df1&#34; var&#34;进行并排比较和重新分配。变量。一种方法可能是使用dplyr::mutate_ifstarts_with("var"),但这对您的数据提出了可能过于严格的要求。

我建议使用中间体&#34; tall&#34; (与#34;广泛的#34;)格式,以便var1var4进行广义处理;这样,如果你真的有更多,你就不需要遍历每个变量。

假设:df2$id应该足够,id$country是不必要的。

library(dplyr)
library(tidyr)

df1<-data_frame(id=1:10, 
                country=c("A","A","A","A","B","B","C","C","C","C"), 
                var1=c(NA,NA,NA,NA,1,1,2,1,2,1), 
                var2=c(1,1,2,2,NA,NA,1,2,2,2),
                var3=c("NO","YES","NO","YES","NO","NO",NA,NA,NA,NA),
                var4=c(NA,NA,NA,NA,"NO","NO",NA,NA,NA,NA)
               )

df2<-data_frame(id=c(2,3,5,6,7,8,9,10),
                country=c("A", "A", "B", "B", "C", "C", "C", "C"),
                var1=c(1,2,2,2,2,1,2,1),
                var2=c(2,1,1,1,1,2,1,1),
                var3=c("NO","NO", "YES", "NO", "NO", "NO", "YES","NO"),
                var4=c("YES", "NO", "NO", "YES", "YES", "NO", "NO", "YES")
                )

select(df2, -country) %>%
  gather(k, newv, -id) %>%
  right_join(gather(df1, k, v, -id, -country), by = c("id", "k")) %>%
  mutate(v = ifelse(is.na(v), newv, v)) %>%
  select(-newv) %>%
  spread(k, v)
# # A tibble: 10 × 6
#       id country  var1  var2  var3  var4
# *  <dbl>   <chr> <chr> <chr> <chr> <chr>
# 1      1       A  <NA>     1    NO  <NA>
# 2      2       A     1     1   YES   YES
# 3      3       A     2     2    NO    NO
# 4      4       A  <NA>     2   YES  <NA>
# 5      5       B     1     1    NO    NO
# 6      6       B     1     1    NO    NO
# 7      7       C     2     1    NO   YES
# 8      8       C     1     2    NO    NO
# 9      9       C     2     2   YES    NO
# 10    10       C     1     2    NO   YES