web-dev-qa-db-ja.com

Rデータフレームを行ごとに作成する

Rで行ごとにデータフレームを構築したいと思います。いくつかの検索を行いましたが、空のリストを作成し、リストインデックススカラーを保持し、そのたびにリストに追加するよう提案しました。単一行のデータフレームを作成し、リストインデックスを1つ進めます。最後に、リストのdo.call(rbind,)

これは機能しますが、非常に面倒です。同じ目標を達成するための簡単な方法はありませんか?

明らかに、いくつかのapply関数を使用できず、明示的に行ごとにデータフレームを作成する必要がある場合を指します。少なくとも、最後に使用したインデックスを明示的に追跡する代わりに、リストの最後にPushする方法はありますか?

101
David B

rbind()を追加または使用して、行ごとに拡大できます。

それはあなたがすべきだという意味ではありません。動的に成長する構造は、Rでコーディングする最も効率の悪い方法の1つです。

可能であれば、data.frame全体を事前に割り当てます。

N <- 1e4  # total number of rows to preallocate--possibly an overestimate

DF <- data.frame(num=rep(NA, N), txt=rep("", N),  # as many cols as you need
                 stringsAsFactors=FALSE)          # you don't know levels yet

そして、操作中に一度に行を挿入します

DF[i, ] <- list(1.4, "foo")

これは、任意のdata.frameで機能し、はるかに効率的です。 Nをオーバーシュートした場合、常に最後の空の行を縮小できます。

89

行をNULLに追加できます:

df<-NULL;
while(...){
  #Some code that generates new row
  rbind(df,row)->df
}

例えば

df<-NULL
for(e in 1:10) rbind(df,data.frame(x=e,square=e^2,even=factor(e%%2==0)))->df
print(df)
47
mbq

これは、do.call(rbind,)の出力でMap()を使用する方法のばかげた例です(lapply()に似ています)。

> DF <- do.call(rbind,Map(function(x) data.frame(a=x,b=x+1),x=1:3))
> DF
  x y
1 1 2
2 2 3
3 3 4
> class(DF)
[1] "data.frame"

私はこの構成を頻繁に使用します。

9
hatmatrix

私がRcppが大好きな理由は、R Coreの考え方が常に得られるとは限らないことです。

哲学的に言えば、あなたは機能的パラダイムに関して罪の状態にあります。それは、すべての値を保証しようとします出現他のすべての値から独立しています。 1つの値を変更しても、Cで表現を共有するポインターで得られるように、別の値に目に見える変化が生じることはありません。

問題は、関数型プログラミングが小さな船に信号を送って邪魔にならないようにし、小さな船が「私は灯台だ」と答えたときに起こります。その間に処理したい大きなオブジェクトに小さな一連の小さな変更を加えると、灯台の領域に行くことができます。

C++ STLでは、Push_back()は生き方です。機能的にしようとはしませんが、一般的なプログラミングのイディオム効率的にに対応しようとします。

舞台裏の賢さを活かして、各世界に片足を配置することができます。スナップショットベースのファイルシステムは良い例です(これはユニオンマウントなどの概念から発展したもので、これも両側に適用されます)。

R Coreがこれを実行したい場合、基礎となるベクトルストレージはユニオンマウントのように機能します。ベクトルストレージへの1つの参照は、サブスクリプト1:Nに対して有効であり、同じストレージへの別の参照は、サブスクリプト1:(N+1)に対して有効です。まだ有効に参照されていない予約済みのストレージがありますが、簡単なPush_back()には便利です。既存の参照が有効と見なす範囲外に追加する場合、機能概念に違反しません。

最終的に行を増分的に追加すると、予約済みストレージが不足します。すべての新しいコピーを作成する必要がありますが、ストレージに増加分を掛けてください。私が使用したSTL実装は、割り当てを拡張するときにストレージを2倍する傾向があります。 R Internalsで、ストレージが20%増加するメモリ構造があると読んだと思いました。いずれにしても、成長操作は、追加された要素の総数に対する対数頻度で発生します。償却ベースでは、これは通常許容されます。

舞台裏でのトリックが進むにつれて、私はさらに悪いことを見てきました。 Push_back()でデータフレームに新しい行を追加するたびに、トップレベルのインデックス構造をコピーする必要があります。新しい行は、古い機能値に影響を与えることなく共有表現に追加できます。ガベージコレクターがそれほど複雑になるとは思いません。 Push_front()を提案していないので、すべての参照は、割り当てられたベクトルストレージの前へのプレフィックス参照です。

8
Allan Stokes

マトリックスなしで生のデータフレームを作成するこの方法を見つけました。

自動列名付き

df<-data.frame(
        t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
        ,row.names = NULL,stringsAsFactors = FALSE
    )

列名付き

df<-setNames(
        data.frame(
            t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
            ,row.names = NULL,stringsAsFactors = FALSE
        ), 
        c("col1","col2","col3")
    )
1
phili_b

Dirk Eddelbuettelの答えは最高です。ここでは、データフレームのディメンションまたはデータ型を事前に指定しなくても済むことに注意してください。これは、複数のデータ型と多数の列がある場合に役立つことがあります。

row1<-list("a",1,FALSE) #use 'list', not 'c' or 'cbind'!
row2<-list("b",2,TRUE)  

df<-data.frame(row1,stringsAsFactors = F) #first row
df<-rbind(d,row2) #now this works as you'd expect.
1
John

行になる予定のベクトルがある場合は、c()を使用してそれらを連結し、行ごとに行列に渡し、その行列をデータフレームに変換します。

たとえば、行

dummydata1=c(2002,10,1,12.00,101,426340.0,4411238.0,3598.0,0.92,57.77,4.80,238.29,-9.9)
dummydata2=c(2002,10,2,12.00,101,426340.0,4411238.0,3598.0,-3.02,78.77,-9999.00,-99.0,-9.9)
dummydata3=c(2002,10,8,12.00,101,426340.0,4411238.0,3598.0,-5.02,88.77,-9999.00,-99.0,-9.9)

したがって、データフレームに変換できます。

dummyset=c(dummydata1,dummydata2,dummydata3)
col.len=length(dummydata1)
dummytable=data.frame(matrix(data=dummyset,ncol=col.len,byrow=TRUE))

確かに、2つの大きな制限があります。(1)これはシングルモードデータでのみ機能します。(2)これが機能するためには、最後の#列を知っている必要があります(つまり、最大行長が不明な不規則配列apriori)。

この解決策は簡単に思えますが、Rでの型変換の経験から、将来的に新しい課題が生じると確信しています。誰でもこれについてコメントできますか?

0
Keegan Smith