【问题标题】:Merging overlapping dataframes in R在 R 中合并重叠的数据帧
【发布时间】:2015-07-02 18:02:35
【问题描述】:

好的,所以我有两个不同的数据框(df1 和 df2),为了简化,它们有一个 ID、一个日期和一个测试分数。在每个数据框中,人 (ID) 已在多个日期进行了测试。在两个数据框之间查看时,有些人在 df1 中列出但不在 df2 中,反之亦然,但有些人在两者中都列出,并且它们可以不同地重叠。

我想将所有数据合并到一个帧中,但棘手的部分是如果 df1 和 df2 中的任何 ID 和分数在 7 天内(我可以使用减去的日期列来做到这一点),我想合并那一行。

本质上,对于每个 ID,如果在 7 天内获得两个分数,则会有一行分别写出,如果不是,它将分成两行,一行来自 df1,一行来自 df2 以及所有其他分数可能不会在两者中列出。

前:

df1

ID Date1(yyyymmdd) Score1
1  20140512        50
1  20140501        30
1  20140703        50
1  20140805        20
3  20140522        70
3  20140530        10

df2

ID Date2(yyyymmdd) Score2
1  20140530        40
1  20140622        20
1  20140702        10
1  20140820        60
2  20140522        30
2  20140530        80

Wanted_df

ID Date1(yyyymmdd) Score1 Date2(yyyymmdd) Score2
1  20140512        50                     
1  20140501        30
1  20140703        50     20140702        10
1  20140805        20
1                         20140530        40
1                         20140622        20
1                         20140820        60
3  20140522        70
3  20140530        10
2                         20140522        30
2                         20140530        80

【问题讨论】:

  • 请显示小示例数据集和基于该示例的预期结果
  • 正如您在这条线上看到的那样,测试是在 7 天内进行的,因此将其合并为一行,而其他的则没有,因此它们被分成不同的行。我希望这个例子有所帮助。
  • 它的重现性仍然不高,但我认为它不值得投反对票
  • 是否可以安全地假设每个 test1 最多有一个测试 2,反之亦然?或者有人可以在同一周内进行 2 个 test2,并且您希望每个 test1 的重复值......因为外部连接似乎不可能,我想知道是否有循环方法
  • @C8H10N4O2 是的,对于每个测试 1,最多有一个测试 2 可以与之匹配(人们最多每周参加一次测试,所以如果我这样做

标签: r merge dataframe


【解决方案1】:

好的。我对虚假的外部连接答案感到难过(这可能在我不知道的库中,但有时使用 RDBMS 有好处......)所以这是一个 hacky 解决方法。它假设所有的连接最多是一对一的,你说的没问题。

# ensure the date columns are date type
df1$Date1 <- as.Date(as.character(df1$Date1), format="%Y%m%d")
df2$Date2 <- as.Date(as.character(df2$Date2), format="%Y%m%d")

# ensure the dfs are sorted 
df1 <- df1[order(df1$ID, df1$Date1),]
df2 <- df2[order(df2$ID, df2$Date2),]

# initialize the output df3, which starts as everything from df1 and NA from df2
df3 <- cbind(df1,Date2=NA, Score2=NA)

library(plyr) #for rbind.fill

for (j in 1:nrow(df2)){
  # see if there are any rows of test1 you could join test2 to
  join_rows <- which(df3[,"ID"]==df2[j,"ID"] & abs(df3[,"Date1"]-df2[j,"Date2"])<7 )
  # if so, join it to the first one (see discussion)
  if(length(join_rows)>0){
    df3[min(join_rows),"Date2"] <- df2[j,"Date2"]
    df3[min(join_rows),"Score2"] <- df2[j,"Score2"]
  } # if not, add a new row of just the test2
  else df3 <- rbind.fill(df3,df2[j,])
}
df3 <- df3[order(df3$ID,df3$Date1,df3$Date2),]
row.names(df3)<-NULL # i hate these
df3  
#    ID      Date1 Score1      Date2 Score2
# 1   1 2014-05-01     30       <NA>     NA
# 2   1 2014-05-12     50       <NA>     NA
# 3   1 2014-07-03     50 2014-07-02     10
# 4   1 2014-08-05     20       <NA>     NA
# 5   1       <NA>     NA 2014-05-30     40
# 6   1       <NA>     NA 2014-06-22     20
# 7   1       <NA>     NA 2014-08-20     60
# 8   2       <NA>     NA 2014-05-22     30
# 9   2       <NA>     NA 2014-05-30     80
# 10  3 2014-05-22     70       <NA>     NA
# 11  3 2014-05-30     10       <NA>     NA

我无法以与您相同的排序顺序获取行,但它们看起来相同。

简短说明:对于 df2 中的每一行,查看 df1 中是否有一行您可以“加入”它。如果没有,把它贴在桌子的底部。在初始化和 rbinding 中,您会看到一些将空白行或列分配为占位符的 hacky 方法。

为什么这是一个糟糕的解决方法:对于大型数据集,df3 到自身的 rbinding 会消耗越来越多的内存。该循环绝对不是最优的,并且它的搜索没有利用表已排序的事实。如果有机会在一周内进行了两次测试,您会看到一些意想不到的行为(来自 df2 的重复等)。

【讨论】:

  • 非常感谢。到目前为止它工作得很好,但我有一个问题是每个数据框中还有一些变量。我需要将它们添加到您第一次创建 df3 的位置还是会自动绑定其余行?
  • 然后在“if”语句下为我想要保留的 df2 中的每个变量添加一行新代码?
  • 是的,是的——我想不出一种更简单、更轻松的方法。不幸的是,没有 cbind.fill() 这样的东西(到 cbind 不等长度的 dfs 并用 NA 填充其余部分)
【解决方案2】:

使用外部联接,对日期差有绝对值限制。 (A outer join B 保留 A 和 B 的所有行。)例如:

library(sqldf)
sqldf("select a.*, b.* from df1 a outer join df2 b on a.ID = b.ID and abs(a.Date1 - b.Date2) <=7")

请注意,您的日期变量必须是真实日期。如果它们当前是字符或整数,则需要执行df1$Date1 &lt;- as.Date(as.character(df$Date1), format="%Y%M%D) 等操作。

【讨论】:

  • 我从来没有使用过这个函数,所以我有点困惑我在那个方程中输入数据和变量的位置。此外,该日期格式更改不起作用(只是全部更改为 NA)。
  • 还说目前不支持 RIGHT 和 FULL OUTER 连接
  • 废话。那么你对基本的sqldf 不走运。对于自定义合并(不是公用键)的外连接,您可以查看this question,特别是有关 GenomicRanges 的答案。
猜你喜欢
  • 2023-03-05
  • 2018-06-29
  • 1970-01-01
  • 2020-08-29
  • 2017-05-10
  • 2014-04-04
  • 1970-01-01
  • 1970-01-01
  • 2017-01-15
相关资源
最近更新 更多