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)
4 réponses
Essayez mapply ():
qq <- mapply(minimum_distance, df$a, df$b, df$c)
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)
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)
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)