Как заставить функцию R вернуть несколько столбцов и добавить их в кадр данных?

StackOverflow https://stackoverflow.com/questions/1395156

  •  21-09-2019
  •  | 
  •  

Вопрос

Начиная с этой кадры данных

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

Предположим, я хочу запустить эту функцию в каждой строке стоимости mydf $

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")
Это было полезно?

Решение

Вы можете изменить свой 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. Анкет Не уверен, что самый элегантный способ справиться с этим.

Другие советы

Как насчет:

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

Что дает вам:

  Value Evenness  Positivity               Log
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.693147180559945

Другая альтернатива:

> 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top