【问题标题】:Aggregating while merging two dataframes in R在 R 中合并两个数据帧时进行聚合
【发布时间】:2015-08-12 23:21:32
【问题描述】:

最终目标是将product_info 中每条记录的总量(transact_data$qty) 相加,其中transact_data$productId 存在于product_info 中,其中transact_data$date 介于product_info$beg_date 和product_info$end_date 之间。

数据框如下:

product_info <- data.frame(productId = c("A", "B", "A", "C","C","B"), 
                      old_price = c(0.5,0.10,0.11,0.12,0.3,0.4),
                      new_price = c(0.7,0.11,0.12,0.11,0.2,0.3),
                      beg_date = c("2014-05-01", "2014-06-01", "2014-05-01", "2014-06-01","2014-05-01", "2014-06-01"),
                      end_date = c("2014-05-31", "2014-06-31", "2014-05-31", "2014-06-31","2014-05-31", "2014-06-31"), stringsAsFactors=FALSE)

transact_data <- data.frame(productId=c('A', 'B','A', 'C','A', 'B','C', 'B','A', 'C','A', 'B'),
                  date=c("2014-05-05", "2014-06-22", "2014-07-05", "2014-08-31","2014-05-03", "2014-02-22",
                    "2014-05-21", "2014-06-19", "2014-03-09", "2014-06-22","2014-04-03", "2014-07-08"),
                    qty =c(12,15,5,21,13,17,2,5,11,9,6,4), stringsAsFactors=FALSE)

我的第一步是按 productId 合并两个数据框:

sku_transact_merge <-merge(x=product_info, y=transact_data, by = c("productId"))

下一步是计算数量总和:

sku_transact_merge$total_qty <- ifelse(sku_transact_merge$date >= sku_transact_merge$beg_date & 
                                       sku_transact_merge$date <= sku_transact_merge$end_date, 
                                     aggregate(qty ~ productId+beg_date+end_date,
                                               data= sku_transact_merge, sum), 0)

结果不是我想要的,我收到一个错误提示

(list) 对象不能被强制输入'double'

任何有关如何正确执行此逻辑的指针将不胜感激!

【问题讨论】:

  • 您是否有意在product_info 中有重复的productId,beg_date,end_date 记录(即A,2014-05-01,2014-05-31 和B,2014-06-01,2014-06-31)?这将导致transact_data 中的记录被merge() 调用复制,从而在最终的sum() 期间多次求和,我无法想象这是正确的。
  • 是的,这是故意的。
  • 您要聚合那些重复的qty 值吗?如果您想要的输出是productId,beg_date,end_date 唯一的表,那么那些重复的qty 值将全部汇总到相同的输出记录中,我仍然无法想象这是正确的。我不清楚你想要的输出。
  • 或者您想要product_info 中的每条记录一个total_qty 值吗?
  • 是的,我正在寻找每条记录的总数量。

标签: r merge data.table aggregate dplyr


【解决方案1】:

这可能是使用dplyr() 的另一种方法(如果您的数据集很大,这应该是有效的)

library(dplyr)
df = subset(sku_transact_merge, date > beg_date & date < end_date)
df = subset(df, select= -c(date))
out = unique(df %>% group_by(productId,old_price) %>% mutate(qty = sum(qty)))

#> out
#Source: local data frame [6 x 6]
#Groups: productId, old_price

#productId old_price new_price   beg_date   end_date qty
#1         A      0.50      0.70 2014-05-01 2014-05-31  25
#2         A      0.11      0.12 2014-05-01 2014-05-31  25
#3         B      0.10      0.11 2014-06-01 2014-06-31  20
#4         B      0.40      0.30 2014-06-01 2014-06-31  20
#5         C      0.12      0.11 2014-06-01 2014-06-31   9
#6         C      0.30      0.20 2014-05-01 2014-05-31   2

否则你可以使用data.table

library(data.table)
out = setDT(df)[, list(qtynew = sum(qty)), by = list(productId, old_price)]

#> out
#   productId old_price qtynew
#1:         A      0.50     25
#2:         A      0.11     25
#3:         B      0.10     20
#4:         B      0.40     20
#5:         C      0.12      9
#6:         C      0.30      2

【讨论】:

  • -6 是什么意思?如果它将我的结果限制为 6x6 矩阵,那么如果我有超过 6 条记录,它将无法扩展。还是只是列数?
  • 这听起来可能很愚蠢,但为什么我们在 R 中使用 "=" 来返回。为什么不使用 "
  • 在 R 脚本中,= 和 &lt;- 都可以用作分配运算符。我更喜欢=,因为它很容易输入!如果您有兴趣了解更多信息,请查看此讨论here
  • 是的,它似乎奏效了。我现在正在做一些质量检查。谢谢。
  • 哈哈当然。我正在测试其他解决方案,并将勾选首选。谢谢。
【解决方案2】:

一种方法是遍历product_info 中的元素,确定transact_data 中的所有匹配产品并将它们的数量相加:

sapply(seq(nrow(product_info)), function(x) {
  d <- product_info[x,]
  sum(transact_data$qty[transact_data$productId == d$productId &
                        transact_data$date >= d$beg_date &
                        transact_data$date <= d$end_date])
})
# [1] 25 20 25  9  2 20

如果需要,您可以将其添加为 product_info 中的新列。

【讨论】:

  • 谢谢。我尝试了您的解决方案并尝试将这些值附加到 product_info 中的新列。我得到一个“错误级别的因素集不同”
  • @user3116753 尝试使用as.character 函数将变量转换为字符串。
【解决方案3】:
product_info$total_qty <- aggregate(col~row,which(outer(product_info$productId,transact_data$productId,`==`)&outer(product_info$beg_date,transact_data$date,`<=`)&outer(product_info$end_date,transact_data$date,`>=`),arr.ind=T),function(x) sum(transact_data$qty[x]))$col;
product_info;
##   productId old_price new_price   beg_date   end_date total_qty
## 1         A      0.50      0.70 2014-05-01 2014-05-31        25
## 2         B      0.10      0.11 2014-06-01 2014-06-31        20
## 3         A      0.11      0.12 2014-05-01 2014-05-31        25
## 4         C      0.12      0.11 2014-06-01 2014-06-31         9
## 5         C      0.30      0.20 2014-05-01 2014-05-31         2
## 6         B      0.40      0.30 2014-06-01 2014-06-31        20

说明

首先,为三个匹配条件中的每一个构造一个逻辑矩阵,使用outer() 将product_info 中的每条记录与transact_data 中的每条记录进行比较。这三个逻辑矩阵进行逻辑与运算,形成一个最终的逻辑矩阵,表示哪些记录组合匹配。

outer(product_info$productId,transact_data$productId,`==`)
&outer(product_info$beg_date,transact_data$date,`<=`)
&outer(product_info$end_date,transact_data$date,`>=`)
##       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10] [,11] [,12]
## [1,]  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [2,] FALSE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE
## [3,]  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [4,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
## [5,] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
## [6,] FALSE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE

然后,通过使用arr.ind=T 调用which() 来确定带有TRUE 的行和列索引。行索引表示来自product_info 的匹配记录(因为它位于outer() 调用的左侧),列索引表示来自transact_data 的匹配记录。

which(...,arr.ind=T)
##       row col
##  [1,]   1   1
##  [2,]   3   1
##  [3,]   2   2
##  [4,]   6   2
##  [5,]   1   5
##  [6,]   3   5
##  [7,]   5   7
##  [8,]   2   8
##  [9,]   6   8
## [10,]   4  10

由于我们想为product_info 中的每条记录从transact_data 中求和qty 值,我们可以通过编写自定义聚合函数来索引transact_data$qty 来为col 索引按row 分组的aggregate()使用col 索引和sum() 它们为每个row 返回一个值。

aggregate(col~row,...,function(x) sum(transact_data$qty[x]))
##   row col
## 1   1  25
## 2   2  20
## 3   3  25
## 4   4   9
## 5   5   2
## 6   6  20

最后,我们可以直接将结果赋值给product_info$total_qty,完成求解。

product_info$total_qty <- ...$col;

我不完全确定是否保证aggregate() 将始终返回按分组列排序的结果。我刚刚在Does aggregate() guarantee that the result will be ordered by the grouping columns?问过这个问题。

另外,我刚刚意识到,如果不是product_info 中的所有记录在transact_data 中至少有一条匹配记录,那么直接分配将失败。

如果违反了这些假设中的任何一个,则可以将解决方案固定如下:

product_info$total_qty <- with(aggregate(col~row,which(outer(product_info$productId,transact_data$productId,`==`)&outer(product_info$beg_date,transact_data$date,`<=`)&outer(product_info$end_date,transact_data$date,`>=`),arr.ind=T),function(x) sum(transact_data$qty[x])),col[match(1:nrow(product_info),row)]);
product_info;
##   productId old_price new_price   beg_date   end_date total_qty
## 1         A      0.50      0.70 2014-05-01 2014-05-31        25
## 2         B      0.10      0.11 2014-06-01 2014-06-31        20
## 3         A      0.11      0.12 2014-05-01 2014-05-31        25
## 4         C      0.12      0.11 2014-06-01 2014-06-31         9
## 5         C      0.30      0.20 2014-05-01 2014-05-31         2
## 6         B      0.40      0.30 2014-06-01 2014-06-31        20

现在,我们必须构造一个长度等于product_info 和match() 和qty 总和(在@987654361 内)的完整向量,而不是取消引用$col 的最后一步@) 到它们对应的索引(在row 内),在with() 的帮助下。

product_info$total_qty <- with(...,col[match(1:nrow(product_info),row)]);

【讨论】:

  • 当前机器使用您的方法内存不足,因此将在另一台机器上测试并回复您。
  • 不。我喜欢这个解决方案,但是当我尝试运行它时,我的机器实际上内存不足。这不是你的解决方案。这台机器该走了。感谢您的帮助。
猜你喜欢
  • 2018-05-31
  • 1970-01-01
  • 2014-04-14
  • 2012-03-07
  • 1970-01-01
  • 1970-01-01
  • 2015-03-16
  • 2019-03-29
  • 2018-03-04
相关资源
最近更新 更多