web-dev-qa-db-fra.com

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

Bien que je puisse 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. Aucune suggestion?

Edit: j'ai fini par utiliser:

df$distance <- mapply(minimum_distance, df$a, df$b, df$c)
29
zach

Essayez mapply ():

qq <- mapply(minimum_distance, df$a, df$b, df$c)
47
geoffjentry

essaye ça:

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

mais vous pouvez écrire une 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
kohske

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: Selon les commentaires de Per Zach, je propose cette fonction plus formelle qui fonctionne également sur les blocs 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
Tyler Rinker

Il vaut mieux écrire une fonction puis 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)
1
Shalini Baranwal