У меня есть пакет R, в котором некоторые функции предназначены для обычного вызова внутри функций dplyr, изменяющих или суммирующих.
newdata <- dplyr::mutate(group_by(olddata, col1), newcol = myfunc(col1))
Однако иногда пользователи могут забыть сгруппировать свои данные, прежде чем помещать их в вызов mutate или summate.
newdata <- dplyr::mutate(olddata, newcol = myfunc(col1))
Когда фрейм данных не сгруппирован первым, функции пакета будут давать бессмысленные результаты. Однако ошибок или предупреждений как таковых не будет, из-за чего пользователи могут не знать причину проблемы.
Я хотел бы добавить Warning()
в код myfunc
, когда myfunc
обнаруживает, что входные данные не поступают из сгруппированного data.frame
. Однако я не могу понять, как myfunc
может определить, поступают ли данные из сгруппированного data.frame
. Похоже, что mutate
передает вектор только myfunc
, поэтому и dplyr::is.grouped_df
, и inherits(x, "grouped_df")
возвращают false.
Что я хотел бы:
myfunc <- function(x) {if (comes.from.grouped.df) {print("grouped")} else {print("ungrouped")}}
mutate(olddata, newcol = myfunc(col1))
'ungrouped'
mutate(group_by(olddata, col1), newcol = myfunc(col1))
'grouped'
'grouped'
'grouped'
см. ?group_data
, от dplyr
Если вы хотите, чтобы ваша функция использовалась в определенном контексте и выдавала предупреждение, если фрейм данных не сгруппирован, вы можете сделать:
library(tidyverse)
myfunc <- function(x) {
if (all(ls(envir = parent.frame()) == "~")) {
ss <- sys.status()
funcs <- sapply(ss$sys.calls, function(x) deparse(as.list(x)[[1]]))
wf <- which(funcs == "mutate")
if (length(wf) == 0) stop("`myfunc` must be called from inside `mutate`")
wf <- max(wf)
data <- eval(substitute(.data), ss$sys.frames[[wf]])
if (!inherits(data, "grouped_df")) {
warning("`myfunc` called on an ungrouped data frame / tibble.")
}
return(x^2)
}
stop("`myfunc` must be called from inside `mutate`")
}
При использовании вне mutate
получаем ошибку:
myfunc(1:10)
#> Error in myfunc(1:10): `myfunc` must be called from inside `mutate`
С несгруппированным фреймом данных или табличкой мы получаем предупреждение:
tibble(iris) %>%
mutate(x = myfunc(Sepal.Length))
#> Warning in myfunc(Sepal.Length): `myfunc` called on an ungrouped data frame /
#> tibble.
#> # A tibble: 150 x 6
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species x
#> <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
#> 1 5.1 3.5 1.4 0.2 setosa 26.0
#> 2 4.9 3 1.4 0.2 setosa 24.0
#> 3 4.7 3.2 1.3 0.2 setosa 22.1
#> 4 4.6 3.1 1.5 0.2 setosa 21.2
#> 5 5 3.6 1.4 0.2 setosa 25
#> 6 5.4 3.9 1.7 0.4 setosa 29.2
#> 7 4.6 3.4 1.4 0.3 setosa 21.2
#> 8 5 3.4 1.5 0.2 setosa 25
#> 9 4.4 2.9 1.4 0.2 setosa 19.4
#> 10 4.9 3.1 1.5 0.1 setosa 24.0
#> # ... with 140 more rows
И он работает без нареканий, если табличка сгруппирована:
tibble(iris) %>%
group_by(Species) %>%
mutate(x = myfunc(Sepal.Length))
#> # A tibble: 150 x 6
#> # Groups: Species [3]
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species x
#> <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
#> 1 5.1 3.5 1.4 0.2 setosa 26.0
#> 2 4.9 3 1.4 0.2 setosa 24.0
#> 3 4.7 3.2 1.3 0.2 setosa 22.1
#> 4 4.6 3.1 1.5 0.2 setosa 21.2
#> 5 5 3.6 1.4 0.2 setosa 25
#> 6 5.4 3.9 1.7 0.4 setosa 29.2
#> 7 4.6 3.4 1.4 0.3 setosa 21.2
#> 8 5 3.4 1.5 0.2 setosa 25
#> 9 4.4 2.9 1.4 0.2 setosa 19.4
#> 10 4.9 3.1 1.5 0.1 setosa 24.0
#> # ... with 140 more rows
Created on 2023-02-15 with reprex v2.0.2
Привет, это действительно здорово, спасибо! У меня есть одно продолжение: как это можно изменить, чтобы оно работало, когда вход представляет собой data.frame, а не табличку? Как сейчас написано, запуск: mutate(iris, x = myfunc(Sepal.Length))
выдает ошибку object '.' not found
. Копаясь в этом, eval(quote(.), parent.frame())
терпит неудачу, когда вход представляет собой data.frame, а не tibble, но all(ls(envir = parent.frame()) == "~")
возвращает TRUE
для ввода data.frame. Как я могу изменить это для поддержки необработанных входных данных data.frame? Извините, я не понимаю этого достаточно хорошо, чтобы понять, как это сделать.
@mikeblazanin это не имеет ничего общего с табличкой и фреймом данных. quote(.)
работает только в том случае, если вы передадите фрейм данных / табличку. Я изменил его, чтобы он работал в любом случае, используя quote(.data)
Ааааа, отлично! Хотя теперь, когда я запускаю вашу отредактированную функцию, которая использует quote(.data)
с третьим блоком кода: tibble(iris) %>% group_by(Species) %>% mutate(x = myfunc(Sepal.Length))
выводит первую ошибку: myfunc called on an ungrouped data frame / tibble.
Знаете, почему это может быть так?
@mikeblazanin мой плохой. Кажется, что .data
должен оцениваться в том же системном фрейме, где был вызван mutate
, иначе он будет интерпретировать .data
как местоимение .data
tidyverse (которое может быть отдельной группой в сгруппированном наборе). Я изменил функцию, чтобы найти правильный кадр оценки.
Вы можете использовать
cur_data_all()
, а затем проверитьis.grouped_df