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