2013-04-22 42 views
5

我有一个广泛的调查数据集。对于一个特定的问题,在原始数据中创建了一组变量来表示调查问题在特定月份被问到的不同事实。使用数据表对子集执行操作

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

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

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”被观察为“是”。我想要一个新的“Q1”变量来表示“may.q1”,“jun.q1”和“jul.q1”。当月份为“可”时,“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方法任何帮助将不胜感激,如我的数据很大。谢谢。

回答

5

一种不同的方式来说明:

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解决方案融化为长格式应该更快。

+0

哇..我觉得凭着这个!真棒。 – Arun 2013-04-22 18:56:58

+0

MatthewDowle,这肯定比(我的)融合​​+演员快。我尝试了一个更大的数据。我不是在哪里接近...... 1e5 * 100列需要23秒,而这只需要不到一秒钟! – Arun 2013-04-22 19:56:38

3

编辑:似乎对更大的数据非常低效。查看@ 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.tableplyr(副本类似于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分组并且将values列拆分ind(其具有q1q2。所以,你会得到每个月2列(然后拼接在一起),以获得您想要的输出。

1

什么像这样

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 ... 

如果你想更进一步,融化这个数据再次可以使用熔融包裹

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" ...