web-dev-qa-db-ja.com

独自の関数を作成するときにRの省略記号機能を使用する方法は?

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.frame_の関数定義の関連する行はobject <- 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つはまったく同じことを行いません。 (違いについては、回答の例を参照してください。)誰もがそれらの間の実際的な違いは何か、私はどちらを使用すべきか教えてもらえますか?

174
Ryan Thompson

私は答えとコメントを読みましたが、言及されていないことがいくつかあります。

  1. _data.frame_はlist(...)バージョンを使用します。コードの断片:

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

    objectを使用して列名を処理しますが、xを使用して最終的な_data.frame_を作成します。
    未評価の_..._引数を使用するには、_write.csv_が使用されている_match.call_コードを参照してください。

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

    _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(...)によるシェーンの回答のように使用する必要があります。

109
Marek

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()の例のように)。ただし、各パラメーターを事前に知っているときに_..._を使用することはお勧めできません。引数文字列にあいまいさと複雑さを追加するためです(そして、他のユーザーには関数シグネチャが不明瞭になります)。引数リストは、関数ユーザーにとって重要なドキュメントです。

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

37
Shane

シェーンとダークの回答に追加するだけ:比較するのは面白い

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、最初の方が明らかに単純ですが。

31
Richie Cotton

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

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> 

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

14

これは期待どおりに機能します。以下は、対話型セッションです。

> 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!
>

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

5