2017-01-15 6 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

После группировки по «типу», создать еще одну группу с minimum из «Value2» на основе выбора элементов, что соответствует минимуму «Value1», используйте summarise_each, чтобы получить min и mean выбранных столбцов (» Значение1' и „значение2“) и удалить „Value2_min“ с 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

Один из способов сделать это является использование имущества из order функции разорвать связи с другим вектором:

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

Это возвращает минимальное значение vec2 среди тех индексов, которые соответствуют минимальному значению vec1. С помощью этой функции трубопроводов просты:

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 

Таким образом, я склонен использовать решение 2 от @evgeniC, поскольку он является самым элегантным/простым, и он так же быстро, как и решение 1. @akrun представила приятное решение, но оно немного сложнее и медленнее. В любом случае, установка может быть полезна в других ситуациях.

+0

Ответ @akrun лучше в случае множества столбцов входных данных: он сохраняет время печати. Также я рекомендовал бы подход к измерению производительности с помощью «microbenchmark» (например, посмотрите раздел «Microbenchmarking» http://adv-r.had.co.nz/Performance.html) – echasnovski