首页 > 解决方案 > 麻烦矢量化 UDF 以允许 mutate() 更改数据帧

问题描述

我正在尝试编写一个可以与 dplyr 和 mutate 一起使用的函数。在我的真实示例中,我想传递两列并返回一个值,该值使用基于每列的 ifelse 语句来决定要执行的计算。

在看到 if 语句没有向量化之后,我尝试使用 ifthen,因为我认为它会自动向量化。我仍然有问题,并且已经将事情减少到现在相当于查找的内容。我有 2 个 skus。每个项目都有一个重量。我希望重量小于 5 磅的物品是小件,而其他物品是大件

library(dplyr)

x<- data.frame(Sku = c(9, 12), Lbs = c(9, 2))
> x
  Sku Lbs
1   9   9
2  12   2

SizeCalc <- function(Wt) 
{ ifelse (Wt <= 5, 
          Size <- "Small",
          Size <- "Big")
  return (Size)
}

第一项是大,第二项是小。但是,如果我在完整的数据帧上运行它,它认为这两个项目都很大。如果我只发送第二个项目,它就知道它很小。

> mutate(x[1:2,], Size = SizeCalc(Lbs))
  Sku Lbs Size
1   9   9  Big
2  12   2  Big
> mutate(x[2:2,], Size = SizeCalc(Lbs))
  Sku Lbs  Size
1  12   2 Small

如果我明确地对函数进行矢量化,它会起作用:

> SizeCalc_v <- Vectorize(SizeCalc)
> mutate(x[1:2,], Size = SizeCalc_v(Lbs))
  Sku Lbs  Size
1   9   9   Big
2  12   2 Small

我是否需要始终对要与 mutate() 一起使用的函数进行矢量化,或者我还遗漏了其他东西?

看到评论后,我正在详细说明。我的实际函数嵌套了 ifelse,它们给出了计算中使用的结果,所以我不能只返回 ifelse 的结果。这是实际的功能。以 YYYYMM 格式输入 First 和 Last 以及日期。我正在计算日期之间的“半年”数,但 Jan 算作上一年的年底。我有一个类似的问题,结果取决于我发送的原始数据帧的数量。

Delta <- function(First, Last) 
{ ifelse (First%%100 <= 6, 
          F <- 2*(First%/%100) + 1, # if in 1st half of year add 1
          F <- 2*(First%/%100) + 2) # if in 2nd half of year add 2
  ifelse (Last%%100 >= 7, 
          L <- 2*(Last%/%100) + 2,  # if in 2nd half of year add 2
          ifelse (Last%%100 >= 2,
                  L <- 2*(Last%/%100) + 1, # if in Feb-Jun 1
                  L <- 2*(Last%/%100)))    # if in Jan, treat as previous year
  return (L-F)
}

标签: rvectorizationuser-defined-functionsdplyr

解决方案


您定义的SizeCalc功能不正确。无需为Size内部的变量 ()赋值ifelse

ifelse是矢量化的,将您的功能更改为

SizeCalc <- function(Wt) ifelse(Wt <= 5, "Small","Big")

现在如果我们使用mutate,它会按预期工作。

library(dplyr)
mutate(x, Size = SizeCalc(Lbs))

#  Sku Lbs  Size
#1   9   9   Big
#2  12   2 Small

更多的dplyr方法是使用链接

x %>% mutate(Size = SizeCalc(Lbs))

要使用您当前的功能进行更多调试,如果您这样做,正在发生的事情是

ifelse(c(9, 2) <= 5, Size <- "Small", Size <- "Big")

如果你现在检查Size它拥有的对象

Size
#[1] "Big"

并且这个值是return从你的函数中编辑的。

相反,你想要的是

ifelse(c(9, 2) <= 5, Size <- "Small", Size <- "Big")
#[1] "Big"   "Small"

推荐阅读