【问题标题】:R interleave two data frames with same column namesR交错两个具有相同列名的数据帧
【发布时间】:2020-09-20 20:55:18
【问题描述】:

我有两个数据表:

  • before 表示处于“原始”状态(在任何清理操作之前)的数据表。
  • after 表示经过各种清理和操作后的数据表。

它们的列名大多匹配。

是否可以构造第三个数据框,其中具有匹配名称的列彼此相邻放置并且名称可能被修改(name.beforename.after)并且所有多余的列都放置在末尾?

例如:

before数据框:

data.table::data.table(a = c(1,2,3), b = c(1,2,3), c = c(1,2,3))

   a b c
1: 1 1 1
2: 2 2 2
3: 3 3 3

after数据框:

data.table::data.table(a = c("a","b","c"), c = c("a","b","c"), d = c(1,2,3))

   a c d
1: a a 1
2: b b 2
3: c c 3

期望的输出是:

   a.before a.after c.before c.after d
1:        1       a        1       a 1
2:        2       b        2       b 2
3:        3       c        3       c 3

这样做的目的是为了方便比较相同的列,以验证在对 data.table 应用各种函数后列输出是否合适。

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    一个选项是 cbind 并在 ordered 列名称上使用 setcolorder 连接,然后使用 make.unique 如果意图识别重复列名称的之前/之后

    library(data.table)
    out <- setcolorder(cbind(dt1, dt2), order(c(names(dt1), names(dt2))))[]
    setnames(out, make.unique(names(out)))[]
    out[, setdiff(names(dt1), names(dt2)) := NULL][]
    #   a.before a.after c.before c.after d
    #1:        1       a        1       a 1
    #2:        2       b        2       b 2
    #3:        3       c        3       c 3
    

    如果我们需要专门使用before/after

    out <- setcolorder(cbind(dt1, dt2), order(c(names(dt1), names(dt2))))[]    
    out[, setdiff(names(dt1), names(dt2)) := NULL][]
    i1 <- duplicated(names(out), fromLast = TRUE)
    i2 <- duplicated(names(out))
    names(out)[i1] <- paste0(names(out)[i1], ".before")
    names(out)[i2] <- paste0(names(out)[i2], ".after")   
    
    out
    #   a.before a.after c.before c.after d
    #1:        1       a        1       a 1
    #2:        2       b        2       b 2
    #3:        3       c        3       c 3
    

    【讨论】:

    • 两者都是错误的,因为 b 列不在右侧。 "所有多余的列都放在最后"
    • @polkas 谢谢,我错过了那部分。更新了解决方案
    【解决方案2】:

    基础 R 操场:

    
    cols_after <- colnames(after)
    
    cols_before <- colnames(before)
    
    inter <- intersect(cols_after, cols_before)
    
    in_after <- cols_after %in% inter
    
    n_after <- paste0(cols_after[in_after], ".after")
    
    colnames(after)[in_after] <- n_after
    
    in_before <- cols_before %in% inter
    
    n_before <- paste0(cols_before[in_before], ".before")
    
    colnames(before)[in_before] <- n_before
    
    # some merge procedure merge_df or simple cbind
    merge_df <- cbind(after, before)
    
    merge_df_names <- merge_df[, c(as.vector(t(data.frame(n_before ,n_after))), 
    colnames(merge_df)[!(colnames(merge_df) %in% c(n_before, n_after))])]
    
    # if merge_df is data.table we need with = FALSE
    # merge_df[, c(as.vector(t(data.frame(n_before ,n_after))), colnames(merge_df)[!(colnames(merge_df) %in% c(n_before, n_after))]), with = FALSE]
    
    merge_df_names
    
    # to remove b column if needed
    # merge_df_names <- merge_df_names[, setdiff(colnames(merge_df_names), "b")]
    # if merge_df is data.table we need with = FALSE
    

    【讨论】:

    • 根据 OP 的帖子,“b”不应出现在预期的输出中。可能你需要使用setdiff
    • 所以这两种解决方案都是错误的。但是我们都知道它应该在右边。
    • 可能是 OP 的拼写错误。没有把握。最好从OP中澄清一下
    【解决方案3】:

    更新不受影响的列(bd)最后显示。

    这种 tidyverse 方法接受 data.table 对象并返回一个 data.table 对象:

    library(tidyverse)
    
    cols_to_rename <- intersect(colnames(before), colnames(after))
    
    rename_cols <- function(data, suffix) 
      data %>% rename_with(~paste0(., suffix), all_of(cols_to_rename))
    
    bind_cols(rename_cols(before, ".before"), rename_cols(after, ".after")) %>%
      select(starts_with(paste0(cols_to_rename, ".")), everything())
    
       a.before a.after c.before c.after b d
    1:        1       a        1       a 1 1
    2:        2       b        2       b 2 2
    3:        3       c        3       c 3 3
    

    【讨论】:

      猜你喜欢
      • 2017-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-03
      • 2012-04-27
      相关资源
      最近更新 更多