【问题标题】:Master view of multiple dataframes with common columns具有公共列的多个数据框的主视图
【发布时间】:2020-12-17 14:36:15
【问题描述】:

我有如下三个数据框:

df3 <- data.frame(col1=c('A','C','E'),col2=c(4,8,2))
df2 <- data.frame(col1=c('A','B','C','E','I'),col2=c(4,6,8,2,9))
df1 <- data.frame(col1=c('A','D','C','E','I'),col2=c(4,7,8,2,9))

任意两个文件的区别如下:

anti_join(df2, df3)
# Joining, by = c("col1", "col2")
#   col1 col2
# 1    B    6
# 2    I    9

anti_join(df3, df2)
# Joining, by = c("col1", "col2")
# [1] col1 col2
# <0 rows> (or 0-length row.names)

anti_join(df1, df2)
# Joining, by = c("col1", "col2")
#   col1 col2
# 1    D    7

anti_join(df2, df1)
# Joining, by = c("col1", "col2")
#   col1 col2
# 1    B    6

我想创建一个主数据框,其中包含特定于每个数据框的 col1col2 中的所有值。如果不存在这样的值,它应该填充NA

  col1 df1_col2 df2_col2 df3_col2
1    A        4        4        4 
2    B       NA        6       NA  
3    C        8        8        8
4    E        2        2        2 
5    I        9        9       NA
6    D        7       NA       NA

上述输出的本质可以从上述anti_join 命令建立。但是,它并没有立即提供完整的图片。关于如何实现这一点的任何想法?

编辑:对于col2 中的多个值,对于col1,输出有点混乱。例如,A 具有值 43

df3 <- data.frame(col1=c('A','C','E'),col2=c(4,8,2))
df2 <- data.frame(col1=c('A','A','B','C','E','I'),col2=c(4,3,6,8,2,9))
df1 <- data.frame(col1=c('A','A','D','C','E','I'),col2=c(4,3,7,8,2,9))

lst_of_frames <- list(df1 = df1, df2 = df2, df3 = df3)
lst_of_frames %>%
  imap(~ rename_at(.x, -1, function(z) paste(.y, z, sep = "_"))) %>%
  reduce(full_join, by = "col1")

它给出以下输出。

#   col1 df1_col2 df2_col2 df3_col2
# 1    A        4        4        4
# 2    A        4        3        4
# 3    A        3        4        4
# 4    A        3        3        4
# 5    D        7       NA       NA
# 6    C        8        8        8
# 7    E        2        2        2
# 8    I        9        9       NA
# 9    B       NA        6       NA

输出中有趣的部分是:

#   col1 df1_col2 df2_col2 df3_col2
# 1    A        4        4        4
# 2    A        4        3        4
# 3    A        3        4        4
# 4    A        3        3        4

而预期的输出是:

#   col1 df1_col2 df2_col2 df3_col2
# 1    A        4        4        4
# 2    A        3        3       NA

【问题讨论】:

    标签: r dplyr anti-join


    【解决方案1】:

    您可以使用dplyr 包中的full_join 函数。

    df_master <- df1 %>% 
      full_join(df2, by = "col1") %>% 
      full_join(df3, by = "col1") %>% 
      select(col1, df1_col2 = col2.x, 
             df2_col2 = col2.y,
             df3_col2 = col2)
    
      col1 df1_col2 df2_col2 df3_col2
    1    A        4        4        4
    2    D        7       NA       NA
    3    C        8        8        8
    4    E        2        2        2
    5    I        9        9       NA
    6    B       NA        6       NA
    

    【讨论】:

    • 稍微扩展一下,如果需要对任意数量的帧执行此操作,那么Reduce(function(a, b) full_join(a, b, by = "col1"), lst_of_frames)(此处为lst_of_frames &lt;- list(df1, df2, df3))。
    • list(df1, df2, df3) %&gt;% reduce(full_join, by = "col1")也!
    • 是的,基本 R 的 Reducepurrr::reduce 之间差别很小。最大的(对我而言)是引入任意论点,正如您在那里展示的那样。
    【解决方案2】:

    类似于@tamtam 的答案,但如果您有动态的帧列表,则有点程序化。

    lst_of_frames <- list(df1 = df1, df2 = df2, df3 = df3)
    # lst_of_frames <- tibble::lst(df1, df2, df3)    # thanks, @user63230
    library(dplyr)
    library(purrr)  # imap, reduce
    lst_of_frames %>%
      imap(~ rename_at(.x, -1, function(z) paste(.y, z, sep = "_"))) %>%
      reduce(full_join, by = "col1")
    #   col1 df1_col2 df2_col2 df3_col2
    # 1    A        4        4        4
    # 2    D        7       NA       NA
    # 3    C        8        8        8
    # 4    E        2        2        2
    # 5    I        9        9       NA
    # 6    B       NA        6       NA
    

    框架列表是一个命名列表,这一点很重要(对于自动重命名列);我的假设是框架变量 list(df1=df1) 的名称,但它也可以很容易地是 list(A=df1) 以最终生成一个名为 A_col2 的列。

    【讨论】:

    • +1,我的意思是在下面的评论中使用lstlst_of_frames &lt;- tibble::lst(df1, df2, df3) 以节省必须命名 dfs
    • @r2evans 你能看看我的编辑吗?我已经用另一个场景更新了它。
    • 您所要求的超出了join 的心态。因为您的两个新框架各有两个 "A",所以您将获得更大的连接。有关问题,请参阅full_join(df1, df2, by = "col1")。使用连接,当有多个相同的键(by="col1"),那么它变成“乘法”。您需要更好地定义框架连接方式(以便抢占它),或者提出额外的filtering 规则(在所有连接之后或每次连接之后),这样就不会发生这种情况。由于您的 4 到 2 行减少似乎有些武断,因此我不能真正建议那里。
    • 非常感谢您的帮助。 4 比 2 行减少背后的想法是:避免多个相同键的组合,并且仅在数据集中匹配它们。在这种情况下,A 有多个值(43),我不希望在输出中包含这些值的组合。相反,它们是否与其他匹配只有一次,否则NA。我有两个值,应该只产生两行。我希望,我在这里很清楚;)
    • 唯一可能在这里工作的逻辑是加入后过滤器,但我没有看到说明要保留四种可能组合中的哪一种的逻辑。您加入的目的是引入"col2",因此您不能在加入中使用它。如果交换df2 的第 1-2 行会发生什么?您需要的行是否相同? (实际上,我不知道该问题的答案是否会改变我的信念:merge/join 是错误的;或者还有更多信息可以通知加入后过滤器。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 2023-01-12
    • 1970-01-01
    • 2011-02-13
    • 1970-01-01
    • 2023-01-19
    相关资源
    最近更新 更多