utiliser plusieurs colonnes comme variables avec sapply

J'ai un dataframe et je voudrais appliquer une fonction qui prend les valeurs de trois colonnes et calcule la différence minimale entre les trois valeurs.

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

Je cherche quelque chose comme:

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

Alors que je peux utiliser ddply:

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

Cela ne conserve pas toutes les colonnes. Toutes les suggestions?

Edit: j'ai fini par utiliser:

df$distance <- mapply(minimum_distance, df$a, df$b, df$c)
25
demandé sur Abel 2012-04-09 22:56:15

4 réponses

Essayez mapply ():

qq <- mapply(minimum_distance, df$a, df$b, df$c)
41
répondu geoffjentry 2012-04-09 19:02:59

Essayez ceci:

do.call("mapply", c(list(minimum_distance), df))

Mais vous pouvez écrire la version vectorisée:

pminimum_distance <- function(a,b,c)
{
 dist1 <- abs(a-b)
 dist2 <- abs(a-c)
 dist3 <- abs(b-c)
 return(pmin(dist1,dist2,dist3))
}
pminimum_distance(df$a, df$b, df$c)

# or
do.call("pminimum_distance", df)
6
répondu kohske 2012-04-09 19:06:28

Je sais que cela a été répondu mais je prendrais en fait une approche différente qui prend n'importe quel nombre de colonnes et est plus généralisable en utilisant une approche externe:

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

apply(df, 1, vdiff)

Je pense que c'est un peu plus propre et flexible.

EDIT: par les commentaires de zach, je propose cette fonction plus formalisée qui fonctionne également sur les trames de données avec des colonnes non numériques en les supprimant et en agissant uniquement sur les colonnes numériques.

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)
5
répondu Tyler Rinker 2012-04-09 22:07:50

Il vaut mieux écrire une fonction et ensuite utiliser mapply sur les vecteurs:

 f1 <- function(a,b,c){
 d =abs(a-b)
 e =abs(b-c)
 f= abs(c-a)
 return(pmin(d,e,f))
 }

 qq <- mapply(f1, df$a, df$b, df$c)
0
répondu Shalini Baranwal 2016-07-28 04:38:29