【问题标题】:Select rows where sum of a column equals a fixed value in R选择列的总和等于 R 中的固定值的行
【发布时间】:2020-05-14 20:46:20
【问题描述】:

我想获得一个(或所有)可能的行组合,其中数量列的总和等于 20

这里是一个例子:

structure(list(id = 1:10, quantity = c(11L, 1L, 4L, 12L, 19L, 10L, 3L, 13L, 16L, 14L)), class ="data.frame", row.names = c(NA,-10L))

id quantity
1   11          
2   1           
3   4           
4   12          
5   19          
6   10          
7   3           
8   13          
9   16          
10  14  

期望的输出(一个可能的集合):

id quantity
3   4           
7   3           
8   13

id quantity
 2  1           
 5  19          

【问题讨论】:

  • 你能补充一下失败的地方吗?
  • 这些组合有哪些规则?
  • 没有固定规则,我只需要满足 sum(quantity)==20 的任意组合
  • thisthis

标签: r


【解决方案1】:

如果组合没问题:

target <- 20
lapply(seq_len(sum(cumsum(sort(x$quantity)) <= target)), function(n) {
  y <- combn(x$quantity, n)
  y[,colSums(y) == target]
})
#[[1]]
#integer(0)
#
#[[2]]
#     [,1] [,2]
#[1,]    1    4
#[2,]   19   16
#
#[[3]]
#     [,1] [,2]
#[1,]    1    4
#[2,]    3    3
#[3,]   16   13
#
#[[4]]
#[1]  1  4 12  3

...并获取行:

lapply(seq_len(sum(cumsum(sort(x$quantity)) <= target)), function(n) {
  y <- combn(x$quantity, n)
  y <- y[,colSums(y) == target, drop = FALSE]
  if(length(y) > 0) {apply(y, 2, match, x$quantity)}
})
#[[1]]
#NULL
#
#[[2]]
#     [,1] [,2]
#[1,]    2    3
#[2,]    5    9
#
#[[3]]
#     [,1] [,2]
#[1,]    2    3
#[2,]    7    7
#[3,]    9    8
#
#[[4]]
#     [,1]
#[1,]    2
#[2,]    3
#[3,]    4
#[4,]    7

... 有点像预期的输出:

lapply(seq_len(sum(cumsum(sort(x$quantity)) <= target)), function(n) {
  y <- combn(x$quantity, n)
  y <- y[,colSums(y) == target, drop = FALSE]
  if(length(y) > 0) {apply(y, 2, function(i) {x[match(i, x$quantity),]})}
})
#[[1]]
#NULL
#
#[[2]]
#[[2]][[1]]
#  id quantity
#2  2        1
#5  5       19
#
#[[2]][[2]]
#  id quantity
#3  3        4
#9  9       16
#
#
#[[3]]
#[[3]][[1]]
#  id quantity
#2  2        1
#7  7        3
#9  9       16
#
#[[3]][[2]]
#  id quantity
#3  3        4
#7  7        3
#8  8       13
#
#
#[[4]]
#[[4]][[1]]
#  id quantity
#2  2        1
#3  3        4
#4  4       12
#7  7        3

数据:

x <- structure(list(id = 1:10, quantity = c(11L, 1L, 4L, 12L, 19L, 10L, 3L, 13L, 16L
  , 14L)), class ="data.frame", row.names = c(NA,-10L))

【讨论】:

  • 我怎样才能得到相关的ID?
  • 我已经进行了更新,它返回了行 - 正如最初要求的那样。
【解决方案2】:

这是另一个基本 R 解决方案,通过定义 递归函数 subsetSum(我想这会更快,因为它避免检查所有组合)

subsetSum <- function(v, target, r = c()) {
    if (sum(r) == target) {
        return(list(r))
    }
    unlist(lapply(seq_along(v), function(k) subsetSum(v[-(1:k)], target, c(r, v[k]))), recursive = FALSE)
}

那么,在运行的时候

target <- 20
lst <- subsetSum(setNames(df$quantity, seq(nrow(df))), target)
res <- Map(function(v) df[as.integer(names(v)), ], lst)

你会得到

> res
[[1]]
  id quantity
2  2        1
3  3        4
4  4       12
7  7        3

[[2]]
  id quantity
2  2        1
5  5       19

[[3]]
  id quantity
2  2        1
7  7        3
9  9       16

[[4]]
  id quantity
3  3        4
7  7        3
8  8       13

[[5]]
  id quantity
3  3        4
9  9       16

如果您只想要一个达到给定值的子集总和,您可以尝试 subsetsum from package adagio

library(adagio)
target <- 20
res <- df[subsetsum(df$quantity,target)$inds,]

给了

> res
  id quantity
2  2        1
5  5       19

【讨论】:

  • 我喜欢你的解决方案,我怎样才能让它在满足求和条件的第一个组合处停止循环?我在 if 语句中添加了一个 break 指令,但没有工作(我必须循环它超过 3000 个产品,每个产品至少数量数组长度为 100)
  • @Seif 我认为您无法进入递归并提取第一个组合。相反,您可以使用包adagio 中的函数subsetsum 来实现它。查看我的更新。
猜你喜欢
  • 2023-01-18
  • 1970-01-01
  • 1970-01-01
  • 2015-10-17
  • 1970-01-01
  • 1970-01-01
  • 2015-01-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多