Come utilizzare la funzione Ellissi di R quando scrivi la tua funzione?
-
27-09-2019 - |
Domanda
Il linguaggio R ha una caratteristica elegante per definire le funzioni che possono assumere un numero variabile di argomenti. Ad esempio, la funzione data.frame
assume un numero qualsiasi di argomenti e ogni argomento diventa i dati per una colonna nella tabella dei dati risultante. Esempio di utilizzo:
> 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
La firma della funzione include un'ellissi, come questa:
function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE,
stringsAsFactors = default.stringsAsFactors())
{
[FUNCTION DEFINITION HERE]
}
Vorrei scrivere una funzione che fa qualcosa di simile, prendendo più valori e consolidandoli in un singolo valore di ritorno (oltre a fare qualche altra elaborazione). Per fare ciò, devo capire come "disimballare" il ...
dagli argomenti della funzione all'interno della funzione. Non so come farlo. La riga pertinente nella definizione della funzione di data.frame
è object <- as.list(substitute(list(...)))[-1L]
, di cui non posso avere alcun senso.
Quindi, come posso convertire le ellissi dalla firma della funzione in, ad esempio, un elenco?
Per essere più specifici, come posso scrivere get_list_from_ellipsis
nel codice seguente?
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)
Modificare
Sembra che ci siano due possibili modi per farlo. Sono as.list(substitute(list(...)))[-1L]
e list(...)
. Tuttavia, questi due non fanno esattamente la stessa cosa. (Per le differenze, vedi esempi nelle risposte.) Qualcuno può dirmi qual è la differenza pratica tra loro e quale dovrei usare?
Soluzione
Ho letto risposte e commenti e vedo che poche cose non sono state menzionate:
data.frame
usilist(...)
versione. Frammento del codice:object <- as.list(substitute(list(...)))[-1L] mrn <- is.null(row.names) x <- list(...)
object
è usato per fare un po 'di magia con i nomi delle colonne, max
viene utilizzato per creare finaledata.frame
.
Per l'uso non valutato...
argomento guardawrite.csv
Codice dovematch.call
viene usato.Mentre scrivi nel commento, il risultato in Dirk Risposta non è un elenco di elenchi. È un elenco di lunghezza 4, quali elementi sono
language
genere. Il primo oggetto è unsymbol
-list
, il secondo è l'espressione1:10
e così via. Che spiegano perché[-1L]
è necessario: rimuove il previstosymbol
da argomenti forniti in...
(Perché è sempre un elenco).
Come afferma Dirksubstitute
ritorna "Analsimi l'espressione non valutata".
Quando chiamimy_ellipsis_function(a=1:10,b=11:20,c=21:30)
poi...
"crea" un elenco di argomenti:list(a=1:10,b=11:20,c=21:30)
esubstitute
rendilo un elenco di quattro elementi:List of 4 $ : symbol list $ a: language 1:10 $ b: language 11:20 $ c: language 21:30
Il primo elemento non ha un nome e questo è
[[1]]
in Dirk Risposta. Ottengo questo risultato usando: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)
Come sopra possiamo usare
str
Per controllare quali oggetti sono in una funzione.my_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
Va bene. Vediamo
substitute
versione:my_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
Non è quello di cui avevamo bisogno. Avrai bisogno di ulteriori trucchi per affrontare questo tipo di oggetti (come in
write.csv
).
Se vuoi usare ...
allora dovresti usarlo come nella risposta di Shane, da list(...)
.
Altri suggerimenti
Puoi convertire le ellipsis in un elenco con list()
, quindi esegui le tue operazioni su di esso:
> test.func <- function(...) { lapply(list(...), class) }
> test.func(a="b", b=1)
$a
[1] "character"
$b
[1] "numeric"
Quindi il tuo get_list_from_ellipsis
La funzione non è altro che list
.
Un caso d'uso valido per questo è nei casi in cui si desidera passare in un numero sconosciuto di oggetti per il funzionamento (come nel tuo esempio di c()
o data.frame()
). Non è una buona idea usare il ...
Quando conosci ogni parametro in anticipo, tuttavia, in quanto aggiunge un po 'di ambiguità e ulteriori complicazioni alla stringa dell'argomento (e rende la firma della funzione poco chiara a qualsiasi altro utente). L'elenco degli argomenti è un importante pezzo di documentazione per gli utenti delle funzioni.
Altrimenti, è utile anche per i casi in cui si desidera passare attraverso i parametri a una sottofunzione senza esporli tutti negli argomenti della tua funzione. Questo può essere notato nella documentazione della funzione.
Solo per aggiungere alle risposte di Shane e Dirk: è interessante confrontare
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
insieme a
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
Allo stato attuale, entrambe le versione appare adatta ai tuoi scopi in my_ellipsis_function
, sebbene il primo sia chiaramente più semplice.
Hai già dato metà della risposta. Ritenere
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>
Quindi questo ha impiegato due argomenti a
e b
dalla chiamata e lo ha convertito in un elenco. Non era quello che hai chiesto?
Funziona come previsto. Di seguito è una sessione interattiva:
> talk <- function(func, msg, ...){
+ func(msg, ...);
+ }
> talk(cat, c("this", "is", "a","message."), sep=":")
this:is:a:message.
>
Lo stesso, tranne con un argomento predefinito:
> talk <- function(func, msg=c("Hello","World!"), ...){
+ func(msg, ...);
+ }
> talk(cat,sep=":")
Hello:World!
> talk(cat,sep=",", fill=1)
Hello,
World!
>
Come puoi vedere, puoi usarlo per passare argomenti "extra" a una funzione all'interno della tua funzione se le impostazioni predefinite non sono ciò che desideri in un caso particolare.