【问题标题】:Difference between c(... %*% ...) and sum(... * ...)c(... %*% ...) 和 sum(... * ...) 之间的区别
【发布时间】:2020-10-17 13:49:54
【问题描述】:

这个问题是对this 回答的讨论的后续问题。

dplyrgroup_by() 函数中使用c(... %*% ...)sum(... * ...) 有什么区别?

这两个代码给出相同的结果:

#1

library(dplyr) # 1.0.0
library(tidyr)
df1 %>%
    group_by(Date, Market) %>% 
    group_by(Revenue = c(Quantity %*% Price), 
             TotalCost = c(Quantity %*% Cost),
             Product, .add = TRUE) %>% 
    summarise(Sold = sum(Quantity)) %>% 
    pivot_wider(names_from = Product, values_from = Sold)

#2

library(dplyr) # 1.0.0
library(tidyr)
df1 %>%
    group_by(Date, Market) %>% 
    group_by(Revenue = sum(Quantity * Price), 
             TotalCost = sum(Quantity * Cost),
             Product, .add = TRUE) %>% 
    summarise(Sold = sum(Quantity)) %>% 
    pivot_wider(names_from = Product, values_from = Sold)
# A tibble: 2 x 7
# Groups:   Date, Market, Revenue, TotalCost [2]
#  Date      Market Revenue TotalCost Apple Banana Orange
#  <chr>     <chr>    <dbl>     <dbl> <int>  <int>  <int>
#1 6/24/2020 A          135      37.5    35     20     20
#2 6/25/2020 A           25      15      10     15     NA

c(... %*% ...)sum(... * ...) 之一更好/更快/首选/更整洁?


原始答案中的DATA

df1 <- structure(list(Date = c("6/24/2020", "6/24/2020", "6/24/2020", 
"6/24/2020", "6/25/2020", "6/25/2020"), Market = c("A", "A", 
"A", "A", "A", "A"), Salesman = c("MF", "RP", "RP", "FR", "MF", 
"MF"), Product = c("Apple", "Apple", "Banana", "Orange", "Apple", 
"Banana"), Quantity = c(20L, 15L, 20L, 20L, 10L, 15L), Price = c(1L, 
1L, 2L, 3L, 1L, 1L), Cost = c(0.5, 0.5, 0.5, 0.5, 0.6, 0.6)), 
class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6"))

【问题讨论】:

  • 您可以在更大的数据集上使用 microbenchmarksystem.time
  • %*% 是矩阵乘积。这有帮助吗?
  • 两个运营商的期望不同。 * 进行元素乘法是正确的。或者%*% 进行线性代数矩阵乘法,这是完全不同的。向量长度与后者之间不应该有区别,因为(1)它们应该是矩阵,并且(2)如果有任何期望你可能有回收,你不应该考虑 LA 矩阵乘法,因为它可以 仅当两个矩阵具有完全兼容的维度 (ncol(m1) == nrow(m2)) 时才会发生。
  • 不同的说法......清楚你正在使用什么。如果QuantityPrice 都是向量,并且Quantity * Price 对您来说有意义,那么使用*。对我来说,这才是最重要的。如果对您得到的结果有任何疑问,请将 1:4 * 2:5(长度 4)与 1:4 %*% 2:5(长度 1)进行比较。从逻辑上讲,您需要什么?
  • 谢谢@r2evans,这真的很清楚。

标签: r linear-algebra matrix-multiplication


【解决方案1】:

我会将 cmets 编译成答案,如果我错过任何内容,其他人可以加入。

  • %*%* 是完全不同的运算符:* 进行元素乘法,%*% 进行线性代数矩阵乘法。这些是非常不同的操作,演示如下:

    1:4 * 2:5
    # [1]  2  6 12 20
    
    1:4 %*% 2:5
    #      [,1]
    # [1,]   40
    
    sum(1:4 * 2:5)
    # [1] 40
    

    如果您正在从两个向量相乘中寻找单个汇总统计量,并且线性代数中的矩阵相乘是有意义的,那么%*% 是适合您的工具。

  • 关于声明性代码应该有所说明;虽然您可以进行第三次操作(sum(.*.)),但对我来说,使用%*% 可能会更好,原因有两个:

    1. 声明性意图。我是说我打算在两个矩阵上做“线性代数”。

    2. 保障措施。如果有任何尺寸不匹配(例如,sum(1:4 * 2:3) 在语法上仍然有效,但 1:4 %*% 2:3 无效),我想马上知道。使用sum(.*.),不匹配会被全世界默默地忽略(我认为回收可能是一个大问题的原因之一)。

    3. 原因是不是性能:虽然%*% 的性能与sum(.*.) 不相上下,但随着数据的大小变大,%*% 的性能相对更贵。

      m1 <- 1:100 ; m2 <- m1+1 ; m3 <- 1:100000; m4 <- m3+1
      microbenchmark::microbenchmark(sm1 = sum(m1*m2), sm2 = m1%*%m2, lg1 = sum(m3*m4), lg2 = m3%*%m4)
      # Unit: nanoseconds
      #  expr    min     lq   mean median     uq      max neval
      #   sm1    800   1100 112900   1600   2100 11083600   100
      #   sm2   1100   1400   2143   1900   2450    10200   100
      #   lg1 239700 249550 411235 270800 355300 11102800   100
      #   lg2 547900 575550 634763 637850 678250   780500   100
      
  • 到目前为止,所有的讨论都是关于向量,它们实际上是一维矩阵(就%*% 似乎认为......尽管这并不完全准确)。一旦你开始接触真正的矩阵,交换它们就变得更加困难......事实上,我不知道有更简单的方法来模拟%*%(缺少for 循环等):

    m1 %*% m2
    #      [,1] [,2] [,3] [,4]
    # [1,]   22   49   76  103
    # [2,]   28   64  100  136
    t(sapply(seq_len(nrow(m1)), function(i) sapply(seq_len(ncol(m2)), function(j) sum(m1[i,] * m2[,j]))))
    #      [,1] [,2] [,3] [,4]
    # [1,]   22   49   76  103
    # [2,]   28   64  100  136
    

    (虽然嵌套-sapply 可能不是最快的非%*% 方法来处理矩阵-y 的东西,%*% 是 1-2 个数量级,因为它是 .Internal 并被编译并意味着"Math!" 像这样。)

底线,虽然%*% 在内部使用 * 运算符(对于几个步骤之一),但两者在其他方面是不同的。哎呀,人们也可以将*^ 以同样的方式进行比较......结果相似。

干杯!

【讨论】:

  • 这太棒了!感谢您的深入回答,并指出sum(.*.)%*% 之间的输出基本上只是表面上的相似性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-12
  • 2011-01-29
  • 1970-01-01
  • 2013-08-08
  • 1970-01-01
  • 2010-10-23
  • 2013-05-02
相关资源
最近更新 更多