如何在 r 中编写一个函数来对记录进行计算?
How do I write a function in r to do cacluations on a record?
在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)
我的函数定义为
FEType <- function(v ){
ret="Low"
if (v["FuelType"]=='G') {
ret ="High"
}
return(ret)
}
这和我预期的不一样
当我检查 v 时,我发现它包含总计而不是我预期的当前行。
我哪里错了?
在问题中,我在最后一段看到了一些提示。
要重现问题,说明我想做什么,我有
IsPretty <-function(PetalWidth){
if (PetalWidth >0.3) return("Y")
return("N")
}
df <- iris
df$Pretty = IsPretty(df$Petal.Width)
这给出了错误
the condition has length > 1 and only the first element will be used
这让我开始研究向量。但我不确定这是正确的方向。
[更新]
我习惯于考虑表格和当前记录。
因此我在想
df$Pretty = IsPretty(df$Petal.Width)
会使用计算出的 isPretty 属性
向我的数据框添加一列
为什么我不能在计算中包含 if 条件?
矢量化是您在 R 中需要习惯的最基本(也是不寻常)的事情之一。许多(大多数?)R 操作都是矢量化的。但有些事情不是 - if(){}else{}
是 non-vectorized 事情之一。它用于控制流(无论是否 运行 一个代码块)而不是向量操作。 ifelse()
是一个单独的函数,用于向量,其中第一个参数是“测试”,第二个和第三个参数是“如果是”和“如果不是”的结果。测试是一个向量,returned 值是测试中每个项目的适当 yes/no 结果。 结果将与测试的长度相同。
所以我们会像这样编写您的 IsPretty
函数:
IsPretty <- function(PetalWidth){
return(ifelse(PetalWidth > 0.3, "Y", "N"))
}
df <- iris
df$Pretty = IsPretty(df$Petal.Width)
与测试条件长度为 1 的 if(){...}else{...}
块对比,任意代码可以是 ...
中的 运行 - 可能 return 更大的结果比测试,或更小的结果,或没有结果 - 可能会修改其他对象......你可以在 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])
}
下面的基准测试表明矢量化版本的速度提高了 50 倍。这是一个如此简单的案例和如此小的数据,这无关紧要,但在更大的数据或更复杂的操作中,向量化代码和 non-vectorized 代码之间的区别可能是几分钟与几天。
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
这是 R 学习者的常见问题 - 您可以在 Stack Overflow 上找到许多问题,其中人们在需要 ifelse()
时使用 if(){}else{}
,反之亦然。 Why can't ifelse
return vectors? 是来自问题对面的FAQ。
你的尝试发生了什么?
df <- iris
## The condition has length equal to the number of rows in the data frame
df$Petal.Width > 0.3
#> [1] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
#> [13] FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE TRUE
## ... truncated
## R warns us that only the first value (which happens to be FALSE) is used
result = if(df$Petal.Width > 0.3) {"Y"} else {"N"}
#> Warning in if (df$Petal.Width > 0.3) {: the condition has length > 1 and only
#> the first element will be used
## So the result is a single "N"
result
#> [1] "N"
length(result)
#> [1] 1
## R "recycles" inputs that are of insufficient length
## so we get a full column of "N"
df$Pretty = result
head(df)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species Pretty
#> 1 5.1 3.5 1.4 0.2 setosa N
#> 2 4.9 3.0 1.4 0.2 setosa N
#> 3 4.7 3.2 1.3 0.2 setosa N
#> 4 4.6 3.1 1.5 0.2 setosa N
#> 5 5.0 3.6 1.4 0.2 setosa N
#> 6 5.4 3.9 1.7 0.4 setosa N
由 reprex package (v0.3.0)
于 2020-11-08 创建
我自己对 Gregor 的回答做的笔记
IsPrettyIf <-function(row){
ret ="N"
if(row$Petal.Width > 0.3) { ret="Y"}
return(ret)
}
df <- iris
df$PrettyLoop ="" # add a column and initialize all the cells to be empty
for(i in 1:5) {
df$PrettyLoop[i] = IsPrettyIf(df[i,])
cat("Row",i, "is Pretty?",df$PrettyLoop[i],"\n")
}
让我感到困惑的一点是 row$PrettyLoop 就像一个单元格,而 df$PrettyLoop 就像一个列,用电子表格类比来思考。
在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)
我的函数定义为
FEType <- function(v ){
ret="Low"
if (v["FuelType"]=='G') {
ret ="High"
}
return(ret)
}
这和我预期的不一样 当我检查 v 时,我发现它包含总计而不是我预期的当前行。
我哪里错了?
在问题
要重现问题,说明我想做什么,我有
IsPretty <-function(PetalWidth){
if (PetalWidth >0.3) return("Y")
return("N")
}
df <- iris
df$Pretty = IsPretty(df$Petal.Width)
这给出了错误
the condition has length > 1 and only the first element will be used
这让我开始研究向量。但我不确定这是正确的方向。
[更新]
我习惯于考虑表格和当前记录。 因此我在想
df$Pretty = IsPretty(df$Petal.Width)
会使用计算出的 isPretty 属性
向我的数据框添加一列为什么我不能在计算中包含 if 条件?
矢量化是您在 R 中需要习惯的最基本(也是不寻常)的事情之一。许多(大多数?)R 操作都是矢量化的。但有些事情不是 - if(){}else{}
是 non-vectorized 事情之一。它用于控制流(无论是否 运行 一个代码块)而不是向量操作。 ifelse()
是一个单独的函数,用于向量,其中第一个参数是“测试”,第二个和第三个参数是“如果是”和“如果不是”的结果。测试是一个向量,returned 值是测试中每个项目的适当 yes/no 结果。 结果将与测试的长度相同。
所以我们会像这样编写您的 IsPretty
函数:
IsPretty <- function(PetalWidth){
return(ifelse(PetalWidth > 0.3, "Y", "N"))
}
df <- iris
df$Pretty = IsPretty(df$Petal.Width)
与测试条件长度为 1 的 if(){...}else{...}
块对比,任意代码可以是 ...
中的 运行 - 可能 return 更大的结果比测试,或更小的结果,或没有结果 - 可能会修改其他对象......你可以在 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])
}
下面的基准测试表明矢量化版本的速度提高了 50 倍。这是一个如此简单的案例和如此小的数据,这无关紧要,但在更大的数据或更复杂的操作中,向量化代码和 non-vectorized 代码之间的区别可能是几分钟与几天。
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
这是 R 学习者的常见问题 - 您可以在 Stack Overflow 上找到许多问题,其中人们在需要 ifelse()
时使用 if(){}else{}
,反之亦然。 Why can't ifelse
return vectors? 是来自问题对面的FAQ。
你的尝试发生了什么?
df <- iris
## The condition has length equal to the number of rows in the data frame
df$Petal.Width > 0.3
#> [1] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
#> [13] FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE TRUE
## ... truncated
## R warns us that only the first value (which happens to be FALSE) is used
result = if(df$Petal.Width > 0.3) {"Y"} else {"N"}
#> Warning in if (df$Petal.Width > 0.3) {: the condition has length > 1 and only
#> the first element will be used
## So the result is a single "N"
result
#> [1] "N"
length(result)
#> [1] 1
## R "recycles" inputs that are of insufficient length
## so we get a full column of "N"
df$Pretty = result
head(df)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species Pretty
#> 1 5.1 3.5 1.4 0.2 setosa N
#> 2 4.9 3.0 1.4 0.2 setosa N
#> 3 4.7 3.2 1.3 0.2 setosa N
#> 4 4.6 3.1 1.5 0.2 setosa N
#> 5 5.0 3.6 1.4 0.2 setosa N
#> 6 5.4 3.9 1.7 0.4 setosa N
由 reprex package (v0.3.0)
于 2020-11-08 创建我自己对 Gregor 的回答做的笔记
IsPrettyIf <-function(row){
ret ="N"
if(row$Petal.Width > 0.3) { ret="Y"}
return(ret)
}
df <- iris
df$PrettyLoop ="" # add a column and initialize all the cells to be empty
for(i in 1:5) {
df$PrettyLoop[i] = IsPrettyIf(df[i,])
cat("Row",i, "is Pretty?",df$PrettyLoop[i],"\n")
}
让我感到困惑的一点是 row$PrettyLoop 就像一个单元格,而 df$PrettyLoop 就像一个列,用电子表格类比来思考。