【问题标题】:How do I improve performance when iterating through multiple conditions in R?在 R 中迭代多个条件时如何提高性能?
【发布时间】:2017-01-01 13:58:37
【问题描述】:

假设我有一个具有以下结构的数据集:

  • 我有 N 个产品
  • 我在 N 个国家/地区开展业务
  • 我有 N 个支付伙伴
  • 5 月数据集包含 N 天
  • 我有 N 个不同的价格可供客户选择

例如:

customer.id <- c(1,2,3,4,5,6,7,8)
product <- c("product1","product2","product1","product2","product1","product2","product1","product2")
country <- c("country1","country2","country1","country2","country1","country2","country1","country2")
payment.partner <- c("pp1","pp2","pp1","pp2","pp1","pp2","pp1","pp2")
day <- c("day1","day2","day1","day2","day1","day2","day1","day2")
price <- c("price1","price2","price1","price2","price1","price2","price1","price2")

customer.data <- data.frame(customer.id,product,country,payment.partner,day,price)
customer.data <- data.table(customer.data)

假设我想从中生成一个聚合,例如,为每个组合执行一个预测算法。为此,我确定了每个条件的唯一项并按如下方式对其进行迭代:

unique.products <- droplevels(unique(customer.data[,product]))
unique.countries <- droplevels(unique(customer.data[,country]))
unique.payment.partners <- droplevels(unique(customer.data[,payment.partner]))
unique.days <- droplevels(unique(customer.data[,day]))
unique.prices <- droplevels(unique(customer.data[,price]))

for(i in seq_along(unique.products)){
  temp.data1 <- customer.data[product==unique.products[[i]]]
  for(j in seq_along(unique.countries)){
    temp.data2 <- temp.data1[country==unique.countries[[j]]]
    for(k in seq_along(unique.payment.partners)){
      temp.data3 <- temp.data2[payment.partner==unique.payment.partners[[k]]]
      for(l in seq_along(unique.days)){
        temp.data4 <- temp.data3[day==unique.days[[l]]]
        for(m in seq_along(unique.prices)){
          temp.data5 <- temp.data4[price==unique.prices[[m]]]
          if(nrow(temp.data5)!=0){
            # do your calculations here
            print(temp.data5)
          }
        }
      }
    }
  }
}

一般来说,这种代码结构可以正常工作,但是在应用包含 500 万行的真实数据时会变得非常烦人。我猜 R 在速度和性能方面并不是最好的语言。当然,我过去使用过多核处理,或者尝试直接从 Hive 或 MySQL DataWarehouse 中获得这样的聚合。使用 C++ 或 Python 等其他语言也始终是一种选择。

但是,有时所有这些选项都是不可能的,这总是会导致我使用完全相同的处理结构。所以我很想知道从相当架构的角度来看是否有更好,分别更快的解决方案,因为众所周知(并且在基准测试时也变得非常清楚)for循环和频繁的数据子选择非常非常慢.

感谢所有 cmets、提示和可能的解决方案!

【问题讨论】:

  • 我承认解释语言比编译语言慢,但我对你声称的“几乎一般”知识感到冒犯。如果您不知道如何正确使用它,这不是语言的错。您也可以编写慢速 C++ 代码。
  • 我没有冒犯任何人的意思。只是 R 本身并不是为了超快而设计的,而是为了具有高可用性和可访问性。例如,请参阅 Ben Webers Talk 在他的 RServer 项目中,他告诉他有时必须证明将 R 用于数据科学应用程序:youtube.com/watch?v=QGzTEuZvyK4
  • 我完全同意@Roland。关于语言速度的一般陈述是错误的。没有经验的 R 用户通常认为它很慢,因为他们使用了 40 多年的 C 方法来解决他们的问题。 R 是一种现代编程语言,需要现代编程技术才能高效。
  • 感谢您的回答。我从最初的帖子中删除了“常识”。我想我以后应该对这种主观陈述更加谨慎。
  • 别误会我的意思:我真的很喜欢使用 R。只是我一直读到它比其他语言慢。既然我不会停止使用 R,很高兴知道这个假设似乎无效。

标签: r performance iteration


【解决方案1】:

您应该阅读您正在使用的软件包的文档。包 data.table 提供了一些优秀的introductory tutorials

customer.data <- data.frame(customer.id,product,country,payment.partner,day,price)
library(data.table)
setDT(customer.data)
customer.data[, 
              print(customer.data[.I]), #don't do this, just refer to the columns you want to work on
              by = .(product, country, payment.partner, day, price)]

当然,一般来说,您不会在这里打印 data.table 子集,而是直接在特定列上工作。

【讨论】:

    【解决方案2】:

    根据您的描述(但不是您的代码,我发现它的用途难以理解,我认为您可能想要使用 `interaction 函数:

    customer.data$grp=droplevels( with( customer.data,
                  interaction(product, country ,payment.partner, day, price) ) )
     table(customer.data$grp)
    #-----------------------
    product1.country1.pp1.day1.price1 
                                    4 
    product2.country2.pp2.day2.price2 
                                    4 
    

    然后您可以使用lapply( split( dat, dat$grp) , analytic_function) 在子集中创建单独的分析。我没有加载 data.table,所以显示了数据帧的方法,但 interaction 没有理由在 data.table 世界中不成功:

    customer.data[ , grp2 := droplevels(interaction( 
                                          product, country ,payment.partner, day, price) ) ]
    

    【讨论】:

    • 但您不需要使用 data.table 创建 interaction,因为它提供了(更)有效的分组。您的解决方案在基础 R 中很好。
    • 顺便说一句。 split 接受一系列因素,因此您无需自己创建交互。
    • data.table 路由当然更有效,而且一旦你学会了区分基本约定和 DT 约定之间的差异,语法也相当直观。
    猜你喜欢
    • 2021-07-04
    • 1970-01-01
    • 2022-01-02
    • 2017-02-23
    • 1970-01-01
    • 2021-03-11
    • 1970-01-01
    • 1970-01-01
    • 2014-01-31
    相关资源
    最近更新 更多