découper un data frame

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

découper un data frame

Messagepar Guillaume Dramais » 05 Oct 2018, 19:54

Bonjour,
J'essaie de découper un dataframe qui représente des mesures repérées en distance et profondeur, en sous parties selon des coordonnées. Voilà un exemple simplifié et les commentaires. Je bloque sur la boucle finale qui doit me permettre de découper en fonction de coordonnées distance profondeur.
Si vous avez des conseils pour m'aider svp

Code : Tout sélectionner


#J'ai un dataframe avec des valeurs numériques qui sont "repérées" par distance

rm(list=ls())
distance<-c(0,2.5,3.5,4.5,5.5,6.5)
x1<-c(1,4,3,6,1,4)
x2<-c(11,42,35,61,11,44)
x3<-c(51,62,5,1,8,NA)
x4<-c(41,52,63,14,74,43)
x5<-c(NA,47,6,4,7,47)
dat<-data.frame(distance,x1,x2,x3,x4,x5)

dat

profondeur <- c(0,2,4,6,8,10) # j'ai un vecteur de valeurs croissantes (de profondeur)
datatest <- rbind(profondeur, dat) # je l'intègre comme première ligne de mon data.frame
# j'ai donc un dataframe avec pour chaque cellule des coordonnées (distance, profondeur)
# par ma prenière ligne et ma première colonne

datatest

#J'ai les coordonnées de (distance, profondeur) des sous-ensemble que je veux découper
# distance min, distance max, profondeur min, profondeur max
s1<-c(2,4,0,2.5)
s2<-c(4,5,2.5,7)
s3<-c(5,6.2,7,9.6)

samp<-rbind(s1, s2, s3)
samp

#je veux découper des sous-ensemble fonctions des coordonnées distance et profondeur de mon objet samp
# Et faire par exemple la moyenne des valeurs numériques de ce sous ensemble
# une boucle doit me permettre de faire ça

out=NULL # objet qui va contenir tes résultats en sortie de boucle,
n=nrow(samp) # nombre de sous-ensembles que je veux obtenir

for (i in 1:n){
 
  ech=data[which(dat$distance > samp$xmin[i] & dat$distance < samp$xmax[i] & dat$profondeur > samp$ymin[i] & data$profondeur < samp$ymax[i]),]
  moyenne=mean(ech$value,na.rm=TRUE)
  out=c(out,moyenne)
}

# Mais la façon de faire appel aux données n'est pas claire pour moi et ça coince

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

Re: découper un data frame

Messagepar Pierre-Yves Berrard » 08 Oct 2018, 08:18

Bonjour,

Ne faudrait-il pas dans un premier temps ajouter quelque part

Code : Tout sélectionner

colnames(samp) <- c("xmin", "xmax", "ymin", "ymax")
?
PY

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

Re: découper un data frame

Messagepar Guillaume Dramais » 08 Oct 2018, 22:36

Bonjour,

Oui effectivement, merci, j'ai aussi changé le nom de mon jeu de données mais je n'arrive toujours pas à obtenir un résultat à ma boucle.

Code : Tout sélectionner


#J'ai un dataframe avec des valeurs numériques qui sont "repérées" par distance

rm(list=ls())
distance<-c(0,2.5,3.5,4.5,5.5,6.5)
x1<-c(1,4,3,6,1,4)
x2<-c(11,42,35,61,11,44)
x3<-c(51,62,5,1,8,NA)
x4<-c(41,52,63,14,74,43)
x5<-c(NA,47,6,4,7,47)
dat<-data.frame(distance,x1,x2,x3,x4,x5)

dat

profondeur <- c(0,2,4,6,8,10) # j'ai un vecteur de valeurs croissantes (de profondeur)
ADCP <- rbind(profondeur, dat) # je l'intègre comme première ligne de mon data.frame
# j'ai donc un dataframe avec pour chaque cellule des coordonnées (distance, profondeur)
# par ma prenière ligne et ma première colonne

ADCP

ADCP[1,] # represente la profondeur

#J'ai les coordonnées de (distance, profondeur) des sous-ensemble que je veux découper
# distance min, distance max, profondeur min, profondeur max
s1<-c(2,4,0,2.5)
s2<-c(4,5,2.5,7)
s3<-c(5,6.2,7,9.6)

samp<-rbind(s1, s2, s3)
colnames(samp) <- c("xmin", "xmax", "ymin", "ymax")
samp<-data.frame(samp)

samp

#je veux découper des sous-ensembles, en fonction des coordonnées distance et profondeur de mon objet "samp"
# Et faire par exemple la moyenne des valeurs numériques de ce sous ensemble

# une boucle doit me permettre de faire ça
out=NULL # objet qui va contenir tes résultats en sortie de boucle,
n=nrow(samp) # nombre de sous-ensembles que je veux obtenir

for (i in 1:n){
 
  ech=ADCP[which(ADCP$distance > samp$xmin[i] & ADCP$distance < samp$xmax[i] & ADCP[1,] > samp$ymin[i] & ADCP[1,] < samp$ymax[i]),]
  moyenne=mean(ech$value,na.rm=TRUE)
  out=c(out,moyenne)
}


J'ai essayé plusieurs façon de nommer mais je n'ai pas trouvé la bonne formule...si vous avez une idée, un conseil, svp

Mickael Canouil
Messages : 1315
Enregistré le : 04 Avr 2011, 08:53
Contact :

Re: découper un data frame

Messagepar Mickael Canouil » 09 Oct 2018, 09:21

Bonjour,

Plusieurs commentaires/remarques et propositions:
a/ Attention, inclure le code "rm(list=ls())" ne garantit pas que l'a session R est réinitialiser, c.-à-d., les packages sont toujours chargés.
Ainsi, il est préférable de démarrer une nouvelle session de R (au passage, il est préférable de désactiver les options de sauvegarde/chargements des sessions de travail)
Image

b/ Quelques espaces et un peu moins d'affectation, permettrait de limiter le nombre d'objet dans votre session R et augmenterait du même coup la lisibilité et l'interprétation du code

Code : Tout sélectionner

ADCP <- data.frame(
  distance = c(0, 2.5, 3.5, 4.5, 5.5, 6.5),
  x1 = c(1, 4, 3, 6, 1, 4),
  x2 = c(11, 42, 35, 61, 11, 44),
  x3 = c(51, 62, 5, 1, 8, NA),
  x4 = c(41, 52, 63, 14, 74, 43),
  x5 = c(NA, 47, 6, 4, 7, 47)
)

samp <- rbind.data.frame(
  c(2, 4, 0, 2.5),
  c(4, 5, 2.5, 7),
  c(5, 6.2, 7, 9.6)
)
colnames(samp) <- c("xmin", "xmax", "ymin", "ymax")


c/ De mon interprétation/lecture, le vecteur "profondeur" n'a rien à faire dans la table ADCP, puisqu'elle semble être associé aux colonnes.
La nature/source des valeurs devient ici hétérogène, complexifiant leur utilisation. Comme la profondeur "0" ne semble pas existé, je ne réaffecte des noms de colonnes qu'avec les autres profondeurs.

Code : Tout sélectionner

colnames(ADCP)[-1] <- sprintf("P%02d", c(2, 4, 6, 8, 10))



d/ Maintenant, que les données sont stockés de façon homogène dans des objets, passons au formatage. Ici, vous souhaitez, effectuer des comparaisons de coordonnées impliquant la "distance" et la "profondeur", or nous avons ici un objet au format matrice (format "wide"), ce qui ne nous aide pas beaucoup.
Si nous avions, une colonne "distance" et une colonne "profondeur" pour chaque valeur, ce serait plus simple, non?

Code : Tout sélectionner

ADCP_longformat <- tidyr::gather(data = ADCP, key = "depth", value = "value", -distance)

Image

e/ Ici, j'avais transformé les profondeurs en chaînes de caractère (noms de colonnes), repassons dans un format numérique

Code : Tout sélectionner

ADCP_longformat[, "depth"] <- as.numeric(gsub("P", "", ADCP_longformat[, "depth"]))


f/ Lors de l'utilisation d'une boucle "for", il est important de définir la taille de l'objet en amont de la boucle, pour des questions de performances et de mémoire. Ici, vous savez que votre objet contiendra un nombre d'éléments égal au nombre de ligne de "samp", précisons le et nommons ces éléments:

Code : Tout sélectionner

samp[, "subset_names"] <- paste0("S", 1:nrow(samp))
out <- rep(NA, nrow(samp))
names(out) <- samp[, "subset_names"]


g/ Votre condition est assez longue et vous n'avez probablement pas vu que les éléments de celle-ci n'était pas de même format/nature...
Reprenez votre code et testez les lignes suivantes de façon indépendantes:

Code : Tout sélectionner

ADCP$distance > samp$xmin[i]
ADCP$distance < samp$xmax[i]
ADCP[1,] > samp$ymin[i]
ADCP[1,] < samp$ymax[i]

Image
Simplifions un peu tout cela, en découpant la condition en deux, sur les x et sur les y:

Code : Tout sélectionner

within_xrange <- ADCP_longformat[, "distance"] > samp[i, "xmin"] & ADCP_longformat[, "distance"] < samp[i, "xmax"]
within_yrange <- ADCP_longformat[, "depth"] > samp[i, "ymin"] & ADCP_longformat[, "depth"] < samp[i, "ymax"]


h/ Vérifions le sous groupe:

Code : Tout sélectionner

i = 1
within_xrange <- ADCP_longformat[, "distance"] > samp[i, "xmin"] & ADCP_longformat[, "distance"] < samp[i, "xmax"]
within_yrange <- ADCP_longformat[, "depth"] > samp[i, "ymin"] & ADCP_longformat[, "depth"] < samp[i, "ymax"]
 
ech <- ADCP_longformat[which(within_xrange & within_yrange), ]

Image

i/ Finalisons le code en utilisant l'ensemble:

Code : Tout sélectionner

ADCP <- data.frame(
  distance = c(0, 2.5, 3.5, 4.5, 5.5, 6.5),
  x1 = c(1, 4, 3, 6, 1, 4),
  x2 = c(11, 42, 35, 61, 11, 44),
  x3 = c(51, 62, 5, 1, 8, NA),
  x4 = c(41, 52, 63, 14, 74, 43),
  x5 = c(NA, 47, 6, 4, 7, 47)
)
colnames(ADCP)[-1] <- sprintf("P%02d", c(2, 4, 6, 8, 10))


samp <- rbind.data.frame(
  c(2, 4, 0, 2.5),
  c(4, 5, 2.5, 7),
  c(5, 6.2, 7, 9.6)
)
colnames(samp) <- c("xmin", "xmax", "ymin", "ymax")

ADCP_longformat <- tidyr::gather(data = ADCP, key = "depth", value = "value", -distance)
ADCP_longformat[, "depth"] <- as.numeric(gsub("P", "", ADCP_longformat[, "depth"]))

samp[, "subset_names"] <- paste0("S", 1:nrow(samp))
out <- rep(NA, nrow(samp))
names(out) <- samp[, "subset_names"]

for (i in 1:nrow(samp)){
 
  within_xrange <- ADCP_longformat[, "distance"] > samp[i, "xmin"] & ADCP_longformat[, "distance"] < samp[i, "xmax"]
  within_yrange <- ADCP_longformat[, "depth"] > samp[i, "ymin"] & ADCP_longformat[, "depth"] < samp[i, "ymax"]
 
  ech <- ADCP_longformat[which(within_xrange & within_yrange), ]
  out[samp[i, "subset_names"]] <- mean(ech[, "value"], na.rm = TRUE)
}

Image

Cordialement,
Mickaël
mickael.canouil.fr | rlille.fr

Jean-Emmanuel Longueville
Messages : 310
Enregistré le : 23 Fév 2011, 08:03

Re: découper un data frame

Messagepar Jean-Emmanuel Longueville » 09 Oct 2018, 10:16

Pardon une petite parenthèse par rapport au sujet mais Mickael serait il possible d'avoir un liens vers la source de votre diapositive Truc et Astuce qui me parait très intéressante.
merci
Jean-Emmanuel
Ingénieur d'étude LNEC

Mickael Canouil
Messages : 1315
Enregistré le : 04 Avr 2011, 08:53
Contact :

Re: découper un data frame

Messagepar Mickael Canouil » 09 Oct 2018, 10:45

N'étant pas l'objet en lui-même de la présentation, il ne s'agit que d'une seule diapositive, issue de mes diapositives sur R et les "bases de données" (au sens très large), disponible sur github: https://github.com/mcanouil/PRESENTATIO ... /Rdatabase

En bonus: "Code smells" de Jenny Brian à useR2018: https://www.youtube.com/watch?v=7oyiPBjLAWY
Mickaël
mickael.canouil.fr | rlille.fr

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

Re: découper un data frame

Messagepar Guillaume Dramais » 09 Oct 2018, 21:06

Bonjour Mickaël,
Merci pour cette réponse très détaillée et le formatage proposé qui semble bien plus pratique. Je vais essayer cette méthode sur mes données.

A propos des étapes c/ (affectation de la profondeur comme nom de colonne) et e/ (passage du nom en format numérique); j'ai essayé d'affecter un vecteur de valeurs numériques comme nom de colonne directement,

Code : Tout sélectionner

colnames(ADCP)[-1] <- (c(2, 4, 6, 8, 10))

mais ça pose problème ensuite pour l'étape d/ de changement de format.

merci encore pour vos précieux conseils
Cdlt

Mickael Canouil
Messages : 1315
Enregistré le : 04 Avr 2011, 08:53
Contact :

Re: découper un data frame

Messagepar Mickael Canouil » 11 Oct 2018, 08:27

Guillaume Dramais a écrit :A propos des étapes c/ (affectation de la profondeur comme nom de colonne) et e/ (passage du nom en format numérique); j'ai essayé d'affecter un vecteur de valeurs numériques comme nom de colonne directement,

Code : Tout sélectionner

colnames(ADCP)[-1] <- (c(2, 4, 6, 8, 10))

mais ça pose problème ensuite pour l'étape d/ de changement de format.

La réponse est dans le problème.
Par définition, un nom est une chaîne de caractère.
Et comme l'utilisation de valeurs numériques convertis en caractère (p.ex. "1", "10", "2", ...) est très ambigüe, par défaut R, va rajouter une lettre ("V") avant lors de l'utilisation de data.frame (résultat de check.names = TRUE).

Code : Tout sélectionner

dta <- as.data.frame(matrix(seq(16), 4, 4))
colnames(dta) <- c(2, 3, 4, 5)
dta[, "2"]
dta[, 2]
Mickaël
mickael.canouil.fr | rlille.fr

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

Re: découper un data frame

Messagepar Guillaume Dramais » 11 Oct 2018, 14:48

Merci


Retourner vers « Questions en cours »

Qui est en ligne

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