2012-09-12 102 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"