在R中对数据框的每一行应用函数

3

我希望在R中对数据框的每一行应用某些函数。

该函数可以返回单行数据框或什么也不返回(我猜 'return ()' 不返回任何东西?)。

我想将此函数应用于给定数据框的每一行,并获得结果数据框(可能比原始数据框更短,即拥有较少的行)。

例如,如果原始数据框类似于:

id size name
1  100  dave
2  200  sarah
3  50   ben

我正在使用的函数从数据框中获取一行(即单行数据框),如果名称与“brave”押韵,则原样返回它,否则返回null。结果应该是:

id size name
1  100  dave

这个例子实际上是指过滤数据框,并且我希望得到一个针对这种任务的答案,但也可以适用于更一般的情况,即即使辅助函数的结果(作用于单个行)可能是任意数据框(仅有一行)。请注意,即使在过滤的情况下,我也想使用一些复杂的逻辑(不是像$size>100这样简单的东西,而是由函数检查的更复杂的条件,比如boo(single_row_df))。
附注: 迄今为止,在这些情况下我所做的是使用apply(df, MARGIN=1)然后do.call(rbind ...),但我认为当我的数据框只有一行时会出现问题(我得到了Error in do.call(rbind, filterd) : second argument must be a list)。
更新: 根据Stephen的回复,我做了以下操作:
ranges.filter <- function(ranges,boo) {
    subset(x=ranges,subset=!any(boo[start:end]))
}

然后我使用类似这样的一些区间数据框来调用ranges.filter

start end
100   200
250   400
698   1520
1988  2147
...

还有一些布尔向量

(TRUE,FALSE,TRUE,TRUE,TRUE,...)

我希望过滤掉包含布尔向量中TRUE值的所有范围。例如,仅当布尔向量在100 .. 200位置为FALSE时,第一个范围100 .. 200才会留在数据框中。
这似乎可以解决问题,但我收到一个警告,提示“数值表达式有53个元素:仅使用第一个”。
3个回答

4

如果需要处理dataframe更一般的情况,请从CRAN获取plyr包并查看ddply函数。

install.packages(plyr)
library(plyr)
help(ddply)

做你想要的事情,无需大量调整。
例如...
> d
    x          y           z xx
1   1 0.68434946 0.643786918  8
2   2 0.64429292 0.231382912  5
3   3 0.15106083 0.307459540  3
4   4 0.65725669 0.553340712  5
5   5 0.02981373 0.736611949  4
6   6 0.83895251 0.845043443  4
7   7 0.22788855 0.606439470  4
8   8 0.88663285 0.048965094  9
9   9 0.44768780 0.009275935  9
10 10 0.23954606 0.356021488  4

我们希望在由“xx”定义的分组内计算x的平均值和标准差:
> ddply(d,"xx",function(r){data.frame(mean=mean(r$x),sd=sd(r$x))})
  xx mean        sd
1  3  3.0        NA
2  4  7.0 2.1602469
3  5  3.0 1.4142136
4  8  1.0        NA
5  9  8.5 0.7071068

它优雅地处理了所有令人讨厌的边缘情况,有时会让你卡住。

您能解释一下如何使用它吗?据我所知,它是针对列而不是行进行操作的。 - David B
plyr有很多文档,可以在包本身或其他地方的帮助中找到。ddply函数接受一个数据框,一个分组变量和一个函数;它通过分组变量拆分数据框,并调用每个拆分的函数。然后将结果转换回数据框。 - Spacedman
帮助内容非常简短。我该如何将数据框拆分成行?我需要添加一个带有唯一ID的虚拟列吗? - David B

1

你可能需要使用lapply而不是apply来强制结果为列表。

> rhymesWithBrave <- function(x) substring(x,nchar(x)-2) =="ave"
> do.call(rbind,lapply(1:nrow(dfr),function(i,dfr)
+                      if(rhymesWithBrave(dfr[i,"name"])) dfr[i,] else NULL,
+                      dfr))
  id size name
1  1  100 dave

但在这种情况下,subset 更为合适:

> subset(dfr,rhymesWithBrave(name))
  id size name
1  1  100 dave

如果你想在返回结果之前执行额外的转换,可以回到上面的lapply方法:

> add100tosize <- function(x) within(x,size <- size+100)
> do.call(rbind,lapply(1:nrow(dfr),function(i,dfr)
+                      if(rhymesWithBrave(dfr[i,"name"])) add100tosize(dfr[i,])
+                      else NULL,dfr))
  id size name
1  1  200 dave

或者,在这个简单的情况下,将函数应用于subset的输出。
> add100tosize(subset(dfr,rhymesWithBrave(name)))
  id size name
1  1  200 dave

更新:

要选择不在起始和结束之间的行,您可能需要构建另一个函数(注意:当对布尔/逻辑向量的结果求和时,TRUE值转换为1,FALSE值转换为0)

test <- function(x)
  rowSums(mapply(function(start,end,x) x >= start & x <= end,
                 start=c(100,250,698,1988),
                 end=c(200,400,1520,2147))) == 0

subset(dfr,test(size))

0

看起来你想使用 subset

subset(orig.df,grepl("ave",name))

第二个参数是一个逻辑表达式,用于确定哪些行被保留。您可以使此表达式使用尽可能多的列值,例如grepl("ave",name) & size>50


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接