【问题标题】:Performing Operations on a Subset Using Data Table使用数据表对子集执行操作
【发布时间】:2013-04-15 17:54:52
【问题描述】:

我有一个广泛的调查数据集。对于特定问题,在原始数据中创建了一组变量,以表示调查问题是在特定月份提出的这一事实。

我希望创建一组具有月份不变名称的新变量;这些变量的值将对应于观察月份的月份变量问题的值。

请看一个例​​子/虚构的数据集:

require(data.table)

data <- data.table(month = rep(c('may', 'jun', 'jul'),  each = 5),
                   may.q1 = rep(c('yes', 'no', 'yes'),  each = 5),
                   jun.q1 = rep(c('breakfast', 'lunch', 'dinner'),  each = 5),
                   jul.q1 = rep(c('oranges', 'apples', 'oranges'),  each = 5),
                   may.q2 = rep(c('econ', 'math', 'science'), each = 5),
                   jun.q2 = rep(c('sunny', 'foggy', 'cloudy'), each = 5),
                   jul.q2 = rep(c('no rain', 'light mist', 'heavy rain'), each = 5))

在本次调查中,实际上只有两个问题:“q1”和“q2”。这些问题中的每一个都被反复问了几个月。但是,仅当数据中观察到的月份与特定月份的调查问题匹配时,观察结果才包含有效响应。

例如:对于“May”中的任何观察,“may.q1”都被观察为“yes”。我想要一个新的“Q1”变量来表示“may.q1”、“jun.q1”和“jul.q1”。当月份为“may”时,“Q1”的值将采用“may.q1”的值,当月份为“jun”时,“Q1”的值将采用“jun.q1”的值.

如果我要尝试使用数据表手动执行此操作,我会想要这样的:

mdata <- data[month == 'may', c('month', 'may.q1', 'may.q2'), with = F]
setnames(mdata, names(mdata), gsub('may\\.', '', names(mdata)))

我希望这个重复“按 = 月”。

如果我要对数据框使用“plyr”包,我会使用以下方法解决:

require(plyr)
data <- data.frame(data)

mdata <- ddply(data, .(month), function(dfmo) {
    dfmo <- dfmo[, c(1, grep(dfmo$month[1], names(dfmo)))]
    names(dfmo) <- gsub(paste0(dfmo$month[1], '\\.'), '', names(dfmo))
    return(dfmo)
})

任何使用 data.table 方法的帮助将不胜感激,因为我的数据很大。谢谢。

【问题讨论】:

    标签: r data.table plyr data-cleaning


    【解决方案1】:

    另一种说明方式:

    data[, .SD[,paste0(month,c(".q1",".q2")), with=FALSE], by=month]
    
        month  may.q1     may.q2
     1:   may     yes       econ
     2:   may     yes       econ
     3:   may     yes       econ
     4:   may     yes       econ
     5:   may     yes       econ
     6:   jun   lunch      foggy
     7:   jun   lunch      foggy
     8:   jun   lunch      foggy
     9:   jun   lunch      foggy
    10:   jun   lunch      foggy
    11:   jul oranges heavy rain
    12:   jul oranges heavy rain
    13:   jul oranges heavy rain
    14:   jul oranges heavy rain
    15:   jul oranges heavy rain
    

    但请注意列名来自第一组(之后可以使用setnames 重命名)。如果有大量列而只需要很少的列,它可能不是最有效的。在这种情况下,Arun 的解决方案融合为长格式应该更快。

    【讨论】:

    • MatthewDowle,这肯定比(我的)融化 + 投射快。我在更大的数据上进行了尝试。我的距离很近......在 1e5 * 100 列上需要 23 秒,而这不到一秒!
    【解决方案2】:

    编辑:在更大的数据上似乎效率很低。查看@MatthewDowle 的回答,了解真正快速而简洁的解决方案。

    这是使用data.table 的解决方案。

    dd <- melt.dt(data, id.var=c("month"))[month == gsub("\\..*$", "", ind)][, 
            ind := gsub("^.*\\.", "", ind)][, split(values, ind), by=list(month)]
    

    函数melt.dt 是一个小函数(仍有待改进)我写信给melt 一个data.table 类似于plyr 中的melt 函数(复制/粘贴此函数显示在尝试上面的代码之前先在下面)。

    melt.dt <- function(DT, id.var) {
        stopifnot(inherits(DT, "data.table"))
        measure.var <- setdiff(names(DT), id.var)
        ind <- rep.int(measure.var, rep.int(nrow(DT), length(measure.var)))
        m1  <- lapply(c("list", id.var), as.name)
        m2  <- as.call(lapply(c("factor", "ind"), as.name))
        m3  <- as.call(lapply(c("c", measure.var), as.name))    
        quoted <- as.call(c(m1, ind = m2, values = m3))
        DT[, eval(quoted)]
    }
    

    想法:首先将data.tableid.var = month 列融为一体。现在,您所有的熔化列名称都采用month.question 的形式。因此,通过从这个融化的列中删除“.question”并将其等同于month 列,我们可以删除所有不必要的条目。一旦我们这样做了,我们就不需要“月”了。在熔化的列“ind”中。因此,我们使用gsub 删除“月份”。只保留q1, q2 等。在此之后,我们必须reshape(或cast)它。这是通过按month 分组并按ind 拆分values 列来完成的(其中有q1q2。因此,您每个月都会得到2 列(然后将它们缝合在一起)得到你想要的输出。

    【讨论】:

      【解决方案3】:

      这样的事情呢

      data <- data.table(
                         may.q1 = rep(c('yes', 'no', 'yes'),  each = 5),
                         jun.q1 = rep(c('breakfast', 'lunch', 'dinner'),  each = 5),
                         jul.q1 = rep(c('oranges', 'apples', 'oranges'),  each = 5),
                         may.q2 = rep(c('econ', 'math', 'science'), each = 5),
                         jun.q2 = rep(c('sunny', 'foggy', 'cloudy'), each = 5),
                         jul.q2 = rep(c('no rain', 'light mist', 'heavy rain'), each = 5)
                         )
      
      
      tmp <- reshape(data, direction = "long", varying = 1:6, sep = ".", timevar = "question")
      
      str(tmp)
      ## Classes ‘data.table’ and 'data.frame':   30 obs. of  5 variables:
      ##  $ question: chr  "q1" "q1" "q1" "q1" ...
      ##  $ may     : chr  "yes" "yes" "yes" "yes" ...
      ##  $ jun     : chr  "breakfast" "breakfast" "breakfast" "breakfast" ...
      ##  $ jul     : chr  "oranges" "oranges" "oranges" "oranges" ...
      ##  $ id      : int  1 2 3 4 5 6 7 8 9 10 ...
      

      如果你想更进一步并再次融合这些数据,你可以使用 melt 包

      require(reshape2)
      ## remove the id column if you want (id is the last col so ncol(tmp))
      res <- melt(tmp[,-ncol(tmp), with = FALSE], measure.vars = c("may", "jun", "jul"), value.name = "response", variable.name = "month")
      
      str(res)
      ## 'data.frame':    90 obs. of  3 variables:
      ##  $ question: chr  "q1" "q1" "q1" "q1" ...
      ##  $ month   : Factor w/ 3 levels "may","jun","jul": 1 1 1 1 1 1 1 1 1 1 ...
      ##  $ response: chr  "yes" "yes" "yes" "yes" ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-18
        • 2020-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-13
        • 1970-01-01
        相关资源
        最近更新 更多