【问题标题】:R: Data.table on subset excluding by valueR:按值排除的子集上的 Data.table
【发布时间】:2013-07-27 02:17:02
【问题描述】:

在R 中使用data.table,我试图对子集排除 选定元素进行操作。我正在使用by 运算符,但我不知道这是否是正确的方法。

这是一个例子。例如。 Delta 在 IAH:SNA 中的值是 (3+3)/2,这是在排除 Delta 后 IAH:SNA 中 Stops 的平均值。

library(data.table)
s1 <- "Market   Carrier Stops
IAH:SNA     Delta     1
IAH:SNA     Delta     1
IAH:SNA Southwest     3
IAH:SNA Southwest     3
MSP:CLE Southwest     2
MSP:CLE Southwest     2
MSP:CLE  American     2
MSP:CLE   JetBlue     1"

d <- data.table(read.table(textConnection(s1), header=TRUE))

setkey(d, Carrier, Market)

f <- function(x, y){
         subset(d, !(Carrier %in% x) & Market == y, Stops)[, mean(Stops)]}

d[, s := f(.BY[[1]], .BY[[2]]), by=list(Carrier, Market)]

##     Market   Carrier Stops     s
## 1: MSP:CLE  American     2  1.666667
## 2: IAH:SNA     Delta     1  3.000000
## 3: IAH:SNA     Delta     1  3.000000
## 5: IAH:SNA Southwest     3  1.000000
## 6: IAH:SNA Southwest     3  1.000000
## 7: MSP:CLE Southwest     2  1.500000
## 8: MSP:CLE Southwest     2  1.500000

上述解决方案在大型数据集(本质上是mapply)上的表现非常很差,但我不确定如何以类似data.table 的方式快速完成。

也许可以(动态地)生成一个这样做的因素?我只是不确定如何。 . .

有没有办法改进它?

编辑:只是为了它,这是一种获得上述更大版本的方法

library(data.table)
dl.dta <- function(...){
      ## input years ..
      years <- gsub("\\.", "_", c(...))
      baseurl <- "http://www.transtats.bts.gov/Download/"
      names <- paste("Origin_and_Destination_Survey_DB1BMarket", years, sep="_")
      info <- t(sapply(names, function(x) file.exists(paste(x, c("zip", "csv"), sep="."))))
      to.download <- paste(baseurl, names, ".zip", sep="")[!apply(info, 1, any)]
      if (length(to.download) > 0){
          message("starting download...")
          sapply(to.download,
                 function(x) download.file(x, rev(strsplit(x, "/")[[1]])[1]))}

      to.unzip <- paste(names,  "zip", sep=".")[!info[, 2]]
      if (length(to.unzip > 0)){
          message("starting to unzip...")
          sapply(to.unzip, unzip)}
      paste(names, "csv", sep=".")}

countWords.split <- function(x, s=":"){
    ## Faster on my machine than grep for some reanon
    sapply(strsplit(as.character(x), s), length)}

countWords.grep <- function(x){
    sapply(gregexpr("\\W+", x), length)+1}

fname <- dl.dta(2013.1)
cols <- rep("NULL", 41)
## Columns to keep: 9 is Origin, 18 is Dest, 24 is groups of airports in travel
## 30 is RPcarrier (reporting carrier).  
## For more columns: 35 is market fare and 36 is distance.
cols[9] <- cols[18] <- cols[24] <- cols[30] <- NA
d <- data.table(read.csv(file=fname,  colClasses=cols))
d[, Market := paste(Origin, Dest, sep=":")]
## should probably
d[, Stops := -2 + countWords.split(AirportGroup)]
d[, Carrier := RPCarrier]
d[, c("RPCarrier", "Origin", "Dest", "AirportGroup") := NULL]

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    使用一点点初等数学:

    d[, c("tmp.mean", "N") := list(mean(Stops), .N), by = Market]
    d[, exep.mean := (tmp.mean * N - sum(Stops)) / (N - .N), by = list(Market,Carrier)]
    
    #     Market   Carrier Stops tmp.mean N exep.mean
    # 1: IAH:SNA     Delta     1     2.00 4  3.000000
    # 2: IAH:SNA     Delta     1     2.00 4  3.000000
    # 3: IAH:SNA Southwest     3     2.00 4  1.000000
    # 4: IAH:SNA Southwest     3     2.00 4  1.000000
    # 5: MSP:CLE Southwest     2     1.75 4  1.500000
    # 6: MSP:CLE Southwest     2     1.75 4  1.500000
    # 7: MSP:CLE  American     2     1.75 4  1.666667
    # 8: MSP:CLE   JetBlue     1     1.75 4  2.000000
    

    【讨论】:

    • 谢谢,这很正常。但也许f 比mean 更复杂,并且没有封闭形式的解决方案。
    • f 可能是市场m 不包括运营商c 的子矩阵的规范。操作子集至少会更直观。 (顺便说一句,您提供的解决方案非常快!)
    • 我可能遗漏了一些东西,但您可以使用与我对Stops 欧几里得范数的回答中相同的原理算法。
    • @Roland 如果 f 是从外部给出的而你不知道它是如何工作的会怎样
    • 这里有一个复杂函数的例子,(对我来说)你将如何处理使用这种方法并不明显 - 如果你正在计算数据的线性拟合怎么办?知道您的合身度加上总体合身度是不够的信息。您也许可以想出一组足够的变量,但这需要合理的工作量。
    【解决方案2】:

    @Roland 的答案适用于某些功能(当它起作用时最好),但不是一般情况下。不幸的是,您不能将拆分-应用-组合策略按原样应用于数据来执行任务,但如果您使数据更大,则可以。让我们从一个更简单的例子开始:

    dt = data.table(a = c(1,1,2,2,3,3), b = c(1:6), key = 'a')
    
    # now let's extend this table the following way
    # take the unique a's and construct all the combinations excluding one element
    combinations = dt[, combn(unique(a), 2)]
    
    # now combine this into a data.table with the excluded element as the index
    # and merge it back into the original data.table
    extension = rbindlist(apply(combinations, 2,
                      function(x) data.table(a = x, index = setdiff(c(1,2,3), x))))
    setkey(extension, a)
    
    dt.extended = extension[dt, allow.cartesian = TRUE]
    dt.extended[order(index)]
    #    a index b
    # 1: 2     1 3
    # 2: 2     1 4
    # 3: 3     1 5
    # 4: 3     1 6
    # 5: 1     2 1
    # 6: 1     2 2
    # 7: 3     2 5
    # 8: 3     2 6
    # 9: 1     3 1
    #10: 1     3 2
    #11: 2     3 3
    #12: 2     3 4
    
    # Now we have everything we need:
    dt.extended[, mean(b), by = list(a = index)]
    #   a  V1
    #1: 3 2.5
    #2: 2 3.5
    #3: 1 4.5
    

    回到原始数据(并做一些稍微不同的操作,以简化表达式):

    extension = d[, {Carrier.uniq = unique(Carrier);
                     .SD[, rbindlist(combn(Carrier.uniq, length(Carrier.uniq)-1,
                              function(x) data.table(Carrier = x,
                                       index = setdiff(Carrier.uniq, x)),
                              simplify = FALSE))]}, by = Market]
    setkey(extension, Market, Carrier)
    
    extension[d, allow.cartesian = TRUE][, mean(Stops), by = list(Market, Carrier = index)]
    #    Market   Carrier       V1
    #1: IAH:SNA Southwest 1.000000
    #2: IAH:SNA     Delta 3.000000
    #3: MSP:CLE   JetBlue 2.000000
    #4: MSP:CLE Southwest 1.500000
    #5: MSP:CLE  American 1.666667
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-08
      • 2021-12-05
      • 1970-01-01
      • 1970-01-01
      • 2017-12-23
      • 1970-01-01
      • 1970-01-01
      • 2014-04-20
      相关资源
      最近更新 更多