2017-01-15 46 views
3

假設我們有R中的以下數據集:鑑於最小列的,發現在其他colunm最小(dplyr)

> td 
    Type Rep Value1 Value2 
1 A 1  7  1 
2 A 2  5  4 
3 A 3  5  3 
4 A 4  8  2 
5 B 1  5  10 
6 B 2  6  1 
7 B 3  7  1 
8 C 1  8  13 
9 C 2  8  13 

> td <- structure(list(Type = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 
3L, 3L), .Label = c("A", "B", "C"), class = "factor"), Rep = c(1L, 
2L, 3L, 4L, 1L, 2L, 3L, 1L, 2L), Value1 = c(7L, 5L, 5L, 8L, 5L, 
6L, 7L, 8L, 8L), Value2 = c(1L, 4L, 3L, 2L, 10L, 1L, 1L, 13L, 
13L)), .Names = c("Type", "Rep", "Value1", "Value2"), class = "data.frame", 
row.names = c(NA, -9L)) 

我想產生如下表:

Type MinValue1 MinValue2 MeanValue1 MeanValue2 
1 A   5   3  6.25  2.5 
2 B   5  10  6.00  4.0 
3 C   3  13  8.00  13.0 

在此表中,數據通過「類型」進行彙總。列「MinValue1」是特定類型和列的最小值「MinValue2」是「Value2」的最小值,給定列「Value1」的最小值。列平均值*是所有觀測值的總體平均值。

這樣做的一種方法是實現遍歷每種類型的循環並進行數學運算。不過,我正在尋找一種更好/簡單/美觀的方式來執行此類操作。

我打的工具從 「tidyverse」:

> library(tidyverse) 
> td %>% 
    group_by(Type) %>% 
    summarise(MinValue1 = min(Value1), 
       MeanValue1 = mean(Value1), 
       MeanValue2 = mean(Value2)) 
# A tibble: 3 × 4 
    Type MinValue1 MeanValue1 MeanValue2 
    <fctr> <int>  <dbl>  <dbl> 
1  A  5  6.25  2.5 
2  B  5  6.00  4.0 
3  C  8  8.00  13.0 

請注意,我們還沒有列 「MinValue2」 在這裏。還要注意,「總結(...,MinValue2 = min(Value2),...)」不起作用,因爲此解決方案採用一種類型的所有觀察值中的最小值。

我們可能會與「切片」玩,然後合併結果:

> td %>% group_by(Type) %>% slice(which.min(Value1)) 
Source: local data frame [3 x 4] 
Groups: Type [3] 

    Type Rep Value1 Value2 
    <fctr> <int> <int> <int> 
1  A  3  5  4 
2  B  1  5  10 
3  C  1  8  13 

但要注意的是,「切片」工具並不能幫助我們在這裏:「A型,值1 5」應該有「值2」 == 3,而不是== 4作爲切片返回。

那麼,你們有沒有一種優雅的方式來達到我追求的效果?謝謝!

回答

1

由「類型」的分組後,創建另一組與「值2」的minimum基於選擇對應於最小的「VALUE1」的元素,可以使用summarise_each獲取所選列的minmean(」值1' 和‘值2與select

td %>% 
    group_by(Type) %>% 
    group_by(MinValue2 = min(Value2[Value1==min(Value1)]), add=TRUE) %>% 
    summarise_each(funs(min, mean), Value1:Value2) %>% 
    select(-Value2_min) 
2

一個辦法做到這一點’),並刪除了‘Value2_min’是使用order功能的特性,打破另一個向量關係:

get_min_at_min <- function(vec1, vec2) { 
    return(vec2[order(vec1, vec2)[1]]) 
} 

這將索引對應於最小值vec1的那些返回最小值vec2。有了這個功能的管道很簡單:

td %>% 
    group_by(Type) %>% 
    summarise(MinValue1 = min(Value1), 
      MinValue2 = get_min_at_min(Value1, Value2), 
      MeanValue1 = mean(Value1), 
      MeanValue2 = mean(Value2)) 

或者只是簡單地使用了一個事實,就是可以解決內部dplyr功能只是計算變量:

td %>% 
    group_by(Type) %>% 
    summarise(MinValue1 = min(Value1), 
      MinValue2 = min(Value2[Value1 == MinValue1]), 
      MeanValue1 = mean(Value1), 
      MeanValue2 = mean(Value2)) 
+0

非常感謝。最後一個選項是我正在尋找的那個。 –

1

非常感謝@evgeniC和@akrun。你的幫助是有價值的。就我的目的/數據集而言,這兩種解決方案工作得很好。因此,以豐富一點點的討論中,我跑了一些實驗,以測試這些建議的速度有多快,使用下面的腳本(當然,註釋/取消註釋每個實驗):

library(tidyverse) 

args <- commandArgs(TRUE) 
set.seed(args[1]) 
n = args[2] 

td = data.frame(Type = sample(LETTERS, n, replace=T), 
       Value1 = sample(1:100, n, replace=T), 
       Value2 = sample(1:100, n, replace=T)) 

ptm <- proc.time() 

# Solution 1 ### 
#get_min_at_min <- function(vec1, vec2) { 
    #return(vec2[order(vec1, vec2)[1]]) 
#} 

#tmp <- td %>% 
     #group_by(Type) %>% 
     #summarise(MinValue1 = min(Value1), 
       #MinValue2 = get_min_at_min(Value1, Value2), 
       #MeanValue1 = mean(Value1), 
       #MeanValue2 = mean(Value2)) 

### Solution 2 ### 
tmp <- td %>% 
     group_by(Type) %>% 
     summarise(MinValue1 = min(Value1), 
       MinValue2 = min(Value2[Value1 == MinValue1]), 
       MeanValue1 = mean(Value1), 
       MeanValue2 = mean(Value2)) 

### Solution 3 ### 
#tmp <- td %>% 
     #group_by(Type) %>% 
     #group_by(MinValue2 = min(Value2[Value1==min(Value1)]), add=TRUE) %>% 
     #summarise_each(funs(min, mean), Value1:Value2) %>% 
     #select(-Value2_min) 

print(proc.time() - ptm) 

併爲每個算法,我跑

$ Rscript test.R 270001 10000000 

使用

> td %>% group_by(Alg) %>% summarise_each(funs(mean, sd), User:Elapsed) 

,我們得到了follwing結果:

 Alg User_mean System_mean Elapsed_mean User_sd System_sd Elapsed_sd 
1 akrun 1.3643333 0.13766667  1.510333 0.01069268 0.005033223 0.02050203 
2 evgeniC1 0.8706667 0.07466667  0.951000 0.03323151 0.003055050 0.04073082 
3 evgeniC2 0.8600000 0.09300000  0.958000 0.05546170 0.005196152 0.06331666 

因此,我傾向於從@evgeniC使用的解決方案2,因爲它是最優雅/簡單,它是儘可能快地解決1 @akrun提出了一個很好的解決方案,但它是一個有點複雜和緩慢。無論如何,該設置可以在其他情況下有用。

+0

@akrun的答案在輸入數據中有很多列的情況下更好:它可以節省打字時間。 另外,我會推薦使用'microbenchmark'來測量性能的方法(例如,查看http://adv-r.had.co.nz/Performance.html的「微博標記」部分) – echasnovski

相關問題