【问题标题】:How can I map a function to apply to only certain rows in a data frame?如何映射函数以仅应用于数据框中的某些行?
【发布时间】:2019-11-01 19:57:36
【问题描述】:

我有一个函数,我想只迭代数据集中的某些行,然后将结果保存在数据集中的变量中。

例如说我有这个设置:

library(tidyverse)

add_one <- function(vector, x_id){
  return(vector[x_id] + 1)
}

test <- data.frame(x = c(1,2,3,4), y = c(1,2,3,4), run_on = c(TRUE,FALSE,TRUE,FALSE))
test

所以测试数据框看起来像:

>  x y run_on
>1 1 1   TRUE
>2 2 2  FALSE
>3 3 3   TRUE
>4 4 4  FALSE

所以我想要做的是遍历数据框并将 y 列设置为将函数 add_one() 应用于 x 列的结果,仅适用于 run_on 为 TRUE 的行。我希望最终结果如下所示:

>  x y run_on
>1 1 2   TRUE
>2 2 2  FALSE
>3 3 4   TRUE
>4 4 4  FALSE

我已经能够使用 apply() 在所有行上迭代该函数。比如:

test$y <- apply(test,1,add_one,x_id = 1)
test

>  x y run_on
>1 1 2   TRUE
>2 2 3  FALSE
>3 3 4   TRUE
>4 4 5  FALSE

但这也将函数应用于第 2 行和第 4 行,这是我不想要的。我怀疑可能有某种方法可以使用 ::purrr 中的 map() 函数版本来做到这一点,这就是我将这篇文章标记为这样的原因。

实际上,我正在使用这种过程多次重复迭代大型数据集,因此我需要它自动且干净地完成。任何帮助或建议将不胜感激。

更新

我设法找到了解决方案。这里提供的一些解决方案在我的玩具示例中确实有效,但没有扩展到我实际使用的更复杂的功能。最终起作用的是类似于 tmfmnk 建议的东西。我只是将原始函数包装在另一个函数中,该函数包含一个 if 语句以确定是否应用原始函数。因此,为了扩展我的玩具示例,我的解决方案如下所示:

add_one_if <- function(vector, x_id, y_id, run_on_id){
    if(vector[run_on_id]){
        return(add_one(vector,x_id))}
    else{
        return(vector[x_id])
    }
}

test$y <- apply(test, 1, add_one_if, x_id = 1, y_id = 2, run_on_id = 3)

这似乎有点令人费解,但它对我有用,并且以我需要的方式可重复且可靠。

【问题讨论】:

  • with(test, y + run_on) ;)
  • 如果 jcr 真的只是在列中添加一个,这是一个聪明的解决方案。但是,听起来这只是一个示例,在这种情况下,这不会推广到其他功能。

标签: r purrr


【解决方案1】:

你也可以这样做:

add_one <- function(data, vector, x_id, n, is.true = c(TRUE, FALSE)) {  
 if (is.true) {
  return(data[[vector]] + (data[[x_id]]) * n)
 } else {
  return(data[[vector]] + (!data[[x_id]]) * n)
 }
}   

add_one(test, vector = "y", x_id = "run_on", 1, is.true = TRUE)

[1] 2 2 4 4

add_one(test, vector = "y", x_id = "run_on", 5, is.true = FALSE)

[1] 1 7 3 9

【讨论】:

    【解决方案2】:

    你的实际情况可能比这允许的更复杂,但为什么不直接使用 ifelse 呢?

    test$y <- ifelse(test$run_on,add_one(test,x),y)
    

    甚至:

    test$y[test$run_on]<-add_one(test[run_on,],x)
    

    【讨论】:

    • 不幸的是,这不起作用,因为 y 和 run_on 的列不是 r 环境中的对象——它们只在数据框中。作为一个 base:: 函数,ifelse() 不够聪明,无法解决这个问题并返回一个错误,就像 object 'y' not found
    • 现在不能测试它,但是 test$y 在那种情况下不能工作吗?另外,第二个选项呢?
    【解决方案3】:

    在将同一个函数应用于多个列之前,您不需要使用purrr。由于您只想修改 一个 列,但根据条件您可以使用mutate() + case_when()

    mutate(test, y = case_when(run_on ~ add_one(y),
                               !run_on ~ y))
    #>   x y run_on
    #> 1 1 2   TRUE
    #> 2 2 2  FALSE
    #> 3 3 4   TRUE
    #> 4 4 4  FALSE
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-02-24
      • 1970-01-01
      • 2018-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多