【问题标题】:How to call a function for each row of a data.frame?如何为data.frame的每一行调用一个函数?
【发布时间】:2018-06-19 22:20:31
【问题描述】:

我有一个带有多个参数的函数。这个函数返回一个data.frame。

我有另一个 data.frame。

现在我想为 data.frame 的每一行调用我的函数(作为参数)。我想 rbind 生成的 data.frames。

所以我想到了类似的东西

do.call(rbind, apply(df, 1, f))

是我的朋友。

但是:在此调用期间,df 被转换为矩阵。在此过程中,所有数字都转换为字符。所以我必须修改我的函数才能重新转换。这很笨拙,我担心我错过了什么。

所以我的问题是,我该怎么做?

示例见以下代码:

Sys.setenv(LANG = "en")
# Create data.frame
df <- data.frame(
  a = c('a', 'b', 'c'),
  b = c(1, 2, 3),
  stringsAsFactors = FALSE
)

# My function 
f <- function(x) {
  data.frame(
    x = rep(paste(rep(x[['a']], x[['b']]), collapse=''),x[['b']]),
    y = 2 * x[['b']],
    stringsAsFactors = FALSE
  )
}

apply(df, 1, f)

这里我得到了错误:

Error in 2 * x[["b"]] : non-numeric argument to binary operator 

所以我将函数 f 更改为函数 g:

g <- function(x) {
  data.frame(
    x = rep(paste(rep(x[['a']], as.numeric(x[['b']])), collapse=''), as.numeric(x[['b']])),
    y = 2 * as.numeric(x[['b']]),
    stringsAsFactors = FALSE
  )
}

现在我可以打电话了

 do.call(rbind, apply(df, 1, g))

我明白了

    x y
1   a 2
2  bb 4
3  bb 4
4 ccc 6
5 ccc 6
6 ccc 6

我尝试使用 for 循环。

result <- f(df[1,])
for(i in 2:nrow(df)){
  result <- rbind(result, f(df[i,]))
}
result

确实有效。但这不可能是 R 方式。 for 循环不是“R-ish” 有太多可能出错的地方。也许 df 可以为空或只有一行。

那么什么是 base-R 或 dplyr/tidyverse 解决方案?

【问题讨论】:

  • 我建议颠倒您的帖子的顺序——从您想要做的事情开始(示例输入和输出),然后向我们展示您的尝试。第一次通读后我有点困惑。
  • 你知道?strrep吗?例如strrep(df$a,df$b) 是一个很好的起点。
  • 请注意apply 几乎立即将df 转换为matrix,所以x[['b']] 已经是character 马上
  • @nicola 重复一个字符串不是重点。这只是一个示例函数。我想为 df 的每一行调用一个函数并 rbind 结果。
  • @MichaelChirico 是的,这就是我寻找更好解决方案的原因。

标签: r dataframe tidyverse


【解决方案1】:

好吧,apply() 用于矩阵,不与 data.frames 一起使用。在这种情况下确实应该避免这种情况。最好编写带有适当参数的函数,而不是需要传递 data.frame 行。

f <- function(a, b) {
  data.frame(
    x = rep(paste(rep(a, b), collapse=''), b),
    y = 2 * b,
    stringsAsFactors = FALSE
  )
}

然后您可以使用更传统的map() 样式方法(如果只使用两列尤其容易)

purrr::map2_df(df$a, df$b, f)

如果列更多,(以及与参数名称匹配的列名),您可以使用

purrr::pmap_df(df, f)

【讨论】:

  • 这很有趣。谢谢。我会尝试将此应用于我的实际问题。
  • 我的“真实世界功能” f 确实有两列以上。所以首先我想我可以将它们作为“...”参数传递。但这不起作用。所以我尝试了 pmap_dfr。但是 pmap_dfr 会破坏 Date (github.com/tidyverse/purrr/issues/358) 类的变量。
【解决方案2】:

我相信你可以在data.table 中非常干净地做到这一点:

library(data.table)
setDT(df)
df[ , .(x = rep(paste(rep(a, b), collapse = ''), b), y = 2*b), 
   keyby = seq_len(nrow(df))]
#    seq_len   x y
# 1:       1   a 2
# 2:       2  bb 4
# 3:       2  bb 4
# 4:       3 ccc 6
# 5:       3 ccc 6
# 6:       3 ccc 6

keyby = seq_len(nrow(df)) 部分是最笨重的部分;这尤其是针对data.table 的一些增强请求的主题, 例如,#1063

【讨论】:

    【解决方案3】:

    tidyverse 答案:

    > df %>% split(1:nrow(df)) %>% map(f) %>% bind_rows()
        x y
    1   a 2
    2  bb 4
    3  bb 4
    4 ccc 6
    5 ccc 6
    6 ccc 6
    

    你可以split df by rows(它给你一个小标题列表),然后map 函数到每一行(函数返回一个数据帧),然后bind_rows() 全部回到一起。

    【讨论】:

      猜你喜欢
      • 2016-04-02
      • 1970-01-01
      • 2016-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-20
      • 2017-07-05
      • 1970-01-01
      相关资源
      最近更新 更多