Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make an R function return multiple columns and append them to a data frame?

Starting with this data frame

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

Suppose I want to run this function on every row of 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)
} 

... to get this data frame

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")
like image 754
Dan Goldstein Avatar asked Sep 08 '09 17:09

Dan Goldstein


People also ask

How do I append multiple columns in R?

How do I concatenate two columns in R? To concatenate two columns you can use the <code>paste()</code> function. For example, if you want to combine the two columns A and B in the dataframe df you can use the following code: <code>df['AB'] <- paste(df$A, df$B)</code>.

How do I add a list of columns to a Dataframe in R?

You can add new columns to a dataframe using the $ and assignment <- operators. To do this, just use the df$name notation and assign a new vector of data to it.

Can R function return multiple values?

1 Answer. In R programming, functions do not return multiple values, however, you can create a list that contains multiple objects that you want a function to return.


1 Answers

You might want to change your getNumberInfo function to return a list rather than a vector, so that the values can have different types. As it is, they're all being cast to strings, which probably isn't what you want for 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)
}

Furthermore, you can use the names to a somewhat better effect so that you don't have to repeat them:

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

Then the solution becomes 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

Finally, if you use ifelse (which can work on vectors) instead of if, it gets even simpler because you don't have to call 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

That last solution emits a warning, because it's actually computing the log of every element, not just those with x>0. Not sure the most elegant way to deal with that.

like image 176
Ken Williams Avatar answered Oct 14 '22 21:10

Ken Williams