独自の関数を作成するときにRの省略記号機能を使用するにはどうすればよいですか?


186

R言語には、可変数の引数を取ることができる関数を定義するための気の利いた機能があります。たとえば、関数data.frameは任意の数の引数を取り、各引数は結果のデータテーブルの列のデータになります。使用例:

> 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

関数のシグネチャには、次のように省略記号が含まれています。

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

複数の値を取り、それらを単一の戻り値に統合する(およびその他の処理を行う)同様のことを行う関数を作成したいと思います。これを行うに...は、関数内の関数の引数から「アンパック」する方法を理解する必要があります。これを行う方法がわかりません。の関数定義の関連する行はですdata.frameobject <- as.list(substitute(list(...)))[-1L]、これは意味がわかりません。

では、省略記号を関数のシグネチャから、たとえばリストに変換するにはどうすればよいでしょうか。

より具体的には、get_list_from_ellipsis以下のコードでどのように書くことができますか?

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)

編集する

これには2つの方法が考えられます。彼らはあるas.list(substitute(list(...)))[-1L]list(...)。ただし、これら2つはまったく同じことを行いません。(違いについては、回答の例を参照してください。)誰もがそれらの実際的な違いは何か、そしてどれを使用するべきかを教えてもらえますか?

回答:


113

回答とコメントを読んだところ、言及されていないことがいくつかありました。

  1. data.framelist(...)バージョンを使用します。コードの一部:

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

    object列名の魔法をxかけるために使用されますが、finalを作成するために使用されますdata.frame
    未評価の...引数の使用については、が使用されているwrite.csvコードを参照してくださいmatch.call

  2. あなたがDirkでコメント結果を書くとき、答えはリストのリストではありません。要素がlanguageタイプである長さ4のリストです。最初のオブジェクトはsymbol- list、2番目は式1:10などです。それはなぜ[-1L]必要なのかを説明します:それはsymbol与えられた引数から期待されたものを取り除きます...(それは常にリストなので)
    ダークが述べるようsubstituteに、「解析木、評価されていない式」を返します。
    あなたが呼び出すとmy_ellipsis_function(a=1:10,b=11:20,c=21:30)、その後...の引数のリスト「を作成」:list(a=1:10,b=11:20,c=21:30)そしてsubstituteそれ四つの要素のリストを作ります:

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

    最初の要素には名前がありません[[1]]。これはダークアンサーにあります。私はこれを使用してこの結果を達成します:

    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. 上記のようstrに、関数内のオブジェクトを確認するために使用できます。

    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 

    大丈夫です。substituteバージョンを見てみましょう:

       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 

    必要なものではありません。これらの種類のオブジェクトを処理するには、追加のトリックが必要になります(などwrite.csv)。

使用したい場合は...、シェーンの回答のように使用してくださいlist(...)


38

省略記号をでリストに変換しlist()、それに対して操作を実行できます。

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

$b
[1] "numeric"

したがって、get_list_from_ellipsis関数はにすぎませんlist

これの有効な使用例は、(c()またはの例のようにdata.frame())操作のために不明な数のオブジェクトを渡したい場合です。...ただし、事前に各パラメーターがわかっている場合にを使用することはお勧めできません。引数の文字列が曖昧になり、さらに複雑になるためです(他のユーザーには関数のシグネチャが不明瞭になります)。引数リストは、関数ユーザーにとって重要なドキュメントです。

それ以外の場合は、独自の関数引数ですべてを公開せずにサブ関数にパラメーターを渡す場合にも役立ちます。これは、関数のドキュメントに記載されています。


省略記号をサブ関数への引数のパススルーとして使用することについては知っていますが、Rプリミティブで説明したように省略記号を使用することも一般的です。実際、listc関数はどちらもこのように機能しますが、どちらもプリミティブであるため、ソースコードを簡単に調べて、それらの機能を理解することはできません。
ライアンC.トンプソン

rbind.data.frameこの方法を使用します。
Marek

5
list(...)十分である場合、なぜそのようなRビルトインが代わりにdata.frame長い形式を使用するのas.list(substitute(list(...)))[-1L]ですか?
ライアンC.トンプソン

1
私はを作成data.frameしなかったので、その答えはわかりません(とはいえ、それに正当な理由がある確信しています)。私list()は自分のパッケージでこの目的で使用しており、まだ問題が発生していません。
シェーン

34

シェーンとダークの応答に追加するだけです:比較するのは興味深いです

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

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

現状では、どちらのバージョンmy_ellipsis_functionもでの目的に適しているように見えますが、最初のバージョンの方が明らかに単純です。


15

あなたはすでに半分の答えを出しました。検討する

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> 

したがって、これは2つの引数aを取りb、呼び出しからそれをリストに変換しました。それはあなたが求めたものではなかったのですか?


2
私が望むものではない。これは実際にはリストのリストを返すように見えます。に注意してください[[1]]。また、魔法の呪文のas.list(substitute(list(...)))仕組みを知りたいのですが。
ライアンC.トンプソン

2
内部list(...)list、引数に基づいてオブジェクトを作成します。次にsubstitute()、評価されていない式の解析ツリーを作成します。この関数のヘルプを参照してください。同様に、R(またはS)に関する優れた高度なテキスト。これは簡単なことではありません。
Dirk Eddelbuettel、2010年

わかりました、[[-1L]](私の質問から)その部分はどうですか?そうじゃないの[[1]]
ライアンC.トンプソン

3
索引付けについて読む必要があります。マイナスは「除外する」、つまりprint(c(1:3)[-1])ます。 2と3のみを印刷します。これLは、整数になることを保証する新しい方法です。これは、Rソースで多く行われます。
Dirk Eddelbuettel、2010年

7
私は、インデックス上に読む必要はありませんが、私はないあなたが示すことがコマンドの出力に近い注意を払う必要があります。[[1]]$aインデックスの違いから、ネストされたリストが関係していると思いました。しかし、今あなたが実際に得るものは私が欲しいリストですが、前に追加の要素があることがわかります。だから、[-1L]意味があります。とにかく、その余分な最初の要素はどこから来るのですか?そして、私がこれを単に使用する代わりに使用する理由はありますlist(...)か?
ライアンC.トンプソン

6

これは期待どおりに機能します。以下はインタラクティブセッションです。

> talk <- function(func, msg, ...){
+     func(msg, ...);
+ }
> talk(cat, c("this", "is", "a","message."), sep=":")
this:is:a:message.
> 

デフォルトの引数を除いて同じです:

> talk <- function(func, msg=c("Hello","World!"), ...){
+     func(msg, ...);
+ }
> talk(cat,sep=":")
Hello:World!
> talk(cat,sep=",", fill=1)
Hello,
World!
>

ご覧のとおり、デフォルトが特定のケースで必要なものでない場合は、これを使用して関数内の関数に「追加」の引数を渡すことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.