如何在 r 中编写一个函数来对记录进行计算?

Kir*_*eed 2 if-statement r vectorization

在 C# 中,我习惯了数据集和当前记录的概念。对我来说,用当前记录的条件编写一个复杂的计算价格函数会很容易。

我无法理解如何在 r 中执行此操作。

我尝试了以下

   train <- read.csv("Train.csv" )
   df <- as.data.frame.matrix(train)
   v = c(  df$Fuel.Type ,df$No.Gears)
   names(v ) <- c( "FuelType" ,"NoGears")
   df$FEType = FEType( v)
Run Code Online (Sandbox Code Playgroud)

其中 my 函数定义为

FEType <- function(v    ){
  ret="Low"
  if (v["FuelType"]=='G') {
    ret ="High"
  }
  return(ret)
}
Run Code Online (Sandbox Code Playgroud)

这不像我预期的那样工作,当我检查 v 时,我看到它包含汇总总数而不是我预期的当前行。

我哪里错了?

这里的问题中我在最后一段中看到了一些提示。

为了重现问题,表明我想做什么,我有

IsPretty <-function(PetalWidth){
  if (PetalWidth  >0.3) return("Y")
  return("N")
}

df <- iris
df$Pretty = IsPretty(df$Petal.Width)
    
Run Code Online (Sandbox Code Playgroud)

这给出了错误

条件的长度 > 1 并且只使用第一个元素

这让我开始研究向量。但我不相信这是正确的方向。

[更新]

我习惯于考虑表格和当前记录。因此我在想

df$Pretty = IsPretty(df$Petal.Width)
Run Code Online (Sandbox Code Playgroud)

将具有使用计算出的 isPretty 属性向我的数据框中添加一列的效果

为什么我的计算中不能包含 if 条件?

Gre*_*gor 5

矢量化是您需要在 R 中习惯的最基本(也是不寻常)的事情之一。许多(大多数?)R 操作都是矢量化的。但有些事情不是 - 并且if(){}else{}是非矢量化的事情之一。它用于控制流(是否运行代码块)而不是向量操作。ifelse()是一个单独的函数,用于向量,其中第一个参数是“测试”,第二和第三个参数是“如果是”和“如果不是”结果。测试是一个向量,返回值是测试中每个项目的适当是/否结果。结果将与 test 的长度相同

所以我们会IsPretty像这样编写你的函数:

IsPretty <- function(PetalWidth){
  return(ifelse(PetalWidth > 0.3, "Y", "N"))
}

df <- iris
df$Pretty = IsPretty(df$Petal.Width)
Run Code Online (Sandbox Code Playgroud)

if(){...}else{...}测试条件长度为 1的块相比,可以在其中运行任意代码...- 可能返回比测试更大的结果,或者更小的结果,或者没有结果 - 可能会修改其他对象......你可以这样做里面的任何东西if(){}else(),但测试条件的长度必须为 1。

您可以IsPretty一次一行地使用您的函数 - 它适用于任何一行。所以我们可以把它放在一个循环中,如下所示,一次检查一行,一次进行if()一项测试,一次分配一个结果。但是 R 针对矢量化进行了优化,这会明显变慢并且是一个坏习惯。

IsPrettyIf <-function(PetalWidth){
  if (PetalWidth  >0.3) return("Y")
  return("N")
}

for(i in 1:nrow(df)) {
  df$PrettyLoop[i] = IsPrettyIf(df$Petal.Width[i])
}
Run Code Online (Sandbox Code Playgroud)

下面的基准测试表明矢量化版本的速度提高了 50 倍。这是一个如此简单的案例和如此小的数据,这无关紧要,但对于更大的数据,或更复杂的操作,矢量化和非矢量化代码之间的差异可能是几分钟而不是几天。

microbenchmark::microbenchmark(
  loop = {
    for(i in 1:nrow(df)) {
      df$PrettyLoop[i] = IsPrettyIf(df$Petal.Width[i])
    }
  },
  vectorized = {
    df$Pretty = IsPretty(df$Petal.Width)    
  }
)
Unit: microseconds
       expr    min     lq     mean median      uq     max neval
       loop 3898.9 4365.6 5880.623 5442.3 7041.10 11344.6   100
 vectorized   47.7   59.6  112.288   67.4   83.85  1819.4   100
Run Code Online (Sandbox Code Playgroud)

这是 R 学习者的一个常见问题 - 您可以在 Stack Overflow 上找到许多问题,人们if(){}else{}在需要时使用这些问题,ifelse()反之亦然。为什么不能ifelse返回向量?是来自问题对立面的常见问题解答。


你的尝试发生了什么?

IsPretty <- function(PetalWidth){
  return(ifelse(PetalWidth > 0.3, "Y", "N"))
}

df <- iris
df$Pretty = IsPretty(df$Petal.Width)
Run Code Online (Sandbox Code Playgroud)

reprex 包(v0.3.0)于 2020 年 11 月 8 日创建