faire un tri en gardant l'information de nom de colonne

Postez ici vos questions, réponses, commentaires ou suggestions - Les sujets seront ultérieurement répartis dans les archives par les modérateurs

Modérateur : Groupe des modérateurs

Guillaume Dramais
Messages : 39
Enregistré le : 25 Sep 2018, 00:04

faire un tri en gardant l'information de nom de colonne

Messagepar Guillaume Dramais » 22 Oct 2018, 23:39

Bonjour,
J'ai deux dataframes,

Code : Tout sélectionner

#j'ai deux dataframes avec des distances et depths en metre

data1 <- data.frame(distance <- rbind(0,2,4,5,7,9,12),
                    depth <-rbind(1.2,2.9,5.8,6.9,4.3,3.1,0.5),
                    valeur <- rbind(14,23,17,45,26,59,22)
                    )
colnames(data1)<- c("distance","depth","valeur")
data1

samples <- data.frame(distance2 <- rbind(0,1,6,6,8),
                      depth2 <- rbind(0,1.3,2.5,4,2),
                      conc <- rbind(0.12, 0.10,0.9, 0.7,0.5)
                      )

rownames(samples)=c("V0","V1-1","V2-1","V2-2","V3")
samples


Je mesure la distance entre chaque echantillon "samples" et chaque valeur de data1avec cette boucle

Code : Tout sélectionner

out <- matrix(nrow=7,ncol=5) #Le résultat de ma boucle


for (i in 1:nrow(data1)){

  for (k in 1:nrow(samples)){ # for each sample
       ddistance = samples[k, 1] - distance[i] # distance entre chaque position et la position de mon sample
       ddepth    = samples[k, 2] - depth[i] # depth entre chaque position et la position de mon sample
   
        d = sqrt(ddistance^2+ddepth^2) # distance en metre entre chaque sample et chaque valeur de data1 
        out[i,k]=d   
   
                }
                            }

colnames(out)=c("V0","V1-1","V2-1","V2-2","V3")
out


Pour chaque valeur de mon data1 je cherche la valeur minimale de distance aux samples (l'échantillon le plus proche)
Je le récupère avec cette boucle mais je souhaiterais garder l'information du nom de la colonne.

Code : Tout sélectionner


out2 <- matrix(NA, nrow = nrow(out)) # taille du fichier de sortie

for (l in 1:nrow(out)){ # pour chaque ligne de out
 
  minimumd=min(out[l,])  #je vais chercher le minimum
     
  out2[l,]=minimumd # je l'ecris dans le fichier de sortie
}

out2



en fait je voudrais un resultat de ce type :
    1,004988 V0
    1,886796 V1-1
    2,690725 V2-2
    3,067572 V2-2
    1,044031 V2-2
    1,486607 V3
    4,272002 V3

J'obtiens bien ma distance minimale mais je voudrais garder l'information du nom de colonne. Il est sans doute possible de tout faire plus rapidement/simplement.
Si vous avez des suggestions pour m'aider à avancer et associer ce nom de colonne.
merci d'avance

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: faire un tri en gardant l'information de nom de colonne

Messagepar Logez Maxime » 23 Oct 2018, 07:52

Bonjour,

Pour être plus efficace dans tes codes évite les doubles boucles.
Ici tu peux très bien faire le calcul de toutes les distances entre un échantillon et les points de data1 en une seule fois, en passant par des matrices et en vectorisant les calculs. Il faut savoir que les données dans une matrice sont rangées en colonne et que quand tu fais des opérations mathématiques avec des objets de tailles différentes, l'objet le plus cours sera recyclé pour permettre le calcul.
Par exemple :

Code : Tout sélectionner

x <- 1:4
x
y <- 1:2
x/y
[1] 1 1 3 2
La première valeur de x est divisée par la première valeur de y, la deuxième valeur de x est divisée par la deuxième valeur de y, la troisième valeur de x est elle divisée par la première valeur de y car y sera recyclé du fait qu'il est plus petit que x.
Tu peux donc simplifier ton calcul de distance comme suit :

Code : Tout sélectionner

samples <- data.frame(distance2 = c(0, 1, 6, 6, 8),
  depth2 = c(0, 1.3, 2.5, 4, 2),
  conc = c(0.12, 0.10, 0.9, 0.7, 0.5))

samples2 <- as.matrix(samples)
data2 <- t(data1[,-3])

# samples2 et data2 sont des matrices
d1 <- (data2-samples2[1,])^2
# toute les distances entre les données de data2 et le 1er echantillon
d1
         [,1]   [,2]    [,3]  [,4]    [,5]    [,6]   [,7]
distance 0.00 3.5344 16.0000 25.00 47.3344 81.0000 144.00
depth    1.44 8.4100 32.2624 47.61 18.4900  8.8804   0.25
# il suffit de faire la somme des valeurs qui sont stockées en colonne :
colSums(d1)
[1]   1.4400  11.9444  48.2624  72.6100  65.8244  89.8804 144.2500
# puis d'en extraire la racine carrée
sqrt(colSums(d1))
# ce qui correspond à la première colonne de out
Il n'y plus qu'a bouclé sur les lignes de samples2.

Pour le reste :

Code : Tout sélectionner

nc <- integer(nrow(out))
for (i in seq_along(nc)) nc[i] <- which.min(out[i,])
# ou : nc <- apply(out, 1, which.min)
res <- data.frame(dist = out[cbind(seq_along(nc), nc)], col = colnames(out)[nc])
res
      dist  col
1 1.004988 V1-1
2 1.886796 V1-1
3 2.690725 V2-2
4 3.067572 V2-2
5 1.044031 V2-2
6 1.486607   V3
7 4.272002   V3

Cordialement,
Maxime

Pierre-Yves Berrard
Messages : 1029
Enregistré le : 12 Jan 2016, 23:30

Re: faire un tri en gardant l'information de nom de colonne

Messagepar Pierre-Yves Berrard » 23 Oct 2018, 07:54

Bonjour,

Une solution rapide à ajouter à votre code.

Code : Tout sélectionner

out3 <- data.frame(
  val_min = out2[,1],
  nom_min = colnames(out)[apply(out, 1, which.min)]
)

Il y a effectivement moyen de faire mieux pour la construction de out et out2.

edit : coiffé au poteau par Maxime
PY

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Re: faire un tri en gardant l'information de nom de colonne

Messagepar François Bonnot » 23 Oct 2018, 10:57

Bonjour,
Une solution utilisant la fonction dist() :

Code : Tout sélectionner

z <- rbind(as.matrix(data1),as.matrix(samples))
d <- as.matrix(dist(z[,1:2]))[1:nrow(data1),nrow(data1)+(1:nrow(samples))]
dmin <- apply(d,1,min)
positions <- apply(d,1,function(x) which(x==min(x))[1])
data.frame(min=dmin,sample=colnames(d)[positions])
François

Guillaume Dramais
Messages : 39
Enregistré le : 25 Sep 2018, 00:04

Re: faire un tri en gardant l'information de nom de colonne

Messagepar Guillaume Dramais » 23 Oct 2018, 16:13

Merci beaucoup de vos réponses rapides, j'essaie de décortiquer tout ça pour comprendre les étapes et fonctions, mais ça marche!

Guillaume Dramais
Messages : 39
Enregistré le : 25 Sep 2018, 00:04

Re: faire un tri en gardant l'information de nom de colonne

Messagepar Guillaume Dramais » 23 Oct 2018, 22:36

Re Bonjour,
J'ai essayé chacune des solutions, sur mon exemple simplifié ça marche, mais je n'arrive pas à les transposer à mes données.

La solution de François était séduisante mais j'ai trop de données (~20000 dans l'équivalent de mon data1) j'obtiens une erreur
Error: cannot allocate vector of size 1.4 Gb


La solution de Pierre-Yves semblait aussi assez simple à transposer mais je n'arrive pas à créer le dataframe de résultats
Error in data.frame(val_min = out8[, 1], nom_min = colnames(out7)[apply(out7, :
les arguments impliquent des nombres de lignes différents : 19375, 0


Et pour la solution de Maxime j'ai suivi les différentes étapes pour finaliser sur l'exemple simplifié mais je n'arrive pas à " boucler sur les lignes de samples2" Voilà la boucle :

Code : Tout sélectionner

#

out <- matrix(NA) #Le résultat de ma boucle

for (i in 1:nrow(samples2)){

          d1 <- (data2-samples2[i,])^2
     out=sqrt(colSums(d1))
  }

out


Je dois rater quelque chose...?
Merci encore pour votre aide
Cdlt

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Re: faire un tri en gardant l'information de nom de colonne

Messagepar François Bonnot » 24 Oct 2018, 07:37

Bonjour,
Si c'est une question de mémoire, on peut envisager une fonction dist2 ne nécessitant pas le calcul des distances inutiles :

Code : Tout sélectionner

dist2 <- function(df1,df2) {
    d2 <- function(a,b) (a-b)^2
    x2 <- outer(df1[[1]],df2[[1]],d2)
    y2 <- outer(df1[[2]],df2[[2]],d2)
    dist <- sqrt(x2+y2)
    colnames(dist) <- rownames(df2)
    dist
}

d <- dist2(data1,samples)
dmin <- apply(d,1,min)
positions <- apply(d,1,which.min)
data.frame(min=dmin,sample=colnames(d)[positions])

A tester sur les données complètes.
François

Pierre-Yves Berrard
Messages : 1029
Enregistré le : 12 Jan 2016, 23:30

Re: faire un tri en gardant l'information de nom de colonne

Messagepar Pierre-Yves Berrard » 24 Oct 2018, 08:28

Guillaume Dramais a écrit :La solution de Pierre-Yves semblait aussi assez simple à transposer mais je n'arrive pas à créer le dataframe de résultats
Error in data.frame(val_min = out8[, 1], nom_min = colnames(out7)[apply(out7, :
les arguments impliquent des nombres de lignes différents : 19375, 0

Peut-on avoir un aperçu de out7 et out8 ?
PY

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Re: faire un tri en gardant l'information de nom de colonne

Messagepar François Bonnot » 24 Oct 2018, 10:47

Le code suivant nécessite peu de mémoire et devrait fonctionner sur des jeux de données volumineux :

Code : Tout sélectionner

dist1 <- function(z,df2) {
    d2 <- function(a,b) (a-b)^2
    x2 <- sapply(df2[[1]],d2,z[1])
    y2 <- sapply(df2[[2]],d2,z[2])
    dist <- sqrt(x2+y2)
    c(min(dist),which.min(dist))
}

min1 <- function(df1,df2) {
    m <- sapply(1:nrow(df1),function(i) dist1(as.numeric(df1[i,]),df2))
    data.frame(min=m[1,],sample=row.names(df2)[m[2,]])
}

min1(data1,samples)

EDIT - Il y a même plus simple :

Code : Tout sélectionner

dist1 <- function(z,df2) {
    dist <- sqrt((df2[[1]]-z[1])^2+(df2[[2]]-z[2])^2)
    c(min(dist),which.min(dist))
}

min1 <- function(df1,df2) {
    m <- sapply(1:nrow(df1),function(i) dist1(as.numeric(df1[i,]),df2))
    data.frame(min=m[1,],sample=row.names(df2)[m[2,]])
}

min1(data1,samples)
François

Guillaume Dramais
Messages : 39
Enregistré le : 25 Sep 2018, 00:04

Re: faire un tri en gardant l'information de nom de colonne

Messagepar Guillaume Dramais » 24 Oct 2018, 17:17

Pierre-Yves,
L'idée est bien d'identifier l'échantillon "samples" le plus proche de chacune de mes données.

out7 est une matrice avec les résultats de ma première boucle qui calculait toutes les distances entre mes 19375 données et mes 14 échantillons

Code : Tout sélectionner

> dim(out7)
[1] 19375    14

out8 est un vecteur de longueur 19375, issu de la seconde boucle, avec la distance minimale ~ l'échantillon le plus proche

Je regarde les différentes solutions proposées
merci

Guillaume Dramais
Messages : 39
Enregistré le : 25 Sep 2018, 00:04

Re: faire un tri en gardant l'information de nom de colonne

Messagepar Guillaume Dramais » 25 Oct 2018, 04:09

Merci aussi pour les solutions proposées par François,

Dans ces solutions je change les df1, df2 par les nom de mes dataframes.
Le premier code marche très bien sur l’exemple, et presque jusqu'au bout sur mon jeu de données.
Sur le jeu de donnée long ça coince à la création du dernier dataframe :

[/quote]> data.frame(min=dmin,sample=colnames(d)[positions])
Error in colnames(d)[positions] : invalid subscript type 'list'[/code]

La deuxième proposition,

Code : Tout sélectionner

> dist1 <- function(z,samplesand) {
+   d2 <- function(a,b) (a-b)^2
+   x2 <- sapply(samplesand[[1]],d2,z[1])
+   y2 <- sapply(samplesand[[2]],d2,z[2])
+   dist <- sqrt(x2+y2)
+   c(min(dist),which.min(dist))
+ }
>
> min1 <- function(ADCP_longformat,samplesand) {
+   m <- sapply(1:nrow(ADCP_longformat),function(i) dist1(as.numeric(ADCP_longformat[i,]),samplesand))
+   data.frame(min=m[1,],sample=row.names(samplesand)[m[2,]])
+ }
>
> min1(ADCP_longformat,samplesand)

Error in m[1, ] : incorrect number of dimensions



Le dernier exemple me renvoie la même erreur
Error in m[1, ] : incorrect number of dimensions

Pourtant mon jeu de données long semble bien identique a celui de l'exemple.
Avez vous une interprétation pour ces erreurs?

merci d'avance
Guillaume

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Re: faire un tri en gardant l'information de nom de colonne

Messagepar François Bonnot » 25 Oct 2018, 06:39

Bonjour,
Dans ces solutions je change les df1, df2 par les nom de mes dataframes.

Il ne faut pas le faire car df1 et df2 sont les arguments des fonctions.
Il faut simplement charger les fonctions dist1 et min1 telles quelles (utiliser la version après EDIT, pas la première) et changer les noms data1 et samples dans la dernière ligne min1(data1,samples).
Vérifier si les erreurs persistent avec cette méthode correcte.
François

Guillaume Dramais
Messages : 39
Enregistré le : 25 Sep 2018, 00:04

Re: faire un tri en gardant l'information de nom de colonne

Messagepar Guillaume Dramais » 25 Oct 2018, 18:10

Bonjour,
Oui j'ai les mêmes erreurs sans changer les noms dans la fonction, le problème doit venir de mes fichiers de données.
...

Guillaume Dramais
Messages : 39
Enregistré le : 25 Sep 2018, 00:04

Re: faire un tri en gardant l'information de nom de colonne

Messagepar Guillaume Dramais » 25 Oct 2018, 22:49

Mon problème vient de la construction du fichier de données. Je pensais avoir assemblé deux df de même type avec rbind mais ils ne l'étaient visiblement pas.

Guillaume Dramais
Messages : 39
Enregistré le : 25 Sep 2018, 00:04

Re: faire un tri en gardant l'information de nom de colonne

Messagepar Guillaume Dramais » 26 Oct 2018, 05:36

Je rame

Error in 1:nrow(ADCP_longformat) : argument of length 0


> min1(ADCP_longformat,samplesand)
Error in m[1, ] : incorrect number of dimensions


Je construis un fichier de 3 colonnes de valeurs, j'assemble avec un autre dataframe de même type (avec les 3 mêmes colonnes) avec rbind j'ai aussi essayé merge. Je me retrouve avec un tableau à trois colonnes de valeurs numériques de même longueur, je peux les sommer, ou faire d'autres opérations, ce que je fais dans la suite de mon code sans problème.

Les codes de tri proposés ci dessus marchent avec la première partie du df,mais pas si j'ajoute la seconde, je ne comprend pas.
Comment déceler le qui cloche dans ce dataframe?
merci


Retourner vers « Questions en cours »

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 1 invité