Alex Rivera | Logout

use multiple columns as variables with sapply

Asked 2012-04-09T18:56:15.590
37

I have a dataframe and I would like to apply a function that takes the values of three columns and computes the minimum difference between the three values.

#dataset
df <- data.frame(a= sample(1:100, 10),b = sample(1:100, 10),c= sample(1:100, 10))

#function
minimum_distance <- function(a,b,c)
{
  dist1 <- abs(a-b)
  dist2 <- abs(a-c)
  dist3 <- abs(b-c)
  return(min(dist1,dist2,dist3))
}

I am looking for something like:

df$distance <- sapply(df, function(x) minimum_distance(x$a,x$b,x$c) )
## errormessage
Error in x$a : $ operator is invalid for atomic vectors

While I can use ddply:

df2 <- ddply(df,.(a),function(r) {data.frame(min_distance=minimum_distance(r$a,r$b, r$c))}, .drop=FALSE)

This doesn't keep all of the columns. Any suggestions?

Edit: I ended up using:

df$distance <- mapply(minimum_distance, df$a, df$b, df$c)
Edit
Report

1 Answer

6

I know this has been answered but I'd actually take a different approach that takes any number of columns and is more generalizable using an outer approach:

vdiff <- function(x){
    y <- outer(x, x, "-")
    min(abs(y[lower.tri(y)]))
}

apply(df, 1, vdiff)

I think this is a little cleaner and flexible.

EDIT: Per zach's comments I propose this more formalized function that works on data frames with non numeric columns as well by removing them and acting only on the numeric columns.

cdif <- function(dataframe){
    df <- dataframe[, sapply(dataframe, is.numeric)]
    vdiff <- function(x){
        y <- outer(x, x, "-")
        min(abs(y[lower.tri(y)]))
    }
    return(apply(df, 1, vdiff))
}

#TEST it out
set.seed(10)
(df <- data.frame(a = sample(1:100, 10), b = sample(1:100, 10), 
    c = sample(1:100, 10), d =  LETTERS[1:10]))

cdif(df)
answered 2012-04-09T21:37:17.910

Your Answer