2015-10-17 3 views
2

Мне было любопытно, есть ли способ передать dplyr's do функцию вектора дополнительных аргументов, которые будут применяться к каждой группе по очереди? Рассмотрим, например, если мы хотим сгруппировать набор данных mtcars по его переменной cyl и применить функцию head к результирующим группам (по одному для 4, 6 и 8 соответственно) с n = 1 для 4 группы, n = 2 для группа 6 и n = 3 для 8-й группы, объединив конечные результаты в единый блок данных.Есть ли способ передать функцию dplyr `do` вектор дополнительных аргументов?

Я могу сделать это с помощью mapply следующим образом:

temp <- mtcars %>% 
    split(mtcars$cyl) %>% 
    mapply(FUN = head, x = ., n = 1:3, SIMPLIFY = FALSE) 
rbind(temp[[1]], temp[[2]], temp[[3]]) 

мне было интересно, если есть эквивалент способ сделать это с dplyr? Я получил, насколько ниже, но был тупик о том, как передать head дополнительный аргумент, представляющий число строк, мы хотели бы, чтобы выбрать:

# only selects first row of each group 
mtcars %>% 
    group_by(cyl) %>% 
    do(data.frame(head(x = ., n = 1))) 

# throws an error because n expects a single number 
mtcars %>% 
    group_by(cyl) %>% 
    do(data.frame(head(x = ., n = 1:3))) 

ответ

2

Это также возможно без группировки вообще,

mtcars %>% arrange(cyl) %>% slice(rep(c(0, which(diff(cyl)>0)), 1:3) + sequence(1:3)) 

# mpg cyl disp hp drat wt qsec vs am gear carb 
# 1 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1 
# 2 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 
# 3 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 
# 4 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2 
# 5 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4 
# 6 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3 

Чтобы ответить на ваш вопрос о do более конкретно, из-за того, как он реализован (оценка выражения в цикле через подмножества), одним из способов сделать вашу функцию head, было бы заставить ее увеличивать переменную каждый раз, когда она вызывается.

## Define a function that increments a variable each time it is called 
heads <- (function() { n <- 0; function(dat) { n <<- n+1; dat[1:n, ] } })() 

mtcars %>% group_by(cyl) %>% do(heads(.)) 
# mpg cyl disp hp drat wt qsec vs am gear carb 
# 1 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1 
# 2 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 
# 3 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 
# 4 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2 
# 5 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4 
# 6 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3 
+0

это точно соответствует тому, что я имел в виду. Благодаря! – jeromefroe

2

Хм, я уверен, что есть более элегантный способ сделать это, но:

group_index = 
    mtcars %>% 
    group_by(cyl) %>% 
    group_indices 

mtcars %>% 
    mutate(group_index = group_index) %>% 
    group_by(cyl) %>% 
    slice(group_index %>% first %>% seq) 
4

, если мы хотим, чтобы сгруппировать mtcars набора данных с помощью своего переменным цилом и применить функцию головки к полученным группам (по одному на 4, 6 и 8, соответственно) с п = 1 для 4 группа, n = 2 для группы 6 и n = 3 для группы 8

Во-первых, формализовать это понятие в data.frame:

heads = data.frame(cyl=c(4,6,8), n = 1:3) 

Затем вы можете объединить его в:

mtcars %>% left_join(heads) %>% group_by(cyl) %>% slice(seq(first(n))) 

#  mpg cyl disp hp drat wt qsec vs am gear carb  n 
# (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (int) 
# 1 22.8  4 108.0 93 3.85 2.320 18.61  1  1  4  1  1 
# 2 21.0  6 160.0 110 3.90 2.620 16.46  0  1  4  4  2 
# 3 21.0  6 160.0 110 3.90 2.875 17.02  0  1  4  4  2 
# 4 18.7  8 360.0 175 3.15 3.440 17.02  0  0  3  2  3 
# 5 14.3  8 360.0 245 3.21 3.570 15.84  0  0  3  4  3 
# 6 16.4  8 275.8 180 3.07 4.070 17.40  0  0  3  3  3 

Я хотел бы также рассмотреть уворачиваясь дополнительные скобки с

... %>% slice(n %>% first %>% seq) 

do существует только в качестве взлома, когда другие функции dplyr не соответствуют заданию, d следует избегать.

+0

Мне очень нравится идея инкапсуляции дополнительных аргументов в кадре данных и использование левого соединения для объединения их с исходным фреймом данных. Но я думаю, что этот ответ немного не соответствует действительности, и я считаю, что это потому, что я не представил хороший пример.Моя фактическая проблема заключалась в том, что у меня был кадр данных, представляющий информацию о пациенте, и я хотел запустить симуляцию в разных группах с разными аргументами. Я смог сделать это с помощью «mapply», но было любопытно, могу ли я сделать это с помощью 'do', так как часто использую' dplyr'. Я понимаю, что мой «пример главы» не зафиксировал мое намерение. – jeromefroe

+0

@JeromeFroelich На самом деле я не понимаю. Все, что можно сделать для разных групп с помощью 'split ... do', может быть сделано гораздо лучше с' group_by' с точки зрения скорости и быть идиоматичным, не так ли? Ваш подход «mapply» мне не совсем понятен - вы пишете свои аргументы для каждой группы вручную внутри вызова «mapply»? В любом случае, если вы думаете, что есть лучший способ создать проблему, я бы предложил опубликовать ее как новый вопрос. Однако, если люди не могут отличить его от этого, это может быть не принято хорошо («разве вы просто не спрашивали об этом?» И т. Д.). – Frank

+1

Спасибо за отзыв! Это был скорее вопрос мыслительного эксперимента, чем реальная проблема, но, похоже, это вызвало хорошее обсуждение. То, как я использовал «mapply», было следующим: 'df%>% split (df $ group_variable)%>% mapply (FUN = simulate_function, x =., Group_specific_args)' Меня в первую очередь интересовали, если это также можно сделать с помощью ' group_by ... do' и [answer] (http://stackoverflow.com/questions/33191821/is-there-a-way-to-pass-dplyrs-do-function-a-vector-of-additional -arguments/33195020 # 33195020) by @Bunk совпадает с тем, что я имел в виду. – jeromefroe

0

будет что-то вроде этой работы. это решение специфично для примера mtcars, но в вашем случае может быть что-то подобное. она включает в себя создание своей собственной функции, которая имеет условные операторы на основе из колонки вы группирование прочь:

head_custom <- function(df, n){ 

    if(df$cyl == 4){ 
    ans <- head(df, n[1]) 
    } 

    if(df$cyl == 6){ 
    ans <- head(df, n[2]) 
    } 

    if(df$cyl == 8){ 
    ans <- head(df, n[3]) 
    } 

    return(ans) 
} 

mtcars %>% 
group_by(cyl) %>% 
do(head_custom(., n = 1:3)) 
+0

Вы можете поместить оператор 'if()' в аргумент 'n', например' head_custom <- function (df, n) head (df, n [if (df $ cyl [1] == 4) 1 else if (df ​​$ cyl [1] == 6) 2 else 3]) '. Это также должно заботиться о вашей проблеме с предупреждениями. Обратите внимание на подмножества на 'cyl'. Вы получали тонны предупреждений. Также проверьте 'help (" == ")', так как этого действительно следует избегать в 'if()' операторах, если возможно –

+0

Я приводил общий пример использования условных операторов в функции «делать» . Я думаю, что проблема OP сильно отличается, но пример mtcars состоял в том, чтобы показать использование условных функций в функции как способ выполнения функции «do» с несколькими параметрами. – easports611

+0

Вы также можете исправить предупреждения, создав: 'cyl.i <- unique (df $ cyl)' в верхней части функции, а затем проверку 'if (cyl.i == 4) {...}' – easports611

Смежные вопросы