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


320

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

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

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

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

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

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

1つには、Rのlistデータ型はマップADT(dictionaryPython、NSMutableDictionaryObjective C、hashPerlおよびRuby、object literalJavascriptなど)の単純な実装であるように思えました。

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

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

そして、R辞書の項目には、Python辞書の項目と同じようにアクセスしますx['ev1']。同様に、「キー」または「値」のみを取得できます。

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もあるとは違って(私はとにかく学んだ言語の中から)他のマップ型のADT。私の推測では、これはSの初期仕様、つまりデータ/統計DSL [ドメイン固有の言語]をゼロから設計する意図の結果です。

list広く使用されている他の言語(Python、Perl、JavaScriptなど)におけるR とマッピングタイプの3つの重要な違い:

まずlistRのsは、値がキー付きであっても、ベクトルと同様に順序付けられたコレクションです(つまり、キーは、連続した整数だけでなく、ハッシュ可能な任意の値にすることができます)。ほとんどの場合、他の言語のマッピングデータタイプは順序付けされていません。

2つ目は、関数を呼び出しlistlistときにaを渡したことがなく、またを返した関数に(明示的な)コンストラクターが含まれていなくても、関数からsを返すことができます(もちろん、これを実際に処理するには返された結果を)の呼び出しでラップします。listlistunlist

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

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

第三 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で始まる連続した整数だけですか?(私は仮定しますが、答えがイエスであると確信しているわけではありません。そうでない場合、このタイプをlistへの呼び出しを含むベクトルに強制することはできませんunlist。)

  • なぜ、これらの二つの異なるオペレータは、ん[]、と[[]]、返す同じ結果を?

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

    どちらの式も「1」を返します。

    x[1]

    x[[1]]

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

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

    x2 = list(1:4)

Rのドキュメント(?listR-intro)を私に向けないでください-私はそれを注意深く読みましたが、上で述べた種類の質問に答えるのに役立ちません。

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


3
ではx = list(1, 2, 3, 4)、これらの両方が同じ結果を返しませんx[1]、とx[[1]]。1つ目はリストを返し、2つ目は数値ベクトルを返します。下にスクロールすると、この質問に正しく回答した回答者はダークだけだったようです。
IRTFM 2016年

2
私はlist、Rがハッシュのようではない方法のリストを広げていることに気づきませんでした。もう1つ、注目に値すると思います。 listRでは、同じ参照名を持つ2つのメンバーを持つことができます。それobj <- c(list(a=1),list(a=2))が有効であり、2つの名前付き値「a」を含むリストを返すと考えてください。このインスタンスでは、の呼び出しobj["a"]は最初に一致したリスト要素のみを返します。Rの環境を使用して、参照された名前ごとに1項目のみのハッシュに類似した(おそらく同一の)動作を得ることができます。例x <- new.env(); x[["a"]] <- 1; x[["a"]] <- 2; x[["a"]]
russellpierce

1
私はこの投稿を過去6か月間に3回再読し、毎回より多くの悟りを見つけました。すばらしい質問といくつかのすばらしい答え。ありがとうございました。
Rich Lysakowski博士

回答:


150

それは本当にa listvectorRの違いを指摘しているので、あなたの質問の最後の部分に取り組むだけです:

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

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

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

これに関する最後のコメントとして:a data.frameは実際には(data.frameドキュメントからの)リストであることに注意してください:

データフレームは、クラス '"data.frame"'が指定された場合に、一意の行名を持つ同じ行数の変数のリストです。

そのため、aの列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番目の列に文字があるため、最初の列のデータ型を数値に変更できないことに注意してください。

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

4
これは役に立ちます。(ちなみに、ご存知かもしれませんが、例の「複雑なリスト」は、C ++やJavaなどで、「switch」ステートメントを持たない言語で「switch」ステートメントを複製する標準的な方法です。おそらく良い方法です。私がする必要があるときにRでこれを行うには)。+1
2010年

8
そうですがswitch、Rにはその目的に使用できる便利な関数があります(を参照help(switch))。
シェーン

63

質問に関しては、順番に説明し、いくつかの例を挙げましょう。

1)returnステートメントがリストを追加すると、リストが返されます。検討する

 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> 

3)同じものは返されません。あなたの例は

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

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

4)最後に、2つはそれぞれ、4つのスカラーを含むリストと単一の要素(たまたま4つの要素のベクトル)を含むリストを作成する際に異なります。


1
とても助かります、ありがとう。(回答の項目1について-同意しますが、私が考えていたのは、ユーザーが作成した関数ではなく、「strsplit」のような組み込み関数でした)。いずれにしても、+ 1してください。
ダグ

2
@dougアイテム#1について私は、特定の機能のヘルプをチェックすることが唯一の方法だと思いますValue。のように?strsplit:「xと同じ長さのリスト」。ただし、引数に応じて異なる値を返す関数がある可能性があることを考慮する必要があります(たとえば、sapplyはリストまたはベクトルを返すことができます)。
Marek、2013年

34

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

この記事のインデックスには、差の問題に対処[]し、を[[]]

つまり、[[]]はリストから単一のアイテムを選択し、選択されたアイテムのリストを[]返します。この例では、x = list(1, 2, 3, 4)'項目1は単一の整数ですx[[1]]が、単一の1をx[1]返し、値が1つだけのリストを返します。

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

> x[[1]]
[1] 1

ちなみに、A = array( 11:16, c(2,3) ); A[5]15はフラット配列ですか?
denis

13

リストが機能する(順序付けされた)ように機能する1つの理由は、ベクターが行わない、任意のノードで任意のタイプを含むことができる順序付けされたコンテナーの必要性に対処するためです。リストは、Rのさまざまな目的で再利用されますdata.frame。これには、任意のタイプの(ただし同じ長さの)ベクトルのリストであるaのベースの形成が含まれます。

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

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

@Shaneの回答に追加して、同じ結果を得たい場合は、以下を試してください。

x3 = as.list(1:4)

ベクトル1:4をリストに強制します。


11

これにもう1つポイントを追加します。

Rには、Python dictと同等のデータ構造があります。 hashパッケージのありますこれについては、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クラスはリストに非常に似ています。ただし、大規模なデータセットの方がパフォーマンスは優れています。


1
私はハッシュパッケージを知っています-元の質問で、従来のハッシュタイプに適したプロキシとして言及されています。
ダグ

また、hash :: hashの使用は、ハッシュ化された環境rpubs.com/rpierce/hashBenchmarksに関連して疑わしいユーティリティであることに注意してください。
russellpierce 2016年

9

あなたは言う:

別の方法として、関数を呼び出したときに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-ed)ので、どうやって同じことをすることができますか?


5
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)

4

これは非常に古い質問ですが、私の意見では、OPの懸念のいくつかに直接対処した人はいないので、新しい答えはいくつかの価値を追加する可能性があると思います。

受け入れられた回答が示唆していることにかかわらずlist、Rのオブジェクトハッシュマップではありません。pythonと並列化したいlist場合は、python lists(またはtuple実際にはs)に似ています。

ほとんどのRオブジェクトが内部に格納される方法を説明する方がよい(RオブジェクトのCタイプはSEXP)。それらは基本的に3つの部分で構成されています。

  • オブジェクトのRタイプ、長さ、その他のメタデータを宣言するヘッダー。
  • 標準のCヒープ割り当て配列(連続したメモリブロック)であるデータ部分。
  • 他のRオブジェクトへのポインターの名前付きリンクリストである属性(またはNULLオブジェクトに属性がない場合)。

内部の観点からは、たとえばlistnumericベクトルの違いはほとんどありません。格納する値は異なります。前に説明したパラダイムに2つのオブジェクトを分解しましょう。

x <- runif(10)
y <- list(runif(10), runif(3))

の場合x

  • ヘッダーには、タイプはnumericREALSXPC側で)、長さは10などと表示されます。
  • データ部分は、10個のdouble値を含む配列になります。
  • NULLオブジェクトには属性がないため、属性はです。

の場合y

  • ヘッダーには、タイプはlistVECSXPC側で)、長さは2などと表示されます。
  • データ部分は、2つのSEXPタイプへの2つのポインタを含む配列でrunif(10)あり、runif(3)それぞれとによって取得された値を指します。
  • 属性はNULLですx

したがって、numericベクトルとa の唯一の違いlistは、numericデータ部分がdouble値で構成されているのに対しlist、データ部分は他のRオブジェクトへのポインターの配列であることです。

名前はどうなりますか?名前は、オブジェクトに割り当てることができる属性の一部にすぎません。以下のオブジェクトを見てみましょう:

z <- list(a=1:3, b=LETTERS)
  • ヘッダーには、タイプはlistVECSXPC側で)、長さは2などと表示されます。
  • データ部分は、2つのSEXPタイプへの2つのポインタを含む配列で1:3あり、LETTERSそれぞれとによって取得された値を指します。
  • これで属性が存在し、値をnames持つcharacterRオブジェクトであるコンポーネントになりますc("a","b")

Rレベルから、attributes関数を使用してオブジェクトの属性を取得できます。

Rのハッシュマップに典型的なKey-Valueは単なる幻想です。あなたが言う時:

z[["a"]]

これは何が起こるかです:

  • [[サブセット関数が呼び出されます。
  • 関数("a")の引数はtype characterであるため、メソッドはnamesオブジェクトの属性(存在する場合)からそのような値を検索するように指示されますz
  • 場合はnames、属性が存在しない、NULL返されます。
  • 存在する場合、その"a"値が検索されます。"a"がオブジェクトの名前でない場合はNULLが返されます。
  • 存在する場合、位置が決定されます(例では1)。したがって、リストの最初の要素、つまりに相当するものが返されz[[1]]ます。

Key-Value検索はかなり間接的で、常に定位置です。また、覚えておくと便利です:

  • ハッシュマップでは、キーが持つ必要がある唯一の制限は、それがハッシュ可能でなければならないということですnamesRでは文字列(characterベクトル)でなければなりません。
  • ハッシュマップでは、2つの同一のキーを持つことはできません。Rでは、names繰り返し値を持つオブジェクトに割り当てることができます。例えば:

    names(y) <- c("same", "same")

    Rでは完全に有効です。試行するとy[["same"]]、最初の値が取得されます。この時点で理由を知っておく必要があります。

結論として、オブジェクトに任意の属性を与える機能は、外部の観点から異なるものの外観を提供します。ただし、R listはハッシュマップではありません。


2

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

  1. ベクトルはRのアトムです。たとえば、rpois(1e4,5)(5つの乱数)、numeric(55)(double上の長さ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演算もより意味があります。たとえば、型がdoubleであることが保証されていない場合cumsum(1:9)cumsum(list(1,2,3,4,5,'a',6,7,8,9))そうではありません。


2番目の質問について:

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

関数は、常に入力されるデータ型とは異なるデータ型を返します。plot入力としてプロットを取りませんが、プロットを返します。Argnumeric受け入れたとしてもを返しますcomplex。等。

(そしてstrsplit:ソースコードはここにあります。)


2

これはかなり古い質問ですが、Rの最初のステップで欠けていた知識、つまり、Rのオブジェクトとして手にあるデータをどのように表現するか、既存のオブジェクトから選択する方法に触れていることを指摘しなければなりません。R初心者が最初から「Rボックスの中」と考えるのは簡単ではありません。

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

質問に対する正確な回答は提供していませんが、以下の短いテキストは、Rを使い始めたばかりで、同様の質問をしている読者に役立つでしょう。

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

したがって、次の例のように、状況に応じて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

それが役立つ場合、私はRの「リスト」を他のOO以前の言語の「レコード」と考える傾向があります。

  • それらは包括的なタイプ(または、任意のアリティとフィールド名のすべての可能なレコードのタイプが利用可能である)についての仮定を行いません。
  • それらのフィールドは匿名にすることができます(厳密な定義順序でそれらにアクセスします)。

「レコード」という名前は、データベース用語では「レコード」(行)の標準的な意味と衝突します。これが、その名前が(フィールドの)リストとして提案した理由である可能性があります。


1

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