首页 > 解决方案 > R语言:处理数据框中数据的函数

问题描述

场景是有一个函数从数据框中的列中获取值,进行一些处理并用结果填充另一列。

这是用于测试的简化版本:

#the function checks if the value in inVar1 exists.
#if it does then it returns the value, if not it returns -100
#inRow: for debugging
test2 <- function (inVar1,inRow)
 {
  #debug
  print(paste("Row=",inRow, " inVar1=", inVar1, sep=''))

  if(is.na(inVar1) || is.null(inVar1))
   {
    #debug
    print("position 1")

    ret <- -100
   }
  else
   {
    #debug
    print("position 2")

    ret <- inVar1
   }

  #debug
  print("position 3")

  return(ret)
 }

简单的功能测试:

> a <- test2(7,1)
[1] "Row=1 inVar1=7"
[1] "position 2"
[1] "position 3"
> print(a)
[1] 7
> 
> a <- test2(NA,1)
[1] "Row=1 inVar1=NA"
[1] "position 1"
[1] "position 3"
> print(a)
[1] -100

它按预期工作。

现在让我们创建一个数据框

> d1 <- data.frame(rowID=c(1,2,3), var1=c(2,NA,5))
> print(d1)
  rowID var1
1     1    2
2     2   NA
3     3    5

让我们测试从数据框中传递值的函数:

> a <- test2(d1[1,2],d1[1,1])
[1] "Row=1 inVar1=2"
[1] "position 2"
[1] "position 3"
> print(a)               
[1] 2
> a <- test2(d1[2,2],d1[2,1])
[1] "Row=2 inVar1=NA"
[1] "position 1"
[1] "position 3"
> print(a)               
[1] -100
> a <- test2(d1[3,2],d1[3,1])
[1] "Row=3 inVar1=5"
[1] "position 2"
[1] "position 3"
> print(a)
[1] 5

同样,它按预期工作。

现在,最后一件事。我想添加一个包含已处理值的新列。

d1$var2 <- test2(d1$var1,d1$rowID)
print(d1)

这会产生以下输出:

> d1$var2 <- test2(d1$var1,d1$rowID)
[1] "Row=1 inVar1=2"  "Row=2 inVar1=NA" "Row=3 inVar1=5" 
[1] "position 2"
[1] "position 3"
> print(d1)
  rowID var1 var2
1     1    2    2
2     2   NA   NA
3     3    5    5

第 1 行和第 3 行中 var2 的值按预期计算,但在第 2 行中它是 NA 而不是预期的 -100。

我做错了什么?

我不明白的另一件事是为什么我们只看到一次调试消息,而不是根据行数,即三次?

谢谢!

标签: r

解决方案


您的函数未矢量化。您通过 1 对数字 3 次,每行一个,这是正确的,而您的最终测试通过 1 对向量一次;每个变量有 1 个向量。为了使您的功能正常工作,您需要一次喂它一对。mapply 会为您做到这一点。

d1$var2 <- mapply(FUN = test2,inVar1 = d1$var1,inRow = d1$rowID)

但总的来说,您可能需要在考虑矢量化的情况下重写您的函数。

# "vectorised in the sense that it can operate on entire vectors at once"
test2vectbasic <- function(inVar1,inRow){
  # using mapply, but could be a basic for loop too
  mapply(FUN = test2,inVar1 = inVar1,inRow = inRow)
}

# efficient R vectorization that uses built in low level language loops
# using "existing R functions that are already vectorised"
test2vectbetter <- function(inVar1){
  ifelse(is.na(inVar1) | is.null(inVar1),-100,inVar1)
}

# sample data
d1 <- data.frame(rowID=c(1,2,3), var1=c(2,NA,5))

# mapply way
d1$var2 <- mapply(FUN = test2,inVar1 = d1$var1,inRow = d1$rowID)

# basic way on atoms or vectors
test2vectbasic(d1[2,2],d1[2,1])
d1$var3 <- test2vectbasic(d1$var1,d1$rowID)

# efficient way
test2vectbetter(d1[2,2],d1[2,1])
d1$var4 <- test2vectbetter(d1$var1)

要获得您想要的功能,您至少需要基本的矢量化。关于矢量化的一些资源:

如何在 r 中定义矢量化函数

R inferno - 进入地狱的第三圈 - 无法矢量化


推荐阅读