Rデータフレームの各行


173

データフレームがあり、そのデータフレームの各行について、いくつかの複雑なルックアップを実行し、データをファイルに追加する必要があります。

dataFrameには、生物学的研究で使用される96ウェルプレートから選択したウェルの科学的結果が含まれているので、次のようなことをしたいと思います。

for (well in dataFrame) {
  wellName <- well$name    # string like "H1"
  plateName <- well$plate  # string like "plate67"
  wellID <- getWellID(wellName, plateName)
  cat(paste(wellID, well$value1, well$value2, sep=","), file=outputFile)
}

私の手続きの世界では、私は次のようなことをします:

for (row in dataFrame) {
    #look up stuff using data from the row
    #write stuff to the file
}

これを行う「Rウェイ」とは何ですか?


ここであなたの質問は何ですか?data.frameは2次元オブジェクトであり、行は各列の「変数」の「観測」のセットであることが多いため、行をループすることは完全に通常の方法です。
Dirk Eddelbuettel、2009年

16
私がやっていることは次のとおりです:for(1:nrow(dataFrame)のインデックス){row = dataFrame [index、]; #行で何かをする}これは私には非常にきれいに思われたことはありません。
カールコリエルマーティン

1
getWellIDはデータベースなどを呼び出しますか?そうでなければ、ジョナサンはおそらく正しいので、これをベクトル化できます。
シェーン

回答:


103

apply()関数を使用してこれを試すことができます

> d
  name plate value1 value2
1    A    P1      1    100
2    B    P2      2    200
3    C    P3      3    300

> f <- function(x, output) {
 wellName <- x[1]
 plateName <- x[2]
 wellID <- 1
 print(paste(wellID, x[3], x[4], sep=","))
 cat(paste(wellID, x[3], x[4], sep=","), file= output, append = T, fill = T)
}

> apply(d, 1, f, output = 'outputfile')

76
データフレームは行列に変換され、最終的に(x)になるのはベクトルなので、注意してください。このため、上記の例では数値インデックスを使用する必要があります。by()アプローチはdata.frameを提供し、コードをより堅牢にします。
ダレン・クック

私にとってはうまくいきませんでした。apply関数は、fに指定されたすべてのxを行ではなく文字値として扱いました。
Zahy 2014

3
列を名前で参照できることにも注意してください。だから:wellName <- x[1]も可能性がありますwellName <- x["name"]
–founddrama

1
ダレンがロバストと言ったとき、彼は列の順序をシフトするようなものを意味しました。この回答は機能しませんが、by()を使用した回答は引き続き機能します。
HelloWorld 2016年

120

次のby()関数を使用できます。

by(dataFrame, 1:nrow(dataFrame), function(row) dostuff)

しかし、このように行を直接反復処理することがめったにありません。代わりにベクトル化してみてください。ループ内の実際の作業が何をしているのか質問できますか?


5
1:0空ではないためにデータフレームに0行がある場合、これはうまく機能しません
sds

10
0行の場合の簡単な修正は、seq_len()を使用seq_len(nrow(dataFrame))して、の代わりに挿入することです1:nrow(dataFrame)
ジム・

13
(行)を実際にどのように実装しますか?dataframe $ columnですか?dataframe [somevariableNamehere]?どのように実際にその行を言うのですか。疑似コード「function(row)dostuff」は実際にはどのように見えるでしょうか?
uh_big_mike_boi

1
@マイク、dostuffこの回答を変更すると、「data.frame」でstr(row) 始まる複数の行がコンソールに出力されます:1つのobs of x変数。 ただし、に変更dostuffrowても、全体として外部関数のdata.frameオブジェクトは返されません。代わりに、1行のデータフレームのリストを返します。
pwilcox

91

まず、ジョナサンのベクトル化に関するポイントは正しいです。getWellID()関数がベクトル化されている場合は、ループをスキップして、catまたはwrite.csvを使用できます。

write.csv(data.frame(wellid=getWellID(well$name, well$plate), 
         value1=well$value1, value2=well$value2), file=outputFile)

getWellID()がベクトル化されていない場合は、ジョナサンの使用の推奨byまたはknguyenの提案が機能するapplyはずです。

それ以外の場合、本当に使用したい場合は、for次のようなことができます。

for(i in 1:nrow(dataFrame)) {
    row <- dataFrame[i,]
    # do stuff with row
}

foreachパッケージを使用することもできますが、その構文に慣れる必要があります。以下に簡単な例を示します。

library(foreach)
d <- data.frame(x=1:10, y=rnorm(10))
s <- foreach(d=iter(d, by='row'), .combine=rbind) %dopar% d

最後のオプションは、plyrパッケージから関数を使用することです。その場合、規則は適用関数と非常に似ています。

library(plyr)
ddply(dataFrame, .(x), function(x) { # do stuff })

シェーン、ありがとう。ベクトル化されたgetWellIDの記述方法がわかりません。私が今しなければならないことは、リストの既存のリストを掘り下げて調べたり、データベースから引き出したりすることです。
カールコリエルマーティン

getWellID質問(つまり、この関数をベクトル化できるか?)を個別に投稿してください。私(または他の誰か)が回答すると確信しています。
シェーン

2
getWellIDがベクトル化されていなくても、このソリューションを使用して、getWellIdをに置き換える必要があると思いますmapply(getWellId, well$name, well$plate)
ジョナサンチャン

データベースからプルした場合でも、それらを一度にプルして、結果をRでフィルタリングできます。これは、反復関数よりも高速です。
シェーン

+1- foreach私はそれから地獄を使います。
Josh Bode 2013年

20

基本的なRでこれを行う最良の方法は次のとおりです。

for( i in rownames(df) )
   print(df[i, "column1"])

- for( i in 1:nrow(df))アプローチよりも優れているのは、dfが空の場合でも問題が発生しないことですnrow(df)=0


17

私はこの単純なユーティリティ関数を使用します:

rows = function(tab) lapply(
  seq_len(nrow(tab)),
  function(i) unclass(tab[i,,drop=F])
)

または、より速く、明確ではない形式:

rows = function(x) lapply(seq_len(nrow(x)), function(i) lapply(x,"[",i))

この関数は、data.frameを行のリストに分割するだけです。次に、このリストに対して通常の「for」を作成できます。

tab = data.frame(x = 1:3, y=2:4, z=3:5)
for (A in rows(tab)) {
    print(A$x + A$y * A$z)
}        

質問のコードは最小限の変更で機能します。

for (well in rows(dataFrame)) {
  wellName <- well$name    # string like "H1"
  plateName <- well$plate  # string like "plate67"
  wellID <- getWellID(wellName, plateName)
  cat(paste(wellID, well$value1, well$value2, sep=","), file=outputFile)
}

ストレートリストにアクセスする方が、data.frameにアクセスする方が高速です。
LŁaniewski-Wołłk

1
ダブルラップで同じことを行うほうがさらに速いことに気づきました:rows = function(x)lapply(seq_len(nrow(x))、function(i)lapply(x、function(c)c [i]))
Ł Łaniewski-Wołłk2016年

したがって、内部はlapplyデータセット全体の列を反復処理し、x各列に名前を付けciその列ベクトルからth番目のエントリを抽出します。これは正しいです?
アーロンマクデイド、

非常に素晴らしい!私の場合、「因子」値から基礎となる値に変換する必要がありましたwellName <- as.character(well$name)
スティーブピッチャー2017

9

ベクトル化されていないオプションの時間パフォーマンスに興味がありました。この目的のために、私はknguyenによって定義された関数fを使用しました

f <- function(x, output) {
  wellName <- x[1]
  plateName <- x[2]
  wellID <- 1
  print(paste(wellID, x[3], x[4], sep=","))
  cat(paste(wellID, x[3], x[4], sep=","), file= output, append = T, fill = T)
}

そして彼の例のようなデータフレーム:

n = 100; #number of rows for the data frame
d <- data.frame( name = LETTERS[ sample.int( 25, n, replace=T ) ],
                  plate = paste0( "P", 1:n ),
                  value1 = 1:n,
                  value2 = (1:n)*10 )

cat()のアプローチとwrite.table()のアプローチを比較するために、2つのベクトル化された関数(確かに他の関数よりも速い)を含めました...

library("ggplot2")
library( "microbenchmark" )
library( foreach )
library( iterators )

tm <- microbenchmark(S1 =
                       apply(d, 1, f, output = 'outputfile1'),
                     S2 = 
                       for(i in 1:nrow(d)) {
                         row <- d[i,]
                         # do stuff with row
                         f(row, 'outputfile2')
                       },
                     S3 = 
                       foreach(d1=iter(d, by='row'), .combine=rbind) %dopar% f(d1,"outputfile3"),
                     S4= {
                       print( paste(wellID=rep(1,n), d[,3], d[,4], sep=",") )
                       cat( paste(wellID=rep(1,n), d[,3], d[,4], sep=","), file= 'outputfile4', sep='\n',append=T, fill = F)                           
                     },
                     S5 = {
                       print( (paste(wellID=rep(1,n), d[,3], d[,4], sep=",")) )
                       write.table(data.frame(rep(1,n), d[,3], d[,4]), file='outputfile5', row.names=F, col.names=F, sep=",", append=T )
                     },
                     times=100L)
autoplot(tm)

結果の画像は、applyがベクトル化されていないバージョンで最高のパフォーマンスを発揮することを示していますが、write.table()はcat()よりも優れているようです。 ForEachRunningTime


6

これにby_rowは、パッケージの関数を使用できますpurrrlyr

myfn <- function(row) {
  #row is a tibble with one row, and the same 
  #number of columns as the original df
  #If you'd rather it be a list, you can use as.list(row)
}

purrrlyr::by_row(df, myfn)

デフォルトでは、からの戻り値myfnはと呼ばれるdfの新しいリスト列に入れられます.out

これがあなたが望む唯一の出力であれば、あなたは書くことができます purrrlyr::by_row(df, myfn)$.out


2

さて、あなたは他の言語と同等のRを要求したので、私はこれを試みました。私は実際にはRでより効率的な手法を検討していませんが、動作するようです。

> myDf <- head(iris)
> myDf
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa
> nRowsDf <- nrow(myDf)
> for(i in 1:nRowsDf){
+ print(myDf[i,4])
+ }
[1] 0.2
[1] 0.2
[1] 0.2
[1] 0.2
[1] 0.2
[1] 0.4

ただし、カテゴリ列の場合は、必要に応じてas.character()を使用して型キャストできるデータフレームを取得します。

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