将向量中的值与所有其他向量进行比较

3
假设有以下数据集:
+---------------+-----------+---------------------+ | flightCarrier | saleTotal | daysBeforeDeparture | +---------------+-----------+---------------------+ | KL | 477.99 | 0 | | AF | 457.99 | 0 | | SQ | 556.31 | 0 | +---------------+-----------+---------------------+
我想要做的是:
1. 将列中的一个值与同一列中的所有其他值进行比较。 2. 如果saleTotal(1)小于saleTotal(2)和saleTotal(3)的值,则怎么办? 3. 如果是,差多少?saleTotal(3)/saleTotal(1)
工单如下:
- 477.99 < 457.99 (false) - 477.99 < 556.31 (true) -> (556.31/477.99)-1=1.16 (增加了16%) - 457.99 < 477.99 (true) -> ..... - 457.99 < 556.31 (true) -> ..... - 556.31 < 477.99 (false) - 556.31 < 457.99 (false)
我尝试过的方法:
cal <- apply(df_matrix[1:2,2], 1, function(x) {
  A <- x
  x <- x[-1]
  ifelse(x>A, 1, ifelse(x<A, 0, NA))
})
cal

这没有成功,输出“logical(0)”,所以我猜没有结果。 我尝试了很多方法,使用lapply、mapply,但似乎都是比较静态数字而不是前一行。
从apply中我了解到的是,每个X都有它正在“迭代”的行。这就是为什么我尝试将X与包含所有saleTotal值的整个向量A进行比较。因此,逐个迭代。
期望输出 业务输出:“价格比其他XY个价格便宜”
我想这可能是避免大矩阵并尽可能保持内存低的最佳方法) 是否可能直接“nrow()”结果,而不是先创建矩阵/列表?
任何想法如何做到这一点?对于性能,我有100000+行?
编辑:预期输出(一种方式)

3
看一下outer(df$saleTotal, df$saleTotal, "/"),值大于1表示增长,小于1表示下降,矩阵的对角线是所有的1,因为它将每个值与其自身进行比较。 - talat
1
想要的输出是什么?那些产生“FALSE”的<比较会发生什么?您的“工作订单”表明需要一个简单的循环,例如lapply(seq_along(x), function(i) x[-i][x[[i]] < x[-i]] / x[[i]]) - alexis_laz
2
@s1x 展示你的示例输入的预期输出... - Tensibai
4
看起来你需要使用findInterval函数;length(x) - findInterval(x, sort(x))应该足够高效。 - alexis_laz
2
@alexis_laz,你应该将这个作为答案发布,如果有人对解决方案感到好奇并进行基准测试,那就太棒了(我认为findInterval会更快);p)。 - Tensibai
显示剩余2条评论
2个回答

5
您可以像这样使用?outer:
outer(df$saleTotal, df$saleTotal, "/")
#          [,1]     [,2]      [,3]
#[1,] 1.0000000 1.043669 0.8592152
#[2,] 0.9581581 1.000000 0.8232640
#[3,] 1.1638528 1.214677 1.0000000

大于1的值表示增加,小于1的值表示减少,矩阵的对角线都是1,因为它将每个值与自己进行比较。

当然,您可以通过使用以下代码仅显示大于1的值:

res <- outer(df$saleTotal, df$saleTotal, "/")
res * as.integer(res > 1)
#         [,1]     [,2] [,3]
#[1,] 0.000000 1.043669    0
#[2,] 0.000000 0.000000    0
#[3,] 1.163853 1.214677    0

或者,如果你只需要一个逻辑矩阵:

res > 1
#      [,1]  [,2]  [,3]
#[1,] FALSE  TRUE FALSE
#[2,] FALSE FALSE FALSE
#[3,]  TRUE  TRUE FALSE

1
在我的小样本中运行得非常好。但是当有 50,000 行时,会出现“错误:无法分配大小为 24.2 Gb 的向量”的问题。因此,在这里使用 outer 函数似乎不能处理大型数据集,因为它会构建一个巨大的矩阵。 - s1x
@s1x,我明白你的意思。你应该更具体地说明你想要的输出,就像alexis_laz所问的那样。如果你希望每个比较都有一个结果,用其他方法也会很困难,我认为(你有多少内存?) - talat
4
如果每个值都必须与所有其他值进行比较,那么得到的向量长度至少为factorial(length(data[,'saleTotal'])) - Tensibai

5
请注意效率问题(见最后注释)。
根据您的预期输出,您可以迭代每个值并计算(对TRUE值求和)比其他所有值更便宜的次数,并返回一个列表以将该值与计数配对。
sapply(data[,2],function(x) {
  list(x, sum(x < data[,2]))
})

这将以长格式给出:

     [,1]   [,2]   [,3]  
[1,] 477.99 457.99 556.31
[2,] 1      2      0     

如果您只想给现有数据集添加一列,可以使用以下操作:

data$cheaperThan <- sapply(data[,2],function(x) sum(x < data[,2])) 

使用的数据:

> system.time(sapply(large,function(x) sum(x < large)))
utilisateur     système      écoulé 
       1.08        0.22        1.30 
> system.time(length(large) - findInterval(large,sort(large)))
utilisateur     système      écoulé 
       0.01        0.00        0.01 

@alexis_laz的解决方案确实非常高效:

> set.seed(123)
> test <- runif(50000)*100
> identical(sapply(test,function(x) sum(x < test)), (length(test) - findInterval(test,sort(test))))
[1] TRUE
> system.time(sapply(test,function(x) sum(x < test)))
utilisateur     système      écoulé 
      13.64        1.24       14.96 
> system.time(length(test) - findInterval(test,sort(test)))
utilisateur     système      écoulé 
       0.01        0.00        0.02

1
谢谢!这个在大数据集上运行得很好(@alexis_laz提供了高效的解决方案)。@docendo discimus的解决方案也可以使用,但由于输出格式的原因会占用大量内存。因此,根据输出情况,你可以两种方案都使用。 - s1x
1
我猜sum(x < x)findinterval(x, sort(x))在概念上是相同的,但是sort一次的成本应该总是大于length(x) * length(x)循环,至少对于大向量来说是这样。我认为findInterval是R中为数不多的“万能函数”之一... :-) - alexis_laz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接