2012-09-12 147 views
1

我从3个大数据表(名为A1,A2,A3)开始。每个表都有4个数据列(V1-V4),1个“日期”列,在所有三个表和数千行中保持不变。根据(部分)匹配的列名计算行平均值

下面是一些近似于我的表的虚拟数据。

A1.V1<-c(1,2,3,4) 
A1.V2<-c(2,4,6,8) 
A1.V3<-c(1,3,5,7) 
A1.V4<-c(1,2,3,4) 


A2.V1<-c(1,2,3,4) 
A2.V2<-c(2,4,6,8) 
A2.V3<-c(1,3,5,7) 
A2.V4<-c(1,2,3,4) 


A3.V1<-c(1,2,3,4) 
A3.V2<-c(2,4,6,8) 
A3.V3<-c(1,3,5,7) 
A3.V4<-c(1,2,3,4) 

Date<-c(2001,2002,2003,2004) 

DF<-data.frame(Date, A1.V1,A1.V2,A1.V3,A1.V4,A2.V1,A2.V2,A2.V3,A2.V4,A3.V1,A3.V2,A3.V3,A3.V4) 

因此,这是我的数据帧结束看起来像:

Date A1.V1 A1.V2 A1.V3 A1.V4 A2.V1 A2.V2 A2.V3 A2.V4 A3.V1 A3.V2 A3.V3 A3.V4 
1 2001  1  2  1  1  1  2  1  1  1  2  1  1 
2 2002  2  4  3  2  2  4  3  2  2  4  3  2 
3 2003  3  6  5  3  3  6  5  3  3  6  5  3 
4 2004  4  8  7  4  4  8  7  4  4  8  7  4 

我的目标是计算该行的意思是每个从各个数据表的匹配列。所以在这个例子中,我希望所有列以V1结尾,所有列以V2结尾,所有列以V3结尾,所有列以V4结尾。

最终的结果是这样的

 V1 V2 V3 V4 
2001 1 2 1 1 
2002 2 4 3 2 
2003 3 6 5 3 
2004 4 8 7 4 

所以我的问题是,如何我去计算行基于列名部分匹配意味着什么?

感谢

+0

我们可以假设我们可以不依赖于列的位置排序?即“匹配”列可能是不规则间隔的? – joran

+0

不,我们不能依赖位置排序。而我正在使用的实际数据集有更多的列,所以指定列定位将是一个麻烦 – Vinterwoo

回答

0

我敢肯定,它可以更优雅的完成,但这似乎工作的一种可能。

# declare the column names 
colnames = c("V1", "V2", "V3", "V4") 

# calculate the means 
means = lapply(colnames, function(name) { apply(DF[,grep(name, names(DF))], 1, mean) }) 

# build the result 
result = do.call(cbind, means) 
result = as.data.frame(t(result)) 
rownames(result) = DF$Date 

我也应该描述一下,我做了什么。

首先,我宣布列名部分匹配。

然后,使用grep命令部分选择数据框中与特定子字符串匹配的列。 apply命令计算平均值,lapply对所有与子串部分匹配的列进行计算。

使用do.callcbind(正如DWin所建议的),我们连接各个列。 最后,我们从原始数据框的Date列中设置列名。

该问题可以更加优雅和高效地解决,请参阅由迪文和Maiasaura提供的解决方案。

+1

这是一个相当曲折的完成路径,特别是可以用“do”替换的for循环。呼叫(cbind,表示)' –

+0

合理的建议,相应地更新了这个帖子。我现在很少使用R一段时间了,但我仍然很难做到:)。顺便提一下,你和@Maiasaura喜欢这个解决方案。 – Timo

4
library(plyr) 
ddply(DF, .(Date), function(x) { 
    foo <- melt(x, id.vars = 1) 
    foo$variable <- substr(foo$variable, 4, 6) 
    return(dcast(foo, Date ~ variable, mean)) 
    }) 
Date V1 V2 V3 V4 
1 2001 1 2 1 1 
2 2002 2 4 3 2 
3 2003 3 6 5 3 
4 2004 4 8 7 4 
2

您可以使用grepvalue = T才能获得相应的名称,然后的data.table

library(data.table) 
# convert to a data.table 
DT <- data.table(DF) 
# the indices we wish to group 
.index <- paste0('V',1:3) 
# a list containing the names 
name_list <- mapply(grep, pattern = as.list(.index), 
        MoreArgs = list(x= names(DT),value=T), SIMPLIFY=F) 
# create the expression 
.e <- parse(text=sprintf('list(%s)', paste(mapply(sprintf, .index, lapply(name_list, paste, collapse = ', '), 
      MoreArgs = list(fmt = '%s = mean(c(%s), na.rm = T)')), collapse = ','))) 

DT[, eval(.e),by=Date] 

## Date V1 V2 V3 
## 1: 2001 1 2 1 
## 2: 2002 2 4 3 
## 3: 2003 3 6 5 
## 4: 2004 4 8 7 

# what .e looks like 
.e 
## expression(list(V1 = mean(c(A1.V1, A2.V1, A3.V1), na.rm = T),V2 = mean(c(A1.V2, A2.V2, A3.V2), na.rm = T),V3 = mean(c(A1.V3, A2.V3, A3.V3), na.rm = T))) 
+1

这种折磨似乎是由@Vinterwoo将两个分类类型合并为一个列名称向量引发的。在'data.table'中,我们将它保存为长格式,然后简单地执行:'DT [,mean(var),by =“A,V”]'。其中一些问题我会试图回答“为什么?”但是使用'with = FALSE'的方式来处理data.table可能更简单。 –

+0

我完全同意! – mnel

+0

太好了。我会为努力和测试+1,但是,哦,天哪,这很丑! :) –

6
colnames = c("V1", "V2", "V3", "V4") 
sapply(colnames, function(x) rowMeans(DF [, grep(x, names(DF))]) ) 
rownames(res) <- DF$Date 
res 
    V1 V2 V3 V4 
2001 1 2 1 1 
2002 2 4 3 2 
2003 3 6 5 3 
2004 4 8 7 4 

j组件内创建调用eval如果需要,自动将生成的名字:

> unique(sapply(strsplit(names(DF)[-1], ".", fixed=TRUE), "[", 2)) 
[1] "V1" "V2" "V3" "V4"