如何合并两个数据框以获取面板数据?

3

我有两个数据框,包含一些调查数据,其中有两列ID。一个数据框包含一年的数据,另一个数据框包含另一年的数据。其中一个数据框有一个变量,而另一个没有。以下是这两个数据框的示例:

df1 <- data.frame(id1 = c(1, 1, 2, 2, 3, 3), 
                  id2 = c(1, 2, 1, 2, 1, 2),
                  name = c("foo1", "foo2", "foo3", "foo4", "foo5", "foo6"),
                  variable = c(100, 110, 120, 130, 140, 150),
                  year = rep(2019, 6))

df2 <- data.frame(id1 = c(1, 1, 2, 2, 3, 3), 
                  id2 = c(1, 2, 1, 2, 1, 2),
                  variable = c(200, 210, 220, 230, 240, 250),
                  year = rep(2020, 6))

我希望得到一个包含每个人两个观察值的独特数据框:2019年的观察值和2020年的观察值。这是我尝试过的代码:
total <- bind_rows(df1, df2)
total <- total[order(total$id1, total$id2, total$year), ]
total <- total[c(1, 2, 3, 5, 4)]

而且我得到的数据框是:
 id1 id2 name year variable
1    1   1 foo1 2019      100
7    1   1 <NA> 2020      200
2    1   2 foo2 2019      110
8    1   2 <NA> 2020      210
3    2   1 foo3 2019      120
9    2   1 <NA> 2020      220
4    2   2 foo4 2019      130
10   2   2 <NA> 2020      230
5    3   1 foo5 2019      140
11   3   1 <NA> 2020      240
6    3   2 foo6 2019      150
12   3   2 <NA> 2020      250

这里有两个问题:第一个问题是name单元格中有50%的NA,而我想要重复名称;第二个问题是需要3步(如果我想要重复名称,则需要更多步骤)。
是否有一种函数可以自动按照我想要的方式合并这两个数据框?我希望使用tidyr包中包含的函数,但接受任何其他解决方案。
我知道有很多关于合并两个数据框的问题,但到目前为止,我没有找到我想要的内容,并且我找不到tidyr函数。 编辑:预期输出:
 id1 id2 name year variable
1    1   1 foo1 2019      100
7    1   1 foo1 2020      200
2    1   2 foo2 2019      110
8    1   2 foo2 2020      210
3    2   1 foo3 2019      120
9    2   1 foo3 2020      220
4    2   2 foo4 2019      130
10   2   2 foo4 2020      230
5    3   1 foo5 2019      140
11   3   1 foo5 2020      240
6    3   2 foo6 2019      150
12   3   2 foo6 2020      250
2个回答

2
听起来你只是想要使用rbind()函数将两个数据框合并,但是需要先对df2添加"名称"列。
步骤1:添加"名称"到df2中。
df2 <- merge(df2, df1[,c("id1", "id2", "name")], 
             by=c("id1", "id2"), all.x=TRUE)

步骤二:将两个数据框连接起来。
# base R approach
result <- rbind(df1, df2)

# tidyverse approach
result <- bind_rows(df1, df2)

[optional] Step 3: sort

# base R approach 
result[order(result$name, result$year), ]

# tidyverse approach
result %>%  arrange(name, year)

这并不完全正确,因为result$name重复了序列foo1...foo6两次,而我想要将带有foo1的行放在一起,等等。 - bretauv
然后你只需要排序结果数据框。我会为你添加一个带有排序的“步骤3”。 - DanY
1
好的,它能工作,但需要三个步骤...我在想是否可能只用一步就完成(就像Stata中的merge 1:1 ... using...),所以我点了赞,但不要把你的答案作为解决方案。 - bretauv

2
library(dplyr)
library(tidyr)
bind_rows(df1, df2) %>% group_by(id1, id2) %>% 
    fill(name, .direction = 'down') %>% 
    arrange(id1, id2, year)

   id1 id2 name variable year
1    1   1 foo1      100 2019
2    1   1 foo1      200 2020
3    1   2 foo2      110 2019
4    1   2 foo2      210 2020
5    2   1 foo3      120 2019
6    2   1 foo3      220 2020
7    2   2 foo4      130 2019
8    2   2 foo4      230 2020
9    3   1 foo5      140 2019
10   3   1 foo5      240 2020
11   3   2 foo6      150 2019
12   3   2 foo6      250 2020

为了填充缺失的列而不指定列名,我们可以这样做:
bind_rows(df1, df2) %>% group_by(id1, id2) %>% 
    arrange(id1, id2, year) %>% ungroup() %>%
    fill(select_if(.,~any(is.na(.))) %>% names() ,.direction = 'down')

强大的解决方案:

#tidyr v1.0.0
full_join(df1, df2, by=c('id1', 'id2'), suffix=c('.df1', '.df2')) %>% 
    pivot_longer(cols=matches('.df[12]$'), names_to = c(".value","Ind"), names_sep = "\\.")

仅出于好奇,是否可能在不指定列名的情况下自动填充? - bretauv
1
@bretauv 在 ?fill ... 中说道:_如果为空,则不会发生任何事情..._,但我们可以使用 select_if 进行一些操作,例如 bind_rows(df1, df2) %>% arrange(id1,id2) %>% fill(select_if(.,~any(is.na(.)))%>%names() ,.direction = 'down') - A. Suliman
1
@A.Suliman,“arrange”不容易出错。我们应该使用“group_by”。如果需要排序,可以在最后进行排序。 - M--
@M-- 你说得对,已经更新了。另外我对使用bind_rows有所怀疑,因为可能会漏掉一些ID,所以我加入了另一个解决方案。 - A. Suliman
当应用第二个解决方案(在强健解决方案之前)时,我遇到了错误Erreur:Column `id1` can't be modified because it's a grouping variable。你如何在select_if中指定不使用分组变量? - bretauv
1
@bretauv 抱歉我没有检查更新的解决方案,我们可以修改/更改管道的步骤,请看我的更新。 - A. Suliman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接