web-dev-qa-db-ja.com

Rでリストを正しく使用する方法は?

簡単な背景:広く使用されている多くの(ほとんど?)現代のプログラミング言語には、少なくとも少数のADT [抽象データ型]が共通してあり、特に、

  • string(文字で構成されるシーケンス)

  • list(値の順序付けられたコレクション)、および

  • マップベースのタイプ(キーを値にマップする順序付けられていない配列)

Rプログラミング言語では、最初の2つはそれぞれcharacterおよびvectorとして実装されます。

Rを学習し始めたとき、2つのことは最初からほとんど明らかでした:listはRの最も重要なデータ型です(Rの親クラスであるためdata.frame)。少なくとも私のコードでそれらを正しく使用するには十分ではありません。

一つには、Rのlistデータ型は、マップADTの単純な実装であるように思われました(Pythonではdictionary、Objective CではNSMutableDictionary、PerlとRubyではhash、Javascriptではobject literalなど)。

たとえば、キーと値のペアをコンストラクターに渡すことにより、Python辞書と同じように作成します(Pythonではdictではなくlistです):

x = list("ev1"=10, "ev2"=15, "rv"="Group 1")

そして、Python辞書のアイテムと同じように、Rリストのアイテムにアクセスします(例:x['ev1'])。同様に、次の方法で'keys'または'values'のみを取得できます。

names(x)    # fetch just the 'keys' of an R list
# [1] "ev1" "ev2" "rv"

unlist(x)   # fetch just the 'values' of an R list
#   ev1       ev2        rv 
#  "10"      "15" "Group 1" 

x = list("a"=6, "b"=9, "c"=3)  

sum(unlist(x))
# [1] 18

しかし、R listsもnlike他のマップタイプADT(とにかく学んだ言語の中から)です。私の推測では、これはSの初期仕様の結果である、つまり、データ/統計DSL [ドメイン固有言語]をゼロから設計する意図であるということです。

three R listsと、広範囲に使用されている他の言語(Python、Perl、JavaScriptなど)のマッピングタイプとの大きな違い:

first、Rのlistsは、値がキー付きであっても、ベクトルと同様にorderedコレクションです(つまり、キーは連続した整数ではなく、ハッシュ可能な値になります)。ほとんどの場合、他の言語のマッピングデータタイプはnorderedです。

second、関数を呼び出したときにlistを一度も渡さなかった場合でも、関数からlistsを返すことができます。また、たとえlistを返す関数には(明示)listコンストラクター(もちろん、返された結果をunlistの呼び出しでラップすることにより、実際にこれに対処できます):

x = strsplit(LETTERS[1:10], "")     # passing in an object of type 'character'

class(x)                            # returns 'list', not a vector of length 2
# [1] list

third Rのlistsに特有の機能:別のADTのメンバーになれないようです。そうしようとすると、プライマリコンテナはlistに強制されます。例えば。、

x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)

class(x)
# [1] list

ここでの私の意図は、言語や文書化方法を批判することではありません。同様に、listデータ構造またはその動作に問題があることを示唆していません。私が望んでいるのは、コードで正しく使用できるように、それらがどのように機能するかを理解することです。

ここに、私がもっとよく理解したいものがあります:

  • 関数呼び出しがlist(たとえば、上記のstrsplit式)を返すタイミングを決定するルールは何ですか?

  • listに明示的に名前を割り当てない場合(たとえば、list(10,20,30,40))は、デフォルトの名前は1で始まる連続した整数ですか? (私は推測しますが、答えがイエスであると確信しているわけではありません。そうでなければ、このタイプのlistunlistへの呼び出しでベクトルに強制することはできません。)

  • これらの2つの異なる演算子[][[]]sameの結果を返すのはなぜですか?

    x = list(1, 2, 3, 4)

    両方の式は「1」を返します。

    x[1]

    x[[1]]

  • なぜこれら2つの式notは同じ結果を返すのですか?

    x = list(1, 2, 3, 4)

    x2 = list(1:4)

Rのドキュメント( ?listR-intro )を指してはいけません上記。

(最後に、 hash と呼ばれるRパッケージ(CRANで利用可能)を最近学び、使用を開始しました。このパッケージはconventional S4クラスを介してマップタイプの動作を実装しています。このパッケージ。)

303
doug

質問の最後の部分に対処するために、Rのlistvectorの違いを実際に指摘しています。

これら2つの式が同じ結果を返さないのはなぜですか?

x = list(1、2、3、4)。 x2 =リスト(1:4)

リストには、各要素として他のクラスを含めることができます。したがって、最初の要素が文字ベクトル、2番目の要素がデータフレームなどのリストを作成できます。この場合、2つの異なるリストを作成しました。 xには、それぞれ長さ1の4つのベクトルがあります。x2には、長さ4の1つのベクトルがあります。

> length(x[[1]])
[1] 1
> length(x2[[1]])
[1] 4

そのため、これらはまったく異なるリストです。

Rリストは ハッシュマップ データ構造に非常によく似ています。各インデックス値は任意のオブジェクトに関連付けることができます。これは3つの異なるクラス(関数を含む)を含むリストの簡単な例です。

> complicated.list <- list("a"=1:4, "b"=1:3, "c"=matrix(1:4, nrow=2), "d"=search)
> lapply(complicated.list, class)
$a
[1] "integer"
$b
[1] "integer"
$c
[1] "matrix"
$d
[1] "function"

最後の要素が検索関数であることを考えれば、私はそのようにそれを呼ぶことができます:

> complicated.list[["d"]]()
[1] ".GlobalEnv" ...

これに対する最終的なコメントとして:data.frameは本当に(data.frameのドキュメントからの)リストであることに注意すべきです:

データフレームは、クラス "data.frame"が与えられた、一意の行名を持つ同数の行の変数のリストです。

そのため、data.frameの列は異なるデータ型を持つことができますが、マトリックスの列はできません。例として、ここで私は数字と文字で行列を作成しようとします:

> a <- 1:4
> class(a)
[1] "integer"
> b <- c("a","b","c","d")
> d <- cbind(a, b)
> d
 a   b  
[1,] "1" "a"
[2,] "2" "b"
[3,] "3" "c"
[4,] "4" "d"
> class(d[,1])
[1] "character"

2列目に文字が含まれているため、1列目のデータ型を数値に変更できないことに注意してください。

> d[,1] <- as.numeric(d[,1])
> class(d[,1])
[1] "character"
138
Shane

あなたの質問に関して、私は順番にそれらに対処し、いくつかの例を挙げましょう。

1)return文が1を足したときにリストが返されます。考えて

 R> retList <- function() return(list(1,2,3,4)); class(retList())
 [1] "list"
 R> notList <- function() return(c(1,2,3,4)); class(notList())
 [1] "numeric"
 R> 

2)名前は単に設定されていません。

R> retList <- function() return(list(1,2,3,4)); names(retList())
NULL
R> 

)同じことを返すわけではありません。あなたの例は与える

R> x <- list(1,2,3,4)
R> x[1]
[[1]]
[1] 1
R> x[[1]]
[1] 1

x[1]xの最初の要素を返します。これはxと同じです。すべてのスカラーは長さ1のベクトルです。一方、x[[1]]はリストの最初の要素を返します。

4)最後に、2つはそれぞれ4つのスカラーを含むリストと1つの要素を持つリスト(4つの要素のベクトルであること)を作成する間で異なります。

61

あなたの質問のサブセットを取るためだけに:

この記事 インデックス作成についての[][[]]の違いについての質問。

手短に言えば、[[]]はリストから単一の項目を選択し、[]は選択された項目のリストを返します。この例では、x = list(1, 2, 3, 4)' item 1は単一の整数ですが、x[[1]]は単一の1を返し、x[1]は値を1つだけ持つリストを返します。

> x = list(1, 2, 3, 4)
> x[1]
[[1]]
[1] 1

> x[[1]]
[1] 1
34
JD Long

1つの理由リストがそうであるように働く(順序付けられている)のはベクトルがしていないどんなノードでもどんなタイプでも含むことができる順序付けられたコンテナの必要性に取り組むことです。リストは、任意の型(ただし長さは同じ)のベクトルのリストであるdata.frameのベースを形成するなど、Rのさまざまな目的に再利用されます。

これら2つの式が同じ結果を返さないのはなぜですか?

x = list(1, 2, 3, 4); x2 = list(1:4)

@ Shaneの答えに追加するには、同じ結果を得たい場合は、次のことを試してください。

x3 = as.list(1:4)

これはベクトル1:4をリ​​ストに強制変換します。

12
Alex Brown

これにもう1つのポイントを追加するだけです。

Rは hashパッケージ にあるPythonの辞書と同等のデータ構造を持ちます。あなたはそれについて読むことができます Open Data Groupからのこのブログ記事 。これは簡単な例です:

> library(hash)
> h <- hash( keys=c('foo','bar','baz'), values=1:3 )
> h[c('foo','bar')]
<hash> containing 2 key-value pairs.
  bar : 2
  foo : 1

使いやすさの点では、hashクラスはリストと非常によく似ています。しかし、パフォーマンスは大規模なデータセットのほうが優れています。

11
Shane

あなたは言う:

もう1つは、関数を呼び出したときにListを渡さなかった場合でも、関数にListコンストラクタが含まれていない場合でも、関数からリストを返すことができることです。

x = strsplit(LETTERS[1:10], "") # passing in an object of type 'character'
class(x)
# => 'list'

そして、私はあなたがこれが問題であることを示唆していると思います(?)。それが問題ではない理由を説明するためにここにいます:-)。あなたの例は少し単純です、あなたが文字列分割をするとき、あなたは1要素の長さである要素を持つリストを持っているので、あなたはx[[1]]unlist(x)[1]と同じであることを知っています。しかし、strsplitの結果が各ビンで異なる長さの結果を返すとしたらどうでしょうか。単にベクトルを(リストに対して)返しただけではまったく意味がありません。

例えば:

stuff <- c("You, me, and dupree",  "You me, and dupree",
           "He ran away, but not very far, and not very fast")
x <- strsplit(stuff, ",")
xx <- unlist(strsplit(stuff, ","))

最初のケース(x:リストを返す)では、3番目の文字列の2番目の「部分」が何であるかを知ることができます。例えば:x[[3]][2]。結果が「解明」されたので(xx)、どうやってunlistを使って同じことができますか?

9
Steve Lianoglou
x = list(1, 2, 3, 4)
x2 = list(1:4)
all.equal(x,x2)

1:4はc(1,2,3,4)と同じなので、これは同じではありません。それらを同じにしたい場合は、

x = list(c(1,2,3,4))
x2 = list(1:4)
all.equal(x,x2)
5
JeremyS

それが助けになるなら、私は他のOO以前の言語では "レコード"としてRの "リスト"を考える傾向があります。

  • それらは包括的な型について仮定することはしません(あるいはあらゆるアリティとフィールド名のすべての可能な記録の型が利用可能です)。
  • それらのフィールドは匿名にすることができます(そしてあなたは厳密な定義順でそれらにアクセスします)。

"record"という名前は、データベースの専門用語では "records"(別名、行)の標準的な意味と衝突します。そのため、名前が(フィールドの)リストとしてそれ自体を示唆しているのかもしれません。

これら2つの異なる演算子[ ][[ ]]が同じ結果を返すのはなぜですか?

x = list(1, 2, 3, 4)
  1. [ ]はサブ設定操作を提供します。一般に、どのオブジェクトのサブセットも、元のオブジェクトと同じ型になります。したがって、x[1]はリストを提供します。同様にx[1:2]は元のリストのサブセットなので、リストです。例.

    x[1:2]
    
    [[1]] [1] 1
    
    [[2]] [1] 2
    
  2. [[ ]]はリストから要素を取り出すためのものです。 x[[1]]は有効であり、リストから最初の要素を抽出します。 x[[1:2]][[ ]]のようなサブ設定を提供しないため、[ ]は無効です。

     x[[2]] [1] 2 
    
    > x[[2:3]] Error in x[[2:3]] : subscript out of bounds
    
1
HariG

他の言語のベクトルとハッシュ/配列の概念について

  1. ベクトルはRのアトムです。例えば、rpois(1e4,5)(5つの乱数)、numeric(55)(倍精度の長さ-55のゼロベクトル)、およびcharacter(12)(12の空の文字列)はすべて「基本」です。

  2. リストかベクトルのどちらかがnamesを持つことができます。

    > n = numeric(10)
    > n
     [1] 0 0 0 0 0 0 0 0 0 0
    > names(n)
    NULL
    > names(n) = LETTERS[1:10]
    > n
    A B C D E F G H I J 
    0 0 0 0 0 0 0 0 0 0
    
  3. ベクトルはすべてが同じデータ型である必要があります。これを見て:

    > i = integer(5)
    > v = c(n,i)
    > v
    A B C D E F G H I J           
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    > class(v)
    [1] "numeric"
    > i = complex(5)
    > v = c(n,i)
    > class(v)
    [1] "complex"
    > v
       A    B    C    D    E    F    G    H    I    J                          
    0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i
    
  4. 他の回答やOPの質問自体に見られるように、リストにはさまざまなデータ型を含めることができます。

私は「配列」が可変データ型を含むことができる言語(Ruby、javascript)を見ましたが、例えばC++では「配列」はすべて同じデータ型でなければなりません。私はこれがスピード/効率の問題だと思います:もしあなたがnumeric(1e6)を持っているなら、あなたはそのサイズとすべての要素の位置を知っています先験的に;もし事物が何らかの未知のスライスに"Flying Purple People Eaters"を含んでいるかもしれないなら、あなたはそれについての基本的な事実を知るために実際にものをパースしなければなりません。

型が保証されている場合は、特定の標準R演算も意味があります。たとえば、cumsum(1:9)は意味がありませんが、cumsum(list(1,2,3,4,5,'a',6,7,8,9))は意味がありません、型がdoubleであることが保証されていない場合。


あなたの2番目の質問に関して:

関数を呼び出したときにListを渡さなかったとしても、関数からリストを返すことができます。

関数は、常に入力されているものとは異なるデータ型を返します。入力としてプロットを受け取らなくても、plotはプロットを返します。 Argは、numericを受け入れてもcomplexを返します。等。

(そしてstrsplitに関しては、ソースコードは ここ です。)

1
isomorphismes

これはかなり古い質問ですが、Rの最初のステップで不足していた知識、つまりRのオブジェクトとして手にデータを表現する方法、または既存のオブジェクトから選択する方法に正確に触れていると言わなければなりません。 R初心者が最初から「Rボックス内」を考えるのは容易ではありません。

そこで、私自身が以下の松葉杖を使い始めました。これは、どのオブジェクトをどのデータに使用するかを見つけ、基本的には実際の使用法を想像するのに役立ちました。

私は質問に正確な答えを与えていませんが、以下の短いテキストは、Rで始めたばかりで、同様の質問をしている読者を助けるかもしれません。

  • 原子ベクトル...私はその「シーケンス」を自分自身のために、方向ではなく、同じタイプのシーケンスと呼びました。 [サブセット。
  • Vector ... 2Dからの1方向のシーケンス、[サブセット。
  • 行列...行または列、行と列、またはシーケンスごとの[サブセットを形成する同じ長さのベクトルの束。
  • 配列... 3Dを形成する層状マトリックス
  • データフレーム... Excelのような2Dテーブル。行や列の並べ替え、追加、削除、またはaritの作成ができます。しばらくしてから、データフレームはlistの巧妙な実装であり、行と列で[を使用し、さらに[[を使用してサブセット化できることを本当に認識しました。
  • リスト...自分自身を助けるために、tree structureの時点でリストについて考えました。[i]はブランチ全体を選択して返し、[[i]]はブランチからアイテムを返します。そして、それはtree like structureであるため、index sequenceを使用して、[[index_vector]]を使用して非常に複雑なlist上のすべての葉をアドレス指定することもできます。リストは単純なものでも非常に複雑なものでもよく、さまざまなタイプのオブジェクトを1つに混在させることができます。

したがって、listsの場合、次の例のように、状況に応じてleafを選択する方法が多くなります。

l <- list("aaa",5,list(1:3),LETTERS[1:4],matrix(1:9,3,3))
l[[c(5,4)]] # selects 4 from matrix using [[index_vector]] in list
l[[5]][4] # selects 4 from matrix using sequential index in matrix
l[[5]][1,2] # selects 4 from matrix using row and column in matrix

この考え方は私を大いに助けてくれました。

1
Petr Matousu