2013-02-06 117 views
4

我的问题与张贴的here非常相似。合并数据帧,同时对R中的公共列进行求和

不同之处在于他们知道将会发生冲突的列,而我需要一种不会事先知道哪些列发生冲突的通用方法。

例如:

TABLE1 
Date    Time ColumnA ColumnB 
01/01/2013  08:00  10   30 
01/01/2013  08:30  15   25 
01/01/2013  09:00  20   20 
02/01/2013  08:00  25   15 
02/01/2013  08:30  30   10 
02/01/2013  09:00  35   5 

TABLE2 
Date   ColumnA ColumnB ColumnC 
01/01/2013  100  300   1 
02/01/2013  200  400   2 

表2只具有日期,并因此被施加到表A无论匹配的日期,关于时间的所有字段。

我想合并总结冲突列进1结果应该是这样的:

TABLE3 
Date    Time ColumnA ColumnB ColumnC 
01/01/2013  08:00  110   330  1 
01/01/2013  08:30  115   325  1 
01/01/2013  09:00  120   320  1 
02/01/2013  08:00  225   415  2 
02/01/2013  08:30  230   410  2 
02/01/2013  09:00  235   405  2 

在我的标准合并只是创建“ColumnA.x”的重复列的那一刻,“ColumnA .y“,”ColumnB.x“,”ColumnB.y“。

任何帮助深表感谢

+0

我可能不会合并。我会rbind.fill然后聚合的关键列与data.table或ddply – frankc

+0

听起来不错,到目前为止,你能否详细说明?合并到目前为止我的能力达到顶峰,尚未使用任何这些功能。 – EvilWeebl

回答

4

如果我理解正确,您需要一个灵活的方法,不需要知道除了要合并的列和要保留的列之外,每个表中存在哪些列。这可能不是最完美的解决方案,但在这里是为了满足您的需求为例功能:

merge_Sum <- function(.df1, .df2, .id_Columns, .match_Columns){ 
    merged_Columns <- unique(c(names(.df1),names(.df2))) 
    merged_df1 <- data.frame(matrix(nrow=nrow(.df1), ncol=length(merged_Columns))) 
    names(merged_df1) <- merged_Columns 
    for (column in merged_Columns){ 
     if(column %in% .id_Columns | !column %in% names(.df2)){ 
      merged_df1[, column] <- .df1[, column] 
     } else if (!column %in% names(.df1)){ 
      merged_df1[, column] <- .df2[match(.df1[, .match_Columns],.df2[, .match_Columns]), column] 
     } else { 
      df1_Values=.df1[, column] 
      df2_Values=.df2[match(.df1[, .match_Columns],.df2[, .match_Columns]), column] 
      df2_Values[is.na(df2_Values)] <- 0 
      merged_df1[, column] <- df1_Values + df2_Values 
     } 
    } 
    return(merged_df1) 
} 

此函数假设你有一个表” .df1' 这是各种各样的高手,你要合并具有匹配'.df1'中的一个或多个行的行的第二个'.df2'表中的数据。从主表'.df1'保留的列被接受为数组'.id_Columns',并且提供用于合并两个表的匹配的列被接受为数组。'match_Columns'

对于您的示例,它的工作是这样的:

merge_Sum(table1, table2, c("Date","Time"), "Date") 

# Date  Time ColumnA ColumnB ColumnC 
# 1 01/01/2013 08:00  110  330  1 
# 2 01/01/2013 08:30  115  325  1 
# 3 01/01/2013 09:00  120  320  1 
# 4 02/01/2013 08:00  225  415  2 
# 5 02/01/2013 08:30  230  410  2 
# 6 02/01/2013 09:00  235  405  2 

在简单的语言,该功能首先找到唯一列的总数量,使在主表中的形状的空数据帧” .df1' 以后保持合并数据。然后,对于'.id_Columns',将数据从'.df1'复制到新的合并数据帧中。对于其他列,存在于'中的任何数据。'.df2'中的任何现有数据都添加了'df1',其中'.df2'中的行基于'.match_Columns'匹配。

可能有一些软件包做类似的操作,但大多数他们需要了解所有现有的专栏以及如何对待它们。正如我之前所说,这不是最优雅的解决方案,但它灵活而准确。

更新:原始函数假设table1和table2之间存在多对一关系,并且OP也请求允许多对一关系。该代码已被更新,但效率稍低,但灵活性更高100%。

+0

这看起来非常出色,我会尝试一下,但是关于'.id_columns'的一个简短问题,我知道它需要日期和时间,因为它们具体,不会被覆盖,但它们似乎是只有一些最初被复制过来,如果我的table1有一个名为columnZ的列在table2中不匹配怎么办?我需要在“id_columns”中指定不匹配的所有列吗? – EvilWeebl

+1

不可以。只有您想要保留的已知列需要位于'.id_Columns'参数中。这些是您希望函数基本忽略的列,并且直接复制。否则,仅存在于一个表中的其他列将被罚款,但只能在计算两个表中的存在并尝试将这些值加在一起之后才能复制。 – Dinre

+0

这是工作出色,我有一个小小的打嗝在表1中,如果有一个日期表的行没有的行,那么公共列的值被设置为NA,而不是采取表1的值,但除此之外它是伟大的。谢谢! – EvilWeebl

3

一个data.table解决方案:

dt1 <- data.table(read.table(header=T, text="Date    Time ColumnA ColumnB 
01/01/2013  08:00  10   30 
01/01/2013  08:30  15   25 
01/01/2013  09:00  20   20 
02/01/2013  08:00  25   15 
02/01/2013  08:30  30   10 
02/01/2013  09:00  35   5")) 

dt2 <- data.table(read.table(header=T, text="Date   ColumnA ColumnB ColumnC 
01/01/2013  100  300   1 
02/01/2013  200  400   2")) 

setkey(dt1, "Date") 
setkey(dt2, "Date") 
# Note: The ColumnC assignment has to be come before the summing operations 
# Else it gives out error (see below) 
dt1[dt2, `:=`(ColumnC = i.ColumnC, ColumnA = ColumnA + i.ColumnA, 
         ColumnB = ColumnB + i.ColumnB)] 

#   Date Time ColumnA ColumnB ColumnC 
# 1: 01/01/2013 08:00  110  330  1 
# 2: 01/01/2013 08:30  115  325  1 
# 3: 01/01/2013 09:00  120  320  1 
# 4: 02/01/2013 08:00  225  415  2 
# 5: 02/01/2013 08:30  230  410  2 
# 6: 02/01/2013 09:00  235  405  2 

我不知道为什么把右端ColumnC分配抛出这个错误。也许MatthewDowle可以解释这个错误的原因。从v1.8.9

dt1[dt2, `:=`(ColumnA = ColumnA + i.ColumnA, ColumnB = ColumnB + i.ColumnB, 
         ColumnC = i.ColumnC)] 

Error in `[.data.table`(dt1, dt2, `:=`(ColumnA = ColumnA + i.ColumnA, : 
    Value of SET_STRING_ELT() must be a 'CHARSXP' not a 'NULL' 

更新:

ö混合添加新的与更新现有列到一个:=()由基;即
DT[,:= (existingCol=...,newCol=...), by=...]
现在可以正常工作,或者 段错误,#2778和#2528。非常感谢Arun报告具有可重复性的例子。添加了测试。

+0

这看起来确实不错,但是您忘记了Table2将成为一个我完全不了解的表,它可能包含匹配或不匹配的列,因此我无法明确选择要绑定的列。也许像for循环匹配列名称的东西? – EvilWeebl

+1

'ColumnC'正被添加到'dt1',但ColumnA和ColumnB正在更新。似乎有一个错误在这里混合添加/更新不喜欢增加在最后出于某种原因。谢谢!已提交[bug#2528](https://r-forge.r-project.org/tracker/index.php?func=detail&aid=2528&group_id=240&atid=975)。 –

+0

有关如何在运行时不知道冲突列的情况下应用此任何想法? – EvilWeebl