2009-09-08 12 views
5

A partir de esta trama de datos¿Cómo hacer que una función R devuelva varias columnas y las agregue a un marco de datos?

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

Supongamos que quiero ejecutar esta función en cada fila de myDF $ Valor

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) 
} 

... para obtener esta trama de datos

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") 

Respuesta

8

Es posible que desee cambiar su función getNumberInfo para devolver una lista en lugar de un vector, por lo que los valores pueden tener diferentes tipos. Tal como están las cosas, están siendo enviadas a cadenas, lo que probablemente no es lo que quieres para 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) 
} 

Además, puede utilizar los nombres de un efecto algo mejor para que usted no tiene que repetirlos:

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) 
} 

A continuación, la solución se vuelve simple:

> 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 

Finalmente, si usa ifelse (que puede funcionar en vectores) en lugar de if, se simplifica aún más porque no tiene que llamar al 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 

Esa última solución emite una advertencia, ya que en realidad está calculando el registro de cada elemento, no sólo aquellos con x>0. No estoy seguro de la forma más elegante de lidiar con eso.

+0

¿Qué significa 't (sapply'do en vez de' tapply'?) No sé lo que hace wrapping algo con 't()'. Nunca lo he visto documentado. –

3

¿Qué tal:

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

que le ofrece:

 
    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 

+0

¿Qué 't (apply'do en lugar de sólo' tapply' no sé qué envolver algo con 't()' lo hace nunca he visto?. documentó. –

3

Otra alternativa:

> 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 
Cuestiones relacionadas