对于R数据帧中的每一行

Car*_*tin 165 r rows dataframe

我有一个数据帧,对于该数据帧中的每一行,我必须进行一些复杂的查找并将一些数据附加到文件中.

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)
}
Run Code Online (Sandbox Code Playgroud)

在我的程序世界中,我会做类似的事情:

for (row in dataFrame) {
    #look up stuff using data from the row
    #write stuff to the file
}
Run Code Online (Sandbox Code Playgroud)

这样做的"R方式"是什么?

Jon*_*ang 115

你可以使用这个by()功能:

by(dataFrame, 1:nrow(dataFrame), function(row) dostuff)
Run Code Online (Sandbox Code Playgroud)

但是像这样直接迭代行很少是你想要的; 你应该尝试矢量化.我可以问一下循环中的实际工作是做什么的吗?

  • 你如何实际实现(行)?是dataframe $ column吗?数据帧[somevariableNamehere]?你怎么这么说呢?伪代码"函数(行)dostuff"实际上看起来怎么样? (12认同)
  • 简单修复0行的情况是使用[seq_len()](http://stat.ethz.ch/R-manual/R-devel/library/base/html/seq.html),插入`seq_len(nrow) (dataFrame))`代替`1:nrow(dataFrame)`. (7认同)
  • 如果数据框有0行,这将无法正常工作,因为`1:0`不为空 (5认同)
  • @Mike,将这个答案中的 `dostuff` 更改为 `str(row)` 您会看到控制台中打印了多行以 _" 'data.frame': 1 obs of x variables."_ 但要小心,更改`dostuff` 到 `row` 不会整体返回外部函数的 data.frame 对象。相反,它返回一行数据帧的列表。 (2认同)

kng*_*yen 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')
Run Code Online (Sandbox Code Playgroud)

  • 要小心,因为数据帧被转换为矩阵,你最终得到的结果(`x`)是一个向量.这就是为什么上面的例子必须使用数字索引; by()方法为您提供了data.frame,使您的代码更加健壮. (70认同)
  • 另请注意,您可以按名称引用列.所以:`wellName < - x [1]`也可以是`wellName < - x ["name"]`. (3认同)

Sha*_*ane 84

首先,乔纳森关于矢量化的观点是正确的.如果你的getWellID()函数是矢量化的,那么你可以跳过循环并只使用cat或write.csv:

write.csv(data.frame(wellid=getWellID(well$name, well$plate), 
         value1=well$value1, value2=well$value2), file=outputFile)
Run Code Online (Sandbox Code Playgroud)

如果getWellID()没有矢量化,那么Jonathan建议使用by或者knguyen的建议apply应该有效.

否则,如果你真的想使用for,你可以这样做:

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

您也可以尝试使用该foreach软件包,但它需要您熟悉该语法.这是一个简单的例子:

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

最后一个选项是使用plyr包中的函数,在这种情况下约定将与apply函数非常相似.

library(plyr)
ddply(dataFrame, .(x), function(x) { # do stuff })
Run Code Online (Sandbox Code Playgroud)

  • 即使getWellID没有矢量化,我认为你应该使用这个解决方案,并用`mapply(getWellId,well $ name,well $ plate)替换getWellId. (2认同)

Ł Ł*_*łłk 17

我用这个简单的实用功能:

rows = function(tab) lapply(
  seq_len(nrow(tab)),
  function(i) unclass(tab[i,,drop=F])
)
Run Code Online (Sandbox Code Playgroud)

或者更快,更不清晰的形式:

rows = function(x) lapply(seq_len(nrow(x)), function(i) lapply(x,"[",i))
Run Code Online (Sandbox Code Playgroud)

此函数只是将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)
}        
Run Code Online (Sandbox Code Playgroud)

您问题中的代码将进行最小的修改:

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)
}
Run Code Online (Sandbox Code Playgroud)


Jul*_*ian 13

我认为用基本R做这个的最好方法是:

for( i in rownames(df) )
   print(df[i, "column1"])
Run Code Online (Sandbox Code Playgroud)

优于for(i in 1:nrow(df))方法的优点是,如果df为空且nrow(df)= 0,则不会遇到麻烦.


Fer*_*n E 8

我很好奇非矢量化选项的时间表现.为此,我使用了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)
}
Run Code Online (Sandbox Code Playgroud)

和一个像他的例子中的数据框:

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 )
Run Code Online (Sandbox Code Playgroud)

为了将cat()方法与write.table()方法进行比较,我包含了两个向量化函数(肯定比其他函数更快)...

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)
Run Code Online (Sandbox Code Playgroud)

结果图像显示apply为非矢量化版本提供了最佳性能,而write.table()似乎优于cat(). ForEachRunningTime


Rob*_*inL 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)
Run Code Online (Sandbox Code Playgroud)

默认情况下,来自的返回值myfn会放入名为的df中的新列表列.out

如果这是您想要的唯一输出,则可以编写 purrrlyr::by_row(df, myfn)$.out