179 votes

Comment utiliser R ' fonction de sélection de s lorsque vous écrivez votre propre fonction ?

Le langage R est une fonctionnalité utile pour définir des fonctions qui peuvent prendre un nombre variable d'arguments. Par exemple, la fonction data.frame prend un nombre quelconque d'arguments, et chaque argument devient les données d'une colonne dans la table de données. Exemple d'utilisation:

> 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 fonction de signature comprend les points de suspension, comme ceci:

function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, 
    stringsAsFactors = default.stringsAsFactors()) 
{
    [FUNCTION DEFINITION HERE]
}

Je voudrais écrire une fonction qui fait la même chose, en prenant plusieurs valeurs et de les regrouper en une seule valeur de retour (ainsi que d'autres de traitement). Pour ce faire, j'ai besoin de comprendre comment faire pour "décompresser" l' ... de les arguments de la fonction au sein de la fonction. Je ne sais pas comment faire. La ligne correspondante dans la définition de la fonction d' data.frame est object <- as.list(substitute(list(...)))[-1L], ce qui je ne peux pas donner un sens.

Alors, comment puis-je convertir le bouton de sélection de la fonction de signature, par exemple, dans une liste?

Pour être plus précis, comment puis-je écrire get_list_from_ellipsis dans le code ci-dessous?

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)

Modifier

Il semble qu'il y a deux façons de le faire. Ils sont as.list(substitute(list(...)))[-1L] et list(...). Cependant, ces deux ne fait pas exactement la même chose. (Pour les différences, voir les exemples dans les réponses.) Quelqu'un peut-il me dire quelle est la différence concrète entre eux, et avec qui je devrais utiliser?

110voto

Marek Points 18000

J'ai lu les réponses et les commentaires et je vois que quelques choses n'ont pas été mentionnés:

  1. data.frame utilise list(...) version. Fragment de code:

    object <- as.list(substitute(list(...)))[-1L]
    mrn <- is.null(row.names)
    x <- list(...)
    

    object est utilisé pour faire de la magie avec les noms de colonnes, mais x est utilisé pour créer final data.frame.
    Pour l'utilisation de non évaluée ... argument regarde write.csv code match.call est utilisé.

  2. Comme vous l'écrivez en commentaire suite à Dirk réponse n'est pas une liste de listes. Est une liste de longueur 4, les éléments qui constituent language type. Premier objet est un symbol - list, le second est l'expression 1:10 et ainsi de suite. Ce qui expliquerait pourquoi [-1L] est nécessaire: il supprime attendue symbol de arguments fournis en ... (car il est toujours dans une liste).
    Comme Dirk unis substitute retours "arbre d'analyse de la non évaluée expression".
    Lorsque vous appelez my_ellipsis_function(a=1:10,b=11:20,c=21:30) alors ... "crée" une liste d'arguments: list(a=1:10,b=11:20,c=21:30) et substitute faire une liste de quatre éléments:

    List of 4
    $  : symbol list
    $ a: language 1:10
    $ b: language 11:20
    $ c: language 21:30
    

    Premier élément n'ont pas de nom et c'est [[1]] chez Dirk réponse. Je parvenir les résultats à l'aide:

    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)
    
  3. Comme ci-dessus, nous pouvons utiliser str pour vérifier ce que les objets sont dans une fonction.

    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 
    

    C'est ok. Permet de voir substitute version:

       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 
    

    N'est-ce pas ce dont nous avions besoin. Vous aurez besoin de plus d'astuces pour faire face à ces types d'objets (comme en write.csv).

Si vous souhaitez utiliser ... , alors vous devriez l'utiliser comme Shane réponse, en list(...).

37voto

Shane Points 40885

Vous pouvez convertir les points de suspension dans une liste avec list(), puis effectuez vos opérations:

> test.func <- function(...) { lapply(list(...), class) }
> test.func(a="b", b=1)
$a
[1] "character"

$b
[1] "numeric"

Si votre get_list_from_ellipsis fonction est rien de plus qu' list.

Un valide en cas d'utilisation de ce est dans le cas où vous souhaitez passer dans un nombre indéterminé d'objets pour l'opération (comme dans votre exemple de c() ou data.frame()). Ce n'est pas une bonne idée d'utiliser l' ... quand vous savez que chaque paramètre à l'avance, cependant, car il ajoute une certaine ambiguïté, et d'autre complication de la chaîne d'argument (et fait de la signature de la fonction claire pour tout autre utilisateur). La liste d'arguments est une pièce importante de la documentation de la fonction des utilisateurs.

Sinon, il est également utile pour les cas où vous souhaitez passer des paramètres à un sous-fonction sans s'exposer à tous dans vos propres arguments de la fonction. Cette information peut être indiquée dans la documentation des fonctions.

31voto

Richie Cotton Points 35365

Juste pour ajouter aux réponses de Shane et de Dirk : il est intéressant de comparer

avec

Telle quelle, soit la version apparaît appropriée pour votre application dans `` , même si le premier est nettement plus simple.

14voto

Dirk Eddelbuettel Points 134700

Vous avez déjà donné la moitié de la réponse. Envisager de

Cela a pris deux arguments et de l’appel et il convertie en une liste. N’était pas si ce que vous avez demandé ?

5voto

Cela fonctionne comme prévu. Ce qui suit est une session interactive :

Idem, sauf avec un argument par défaut :

Comme vous pouvez le voir, vous pouvez utiliser cela pour passer des arguments « extra » à une fonction au sein de votre fonction si les valeurs par défaut ne sont pas ce que vous voulez dans un cas particulier.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X