【问题标题】:How can I pass dataframe variables to a for-loop using pipes with dplyr?如何使用带有 dplyr 的管道将数据帧变量传递给 for 循环?
【发布时间】:2020-02-25 02:12:57
【问题描述】:

我正在尝试使用 dplyr 管道末端的 for 循环对我的 df 子集进行一些计算,但我从 df 传递给 for 循环的变量无法识别。

我尝试按照这篇文章中的步骤进行操作: use for loop with pipes in R。 基本上,我将 for 循环包装在用户定义的函数中,并通过管道将 df 传递给函数。

我正在使用产品销售数据集,并尝试计算每个产品在每个季度内每对时期的平均销售额(促销的一种销售基准)。例如,我第一次遍历子集将计算周期 2 和 3 的平均值,省略 1。我的第二次遍历将排除周期 2 并计算 1 和 3 的平均销售额,等等。

#Create dataframe
Article <- rep(1:3, each = 6)
Quarter <- rep(1:2, each = 3, 3)
Period <- rep(1:3, 6)
Sales <- sample(10:20, 18, replace = T)

df <-data.frame(Article, Quarter, Period, Sales)

foo <- function(x){
  for (i in unique(Period)) {
    filter(Period != i) %>%
      summarize(average_sales = mean(Sales))
  } 
  return(x)
}

df <- df %>% 
  group_by(Article, Quarter) %>%
  foo() 

#Desired resultant df:
average_sales <- c(14.5, 16.5, 12, 12, 16, 15, 16.5, 12.5, 16, 15, 14, 18, 11.5, 11, 11.5, 16, 16, 12)
df$average_sales <- average_sales
print(df, row.names = F)
Article Quarter Period Sales average_sales
      1       1      1    14          14.5
      1       1      2    10          16.5
      1       1      3    19          12.0
      1       2      1    19          12.0
      1       2      2    11          16.0
      1       2      3    13          15.0
      2       1      1    12          16.5
      2       1      2    20          12.5
      2       1      3    13          16.0
      2       2      1    17          15.0
      2       2      2    19          14.0
      2       2      3    11          18.0
      3       1      1    11          11.5
      3       1      2    12          11.0
      3       1      3    11          11.5
      3       2      1    12          16.0
      3       2      2    12          16.0
      3       2      3    20          12.0

我知道这段代码仍然没有给出我的最终结果,理想情况下它是 df 中的第五个变量,其中包含每个时期的其他两个时期的平均销售额,但这就是我所在的地方卡住。我什至不确定 for 循环是否是解决这个问题的最佳/最有效的方法(我是一个有限的 R 编码器,不熟悉整套 tidyverse 工具),但是关于如何完成的任何建议数据框也将不胜感激。谢谢!

【问题讨论】:

  • 你能举例说明预期的结果吗?
  • 你的函数接受一个名为x的参数。然后它在循环中执行一些不相关的事情(x 未在循环中使用),从不分配任何内容(循环内没有 &lt;-=),因此不会保存循环中发生的任何内容,并且然后它返回x,即未修改的输入。 (此评论的目的是试图帮助您了解为什么您的功能无法正常工作......我相信很快就会有人提出一个很好的解决方案。)
  • @Gregor 感谢您的反馈。新手编码员在这里(因为所有中间编码员都表明自己,大声笑)。我假设 df 通过管道作为x 传递,但似乎并非如此。
  • df 确实作为x 传递给函数。但是你根本不会在函数内部使用x,除了在最后返回它。在你的 for 循环内部,在函数内部,你需要告诉filter 使用xx %&gt;% filter...。但是您还需要将每个循环迭代的结果保存在某处。您运行了所有正确的计算,但由于您没有将它们分配给 &lt;-=,因此它们不会被存储。
  • @LloydChristmas 完成。

标签: r dataframe dplyr


【解决方案1】:

把我的 cmets 变成一个答案,用一些简化的例子来帮助你理解如何修复你的函数:

foo1 <- function(x) {
  1 + 2
  return(x)
}

foo1(0)
# [1] 0

foo1 是我对你的函数的简化版本。 In 接受参数x,执行不使用x 的操作,然后返回x。这是一个毫无意义的功能——我们做1 + 2 并不重要,因为结果没有做任何事情。在其最后一行中,foo1 返回与传递给它的值相同的值,保持不变。

foo2 <- function(x) {
  x + 1
  return(x)
}

foo2(0)
# [1] 0

foo2 稍微好一点,但最终同样毫无意义。中间计算使用x,逻辑上向前迈了一步,但是结果x + 1没有保存,函数还是返回原来传入的x

foo3 <- function(x) {
  y <- x + 1
  return(y)
}

foo3(0)
# [1] 1

最后,一个可以做某事的函数! foo3 在其输入中添加 1,修改输入以将结果存储在新变量 y 中(它也可以修改 xx &lt;- x + 1),然后返回修改后的变量。

有了for 循环,你不能只做y &lt;- for(...),我们需要在循环内进行赋值

foo4 <- function(x) {
  for(i in 1:3) {
    y <- x + i
  }
  return(y)
}

foo4(0)
# [1] 3

foo4 显示了一个常见的初学者错误 - y 每次循环都会被修改,但每次都会被覆盖。 y 将是x + 1,第一次通过,然后y 将是x + 2,然后当i 是3 y 将是x + 3,没有以前的迭代记忆。我们需要给y 一些长度,以便它可以单独存储每个迭代。

foo5 <- function(x) {
  y <- numeric(3)
  for(i in 1:3) {
    y[i] <- x + i
  }
  return(y)
}

foo5(0)
# [1] 1 2 3

foo5 不错!我们将y初始化为正确的长度,循环的每次迭代将其结果保存到y的不同部分,然后在最后返回整个y

foo <- function(x) {
  y <- list() # with a `list`, we don't absolutely need to specify the length upfront
  for(i in unique(x$Period)) {
    # use [[ for list assignment
    y[[i]] <- x %>%
      filter(Period != i) %>%
      summarize(
        period_excluded = i, # we'll use this to keep track 
        average_sales = mean(Sales)
      )
  } 
  # do ourselves a favor and turn the list of data frames into a single data frame 
  # with bind_rows before returning
  return(bind_rows(y))
}

foo(df)
#   period_excluded average_sales
# 1               1      14.58333
# 2               2      14.16667
# 3               3      15.58333

【讨论】:

    【解决方案2】:

    如果我们正在寻找一种方法来获取特定“期间”的“销售”以外的元素的 mean,请获取“销售”与“销售”的 sum 的差异每个'Article','Quarter',并除以组的长度-1。

    library(dplyr)
    df %>%
       group_by(Article, Quarter) %>%
       mutate(average_sales = (sum(Sales)- Sales)/(n()-1))
    # A tibble: 18 x 5
    # Groups:   Article, Quarter [6]
    #   Article Quarter Period Sales average_sales
    #     <int>   <int>  <int> <int>         <dbl>
    # 1       1       1      1    14          14.5
    # 2       1       1      2    10          16.5
    # 3       1       1      3    19          12  
    # 4       1       2      1    19          12  
    # 5       1       2      2    11          16  
    # 6       1       2      3    13          15  
    # 7       2       1      1    12          16.5
    # 8       2       1      2    20          12.5
    # 9       2       1      3    13          16  
    #10       2       2      1    17          15  
    #11       2       2      2    19          14  
    #12       2       2      3    11          18  
    #13       3       1      1    11          11.5
    #14       3       1      2    12          11  
    #15       3       1      3    11          11.5
    #16       3       2      1    12          16  
    #17       3       2      2    12          16  
    #18       3       2      3    20          12  
    

    数据

    df <- structure(list(Article = c(1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 
    2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L), Quarter = c(1L, 1L, 1L, 
    2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L), 
        Period = c(1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 
        1L, 2L, 3L, 1L, 2L, 3L), Sales = c(14L, 10L, 19L, 19L, 11L, 
        13L, 12L, 20L, 13L, 17L, 19L, 11L, 11L, 12L, 11L, 12L, 12L, 
        20L)), row.names = c(NA, -18L), class = "data.frame")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-05
      • 2016-11-29
      • 2021-05-31
      • 1970-01-01
      • 2021-10-24
      • 1970-01-01
      • 2013-10-25
      相关资源
      最近更新 更多