我有一个数据框,对于该数据框中的每一行,我必须进行一些复杂的查找并将一些数据附加到文件中。
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方式”是什么?
您可以使用 by()
函数:
by(dataFrame, seq_len(nrow(dataFrame)), function(row) dostuff)
但是像这样直接遍历行很少是你想要的;你应该尝试矢量化。我能问一下循环中的实际工作在做什么吗?
你可以试试这个,使用 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')
x
) 是一个向量。这就是为什么上面的例子必须使用数字索引的原因; by() 方法为您提供了一个 data.frame,这使您的代码更加健壮。
wellName <- x[1]
也可能是 wellName <- x["name"]
。
首先,乔纳森关于矢量化的观点是正确的。如果你的 getWellID() 函数是矢量化的,那么你可以跳过循环,只使用 cat 或 write.csv:
write.csv(data.frame(wellid=getWellID(well$name, well$plate),
value1=well$value1, value2=well$value2), file=outputFile)
如果 getWellID() 未矢量化,则 Jonathan 建议使用 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
包中的函数,在这种情况下,约定将与 apply 函数非常相似。
library(plyr)
ddply(dataFrame, .(x), function(x) { # do stuff })
mapply(getWellId, well$name, well$plate)
。
foreach
- 我将使用那个地狱。
我认为使用基本 R 做到这一点的最佳方法是:
for( i in rownames(df) )
print(df[i, "column1"])
与 for( i in 1:nrow(df))
方法相比的优势在于,如果 df
为空且 nrow(df)=0
,您不会遇到麻烦。
我使用这个简单的实用功能:
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)
}
lapply
遍历整个数据集 x
的列,为每列指定名称 c
,然后从该列向量中提取第 i
个条目。这个对吗?
wellName <- as.character(well$name)
。
我很好奇非矢量化选项的时间性能。为此,我使用了 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() 方法进行比较...
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)
https://i.stack.imgur.com/hHQ3M.png
为此,您可以使用包 purrrlyr
中的 by_row
函数:
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
的返回值被放入名为 .out
的 df 中的新 list column 中。
如果这是您想要的唯一输出,您可以编写 purrrlyr::by_row(df, myfn)$.out
好吧,既然你要求 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() 进行类型转换。
你可以为列表对象做点什么,
data("mtcars")
rownames(mtcars)
data <- list(mtcars ,mtcars, mtcars, mtcars);data
out1 <- NULL
for(i in seq_along(data)) {
out1[[i]] <- data[[i]][rownames(data[[i]]) != "Volvo 142E", ] }
out1
或数据框,
data("mtcars")
df <- mtcars
out1 <- NULL
for(i in 1:nrow(df)) {
row <- rownames(df[i,])
# do stuff with row
out1 <- df[rownames(df) != "Volvo 142E",]
}
out1
1:0
不为空seq_len(nrow(dataFrame))
代替1:nrow(dataFrame)
。dostuff
更改为str(row)
您将在控制台中看到以 " 'data.frame': 1 obs of x variables 开头的多行打印。" 但要小心,将dostuff
更改为row
不会为整个外部函数返回 data.frame 对象。相反,它返回一行数据帧的列表。