如何使一个R函数返回多列并将它们附加到数据框中?

6

从这个数据框开始

myDF = structure(list(Value = c(-2, -1, 0, 1, 2)), .Names = "Value", row.names = c(NA, 5L), class = "data.frame")

假设我想在myDF$Value的每一行上运行这个函数
getNumberInfo <- function(x) {
if(x %% 2 ==0) evenness = "Even" else evenness="Odd"
if(x > 0) positivity = "Positive" else positivity = "NonPositive"
if (positivity == "Positive") logX = log(x) else logX=NA
c(evenness,positivity,logX)
} 

...获取这个数据框

structure(list(Value = c(-2, -1, 0, 1, 2), Evenness = c("Even", 
"Odd", "Even", "Odd", "Even"), Positivity = c("NonPositive", 
"NonPositive", "NonPositive", "Positive", "Positive"), Log = c(NA, 
NA, NA, "0", "0.693147180559945")), row.names = c(NA, 5L), .Names = c("Value", 
"Evenness", "Positivity", "Log"), class = "data.frame")
3个回答

8

您可能希望将getNumberInfo函数更改为返回列表而不是向量,以便值可以具有不同的类型。目前,它们都被强制转换为字符串,这可能不适用于logX

getNumberInfo <- function(x) {
  if(x %% 2 ==0) evenness = "Even" else evenness="Odd"
  if(x > 0) positivity = "Positive" else positivity = "NonPositive"
  if (positivity == "Positive") logX = log(x) else logX=NA
  list(evenness,positivity,logX)
}

此外,您可以更好地利用名称,以便不必重复使用它们:
getNumberInfo <- function(x) {
  list(evenness = if(x %% 2 ==0) "Even" else "Odd",
       positivity = if(x > 0) "Positive" else "NonPositive",
       logX = if(x > 0) log(x) else NA)
}

那么解决方案就变得简单了:
> cbind(myDF, t(sapply(myDF$Value, getNumberInfo)))
  Value evenness  positivity      logX
1    -2     Even NonPositive        NA
2    -1      Odd NonPositive        NA
3     0     Even NonPositive        NA
4     1      Odd    Positive         0
5     2     Even    Positive 0.6931472

最后,如果你使用ifelse(可以在向量上工作)而不是if,它会变得更简单,因为你不必调用apply

getNumberInfo <- function(x) {
  list(evenness = ifelse(x %% 2 ==0, "Even", "Odd"),
       positivity = ifelse(x > 0, "Positive", "NonPositive"),
       logX = ifelse(x > 0, log(x), NA))
}

> cbind(myDF, getNumberInfo(myDF$Value))
  Value evenness  positivity      logX
1    -2     Even NonPositive        NA
2    -1      Odd NonPositive        NA
3     0     Even NonPositive        NA
4     1      Odd    Positive 0.0000000
5     2     Even    Positive 0.6931472

那个最后的解决方案会发出一个警告,因为它实际上计算了每个元素的对数,而不仅仅是那些 x>0 的元素。不确定处理这个问题的最优雅的方法是什么。

t(sapply) tapply相比有什么不同?我不知道用t()包装某些东西的作用是什么。我从未见过它的文档说明。 - Jim G.
t()函数进行转置操作(例如,将行变为列,或将列变为行)。 - John

3
如何:
 out <- cbind(myDF, t(apply(myDF, 1, getNumberInfo)))
 colnames(out) <- c('Value', 'Evenness', 'Positivity', 'Log')

这将为您提供以下内容:

  值   奇偶性    正负性               对数
1  -2     偶数    非正数              NA
2  -1     奇数    非正数              NA
3   0     偶数    非正数              NA
4   1     奇数    正数                  0
5   2     偶数    正数   0.693147180559945


t(apply) tapply相比有何区别? 我不知道使用t()包装东西的作用。我从未见过它的文档记录。 - Jim G.

3
另外一个选择:
> library(plyr)
> df <- mdply(myDF, getNumberInfo)
> names(df) <- c('Value', 'Evenness', 'Positivity', 'Log')
> df
  Value Evenness  Positivity       Log
1    -2     Even NonPositive        NA
2    -1      Odd NonPositive        NA
3     0     Even NonPositive        NA
4     1      Odd    Positive 0.0000000
5     2     Even    Positive 0.6931472

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接