2015-06-18 20 views
1

我有一個公司的數據集按類別分組。一些公司有多個類別,可以在多個列上重複。我想知道如何根據類別(例如每個類別的平均值)來分析它們。下面是一個示例data.frame我想分析R中多個列的重複類別

a <- factor(c("cat1", "cat2", "cat3", "cat4", "cat2")) 
b <- factor(c("cat5", "cat4", "cat2", "cat1", "NA")) 
comp <- factor(c("company1", "company2", "company3", "company4","company5")) 
score <- c(1, -1, 2, -2, 1.5) 
df <- data.frame(a, b, comp, score) 

#  a b  comp score 
#1 cat1 cat5 company1 1.0 
#2 cat2 cat4 company2 -1.0 
#3 cat3 cat2 company3 2.0 
#4 cat4 cat1 company4 -2.0 
#5 cat2 NA company5 1.5 

一個任務是瞭解每個類別的平均值。從注視這個簡單的數據集,我知道cat1的平均值將是-0.5。我有一些粗糙的方法,但是後面沒有過的任務:

  1. 我曾考慮過將融化的data.frame,這樣一類因素的每個實例將有自己的行(在這種情況下, ,維度將是10x1,每行的每個類別 - 分數對)。

  2. 我也想過用dplyr和分組由唯一的名稱,從類似

    mynames <- unique(c(levels(a), levels(b))) 
    

但是,dplyr要求分組是在data.frame向量(除非有一些方法來按照我不知道的模式或因素級別,這將是偉大的!)

  1. 最後,我可能不需要製作單獨的數據框。也許有一種方法可以使用apply將功能應用到獨特的類別級別,例如平均值。

謝謝你的幫助!

+0

是否擔心熔化成長格式的時間太長您的實際數據集?看起來這個任務在長格式數據集上很容易... – aosmith

回答

2

按照您的想法獲取所有類別,我們可以爲每個類別創建布爾列,並且它們僅包含那些具有該類別的布爾列。例如:

a <- factor(c("cat1", "cat2", "cat3", "cat4", "cat2")) 
b <- factor(c("cat5", "cat4", "cat2", "cat1", NA)) #NA, not "NA" or it will be a new level 
score <- c(1, -1, 2, -2, 1.5) 
df <- data.frame(a, b, score) 

我們創建一個包含所有類別的載體:

cats <- unique(c(levels(df$a), levels(df$b))) 

然後,對每個類別中,我們檢查,對於每一行,羯羊類別存在或不ab

catcols <- sapply(cats, function(i) { 
    sapply(1:nrow(df), function(j) { 
    return(i %in% df$a[j] | i %in% df$b[j]) 
    }) 
}) 

這將返回一個矩陣,我們可以添加到我們的數據框:

> catcols 
     cat1 cat2 cat3 cat4 cat5 
[1,] TRUE FALSE FALSE FALSE TRUE 
[2,] FALSE TRUE FALSE TRUE FALSE 
[3,] FALSE TRUE TRUE FALSE FALSE 
[4,] TRUE FALSE FALSE TRUE FALSE 
[5,] FALSE TRUE FALSE FALSE FALSE 

> df2 <- cbind(df, catcols) 

現在我們只需要取均值爲每個類別,子集僅是TRUE該類別的那些行:

means <- sapply(cats, function(i) { 
    mean(df2[df2[,i],"score"]) 
}) 

means 

#  cat1  cat2  cat3  cat4  cat5 
# -0.5000000 0.8333333 2.0000000 -1.5000000 1.0000000 

編輯

我想不出一個更更好的選擇,但能夠稍微改進代碼。使用關於你提到的大小隨機數據:

ncats <- 500 
allcats <- paste0("cat", 1:ncats) 
nrow <- 50000 
ncol <- 26 
set.seed(1) 
bigdf <- data.frame(replicate(ncol, sample(allcats, nrow, replace = TRUE)), score=rnorm(nrow)) 

代碼:

scorecol <- which(names(bigdf) == "score") 

catcols <- data.frame(sapply(allcats, function(i) { 
    apply(bigdf[,-scorecol], 1, function(j) i %in% j) 
})) 

means <- sapply(allcats, function(i) { 
    mean(bigdf[catcols[,i],"score"]) 
}) 

這花了195秒我,約30%的前一種方法的時間(microbenchmarked較小的數據)。結果如下:

> head(means) 
     cat1   cat2   cat3   cat4   cat5   cat6 
0.0019851051 0.0006465704 0.0066345735 0.0126089999 0.0135545455 0.0253983216 

我試過尋找dplyr替代方法,但找不到任何有用的東西。我確信使用data.table有更快的方法,但我對包還不是很好。

+0

好棒!我可能應該更清楚地表明這是一個更大的數據集(50K行)。我嘗試了你的功能,catcols,並繼續運行 - 這只是2種類別。我在整個數據集中有大約26列和500個級別。所以,你絕對解決了示例DF,但我有麻煩應用它:)我怎麼可以將這個應用到一個更大的數據集? – tom

+0

@tom對於大數據來說,定義嵌套循環並不是一個好方法。我會看看我是否能想到更好的東西。 – Molx

+0

好棒!謝謝 – tom

1

使用熔融和ddply功能:

df_melt <- melt(df, id.vars = c("comp", "score"), measure.vars = c("a", "b")) 
ddply(.data = df_melt, .variables = .(value), summarise, mean = round(mean(score), digits = 2)) 

這樣也很容易計算其他摘要統計。

對於更大的數據集作爲每MOLX:

# adding a hypothetical company column so that we do not deviate from the smaller dataset case above: 
bigdf$comp <- paste("company", 1:50000, sep = "") 

measure_vars <- names(bigdf)[-c(dim(bigdf)[2]-1, dim(bigdf)[2])] 
bigdf_melt <- melt(bigdf, id.vars = c("comp", "score"), measure.vars = measure_vars) 
# transform value column to an ordered factor so that ddply returns result in an ordered category fashion 
bigdf_melt$value <- factor(bigdf_melt$value, levels = paste0("cat", 1:ncats), ordered = TRUE) 

bigdf_mean <- ddply(.data = bigdf_melt, .variables = .(value), summarise, mean = round(mean(score), digits = 4)) 
bigdf_mean