Jak wykorzystać funkcję elipsy R podczas pisania własnej funkcji?
Język R ma sprytną funkcję do definiowania funkcji, które mogą przyjmować zmienną liczbę argumentów. Na przykład funkcja data.frame
pobiera dowolną liczbę argumentów, a każdy argument staje się danymi dla kolumny w wynikowej tabeli danych. Przykład użycia:
> data.frame(letters=c("a", "b", "c"), numbers=c(1,2,3), notes=c("do", "re", "mi"))
letters numbers notes
1 a 1 do
2 b 2 re
3 c 3 mi
Podpis funkcji zawiera elipsę, jak to:
function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE,
stringsAsFactors = default.stringsAsFactors())
{
[FUNCTION DEFINITION HERE]
}
Chciałbym napisać funkcję, która robi coś podobnego, biorąc wiele wartości i konsolidując je w pojedyncza wartość zwracana (jak również inne przetwarzanie). Aby to zrobić, muszę dowiedzieć się, jak" rozpakować " ...
z argumentów funkcji wewnątrz funkcji. Nie wiem, jak to zrobić. Odpowiedni wiersz w definicji funkcji data.frame
to object <- as.list(substitute(list(...)))[-1L]
, którego nie mogę zrozumieć.
Więc jak mogę przekonwertować elipsę z podpisu funkcji na, na przykład, listę?
Mówiąc dokładniej, jak mogę napisać get_list_from_ellipsis
w kodzie na dole?
my_ellipsis_function(...) {
input_list <- get_list_from_ellipsis(...)
output_list <- lapply(X=input_list, FUN=do_something_interesting)
return(output_list)
}
my_ellipsis_function(a=1:10,b=11:20,c=21:30)
Edit
Wydaje się, że są dwa możliwe sposoby, aby to zrobić. Są to as.list(substitute(list(...)))[-1L]
i list(...)
. Jednak te dwa nie robią dokładnie tego samego. (Różnice można znaleźć w przykładach w odpowiedziach.) Czy ktoś może mi powiedzieć jaka jest praktyczna różnica między nimi, a którą powinienem zastosować?
5 answers
Czytam odpowiedzi i komentarze i widzę, że niewiele rzeczy nie zostało wymienionych:
-
data.frame
używa wersjilist(...)
. Fragment kodu:object <- as.list(substitute(list(...)))[-1L] mrn <- is.null(row.names) x <- list(...)
object
jest używany do wykonywania magii z nazwami kolumn, alex
jest używany do tworzenia ostatecznegodata.frame
.
Aby użyć nieocenionego argumentu...
, spójrz na kodwrite.csv
, w którym użytomatch.call
. -
Jak piszesz w komentarzu to nie jest lista list. Jest listą długości 4, które elementy są
language
Typ. Pierwszym obiektem jestsymbol
-list
, drugi to wyrażenie1:10
i tak dalej. To wyjaśnia, dlaczego {[17] } jest potrzebne: usuwa oczekiwanesymbol
z podanych argumentów w...
(ponieważ zawsze jest to lista).
Jak stwierdza Dirksubstitute
zwraca "parse tree the unevaluated expression".
Po wywołaniumy_ellipsis_function(a=1:10,b=11:20,c=21:30)
...
"tworzy" listę argumentów:list(a=1:10,b=11:20,c=21:30)
isubstitute
tworzy listę czterech elementów:List of 4 $ : symbol list $ a: language 1:10 $ b: language 11:20 $ c: language 21:30
Pierwszy element nie ma nazwy, a to
[[1]]
W Dirk answer. I osiągnij te wyniki używając:my_ellipsis_function <- function(...) { input_list <- as.list(substitute(list(...))) str(input_list) NULL } my_ellipsis_function(a=1:10,b=11:20,c=21:30)
-
Jak powyżej możemy użyć
str
, aby sprawdzić, jakie obiekty są w funkcji.
W porządku. Zobaczmymy_ellipsis_function <- function(...) { input_list <- list(...) output_list <- lapply(X=input_list, function(x) {str(x);summary(x)}) return(output_list) } my_ellipsis_function(a=1:10,b=11:20,c=21:30) int [1:10] 1 2 3 4 5 6 7 8 9 10 int [1:10] 11 12 13 14 15 16 17 18 19 20 int [1:10] 21 22 23 24 25 26 27 28 29 30 $a Min. 1st Qu. Median Mean 3rd Qu. Max. 1.00 3.25 5.50 5.50 7.75 10.00 $b Min. 1st Qu. Median Mean 3rd Qu. Max. 11.0 13.2 15.5 15.5 17.8 20.0 $c Min. 1st Qu. Median Mean 3rd Qu. Max. 21.0 23.2 25.5 25.5 27.8 30.0
substitute
Wersja:
Nie tego potrzebowaliśmy. Będziesz potrzebował dodatkowych sztuczek, aby poradzić sobie z tego rodzaju obiektami (jak wmy_ellipsis_function <- function(...) { input_list <- as.list(substitute(list(...))) output_list <- lapply(X=input_list, function(x) {str(x);summary(x)}) return(output_list) } my_ellipsis_function(a=1:10,b=11:20,c=21:30) symbol list language 1:10 language 11:20 language 21:30 [[1]] Length Class Mode 1 name name $a Length Class Mode 3 call call $b Length Class Mode 3 call call $c Length Class Mode 3 call call
write.csv
).
Jeśli chcesz użyć ...
, powinieneś użyć go tak jak w odpowiedzi Shane ' a, by list(...)
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-25 02:58:16
Możesz przekonwertować wielokropek na Listę za pomocą list()
, a następnie wykonać na niej swoje operacje:
> test.func <- function(...) { lapply(list(...), class) }
> test.func(a="b", b=1)
$a
[1] "character"
$b
[1] "numeric"
Więc twoja get_list_from_ellipsis
funkcja jest niczym więcej niż list
.
Prawidłowy przypadek użycia to przypadki, w których chcesz przekazać nieznaną liczbę obiektów do operacji(jak w twoim przykładzie c()
lub data.frame()
). Nie jest dobrym pomysłem, aby używać ...
, Gdy znasz każdy parametr z góry, jednak, ponieważ dodaje pewne niejasności i dalsze komplikacje do ciągu argumentu (i sprawia, że podpis funkcji jest niejasny dla każdego innego użytkownika). Lista argumentów jest ważnym elementem dokumentacji dla użytkowników funkcji.
W przeciwnym razie, jest to również przydatne w przypadkach, gdy chcesz przekazać parametry do podfunkcji bez ujawniania ich wszystkich we własnych argumentach funkcji. Można to odnotować w dokumentacji funkcji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-17 16:03:31
Aby dodać do odpowiedzi Shane ' a i Dirka: interesujące jest porównanie
get_list_from_ellipsis1 <- function(...)
{
list(...)
}
get_list_from_ellipsis1(a = 1:10, b = 2:20) # returns a list of integer vectors
$a
[1] 1 2 3 4 5 6 7 8 9 10
$b
[1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Z
get_list_from_ellipsis2 <- function(...)
{
as.list(substitute(list(...)))[-1L]
}
get_list_from_ellipsis2(a = 1:10, b = 2:20) # returns a list of calls
$a
1:10
$b
2:20
W obecnej formie, każda wersja wydaje się odpowiednia dla Twoich celów w my_ellipsis_function
, choć pierwsza jest wyraźnie prostsza.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-31 08:56:22
Dałaś już połowę odpowiedzi. Consider
R> my_ellipsis_function <- function(...) {
+ input_list <- as.list(substitute(list(...)))
+ }
R> print(my_ellipsis_function(a=1:10, b=2:20))
[[1]]
list
$a
1:10
$b
11:20
R>
Więc to wzięło dwa argumenty a
i b
z wywołania i przekonwertowało je na listę. Czy nie o to prosiłeś?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-16 21:45:09
To działa zgodnie z oczekiwaniami. Poniżej znajduje się sesja interaktywna:
> talk <- function(func, msg, ...){
+ func(msg, ...);
+ }
> talk(cat, c("this", "is", "a","message."), sep=":")
this:is:a:message.
>
To samo, z wyjątkiem domyślnego argumentu:
> talk <- function(func, msg=c("Hello","World!"), ...){
+ func(msg, ...);
+ }
> talk(cat,sep=":")
Hello:World!
> talk(cat,sep=",", fill=1)
Hello,
World!
>
Jak widzisz, możesz użyć tego, aby przekazać "dodatkowe" argumenty do funkcji wewnątrz funkcji, jeśli domyślne wartości nie są tym, czego chcesz w danym przypadku.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-09 02:08:31