lapply en fonction de plusieurs attributs

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

Jocelyn Carré
Messages : 25
Enregistré le : 13 Juin 2016, 12:54

lapply en fonction de plusieurs attributs

Messagepar Jocelyn Carré » 15 Juin 2016, 13:50

Bonjour à tous,
A partir d'un data.frame, je souhaite réaliser soit une suite de subset soit une suite de split afin de créer de nouveaux data.frame ne tenant compte que de certains facteurs. Par exemple :

Code : Tout sélectionner

####Example :
#Delete Environment and clear console
cat("\014")
rm(list=ls())
## Data creation
experimental_plot<-1:10
species<-(c("Wheat","Maiz","Barley","Ray_Grass","Wheat","Maiz","Barley","Ray_Grass","Barley","Ray_Grass") )
variable_plant<-(c("Yield","Impact","Impact","Quality","Yield","Yield","Impact","Impact","Yield","Quality") )
organ<-(c("grain", "entire_plant","grain","entire_plant","straw","grain","straw","grain","grain","entire_plant"))
values_plant<-c(25,3,2,0.55,28,21,3,1,26,0.80)
mydata1<-data.frame(experimental_plot,species,variable_plant,values_plant,organ)
print(mydata1)

   experimental_plot   species variable_plant values_plant        organ
1                  1     Wheat          Yield        25.00        grain
2                  2      Maiz         Impact         3.00 entire_plant
3                  3    Barley         Impact         2.00        grain
4                  4 Ray_Grass        Quality         0.55 entire_plant
5                  5     Wheat          Yield        28.00        straw
6                  6      Maiz          Yield        21.00        grain
7                  7    Barley         Impact         3.00        straw
8                  8 Ray_Grass         Impact         1.00        grain
9                  9    Barley          Yield        26.00        grain
10                10 Ray_Grass        Quality         0.80 entire_plant


Je souhaite donc récupérer quatre data.frame, pour chacune des quatre espèces, en conservant les données (rendement, qualité, impact) correspondantes. J'ai deux possibilités, faire un subset ou un split. Ceci étant un exemple, je vais travailler sur une table bien plus grande, je souhaiterais donc automatiser ces subset/split.
J'ai donc tenté ceci :

Code : Tout sélectionner

### Split with a for loop:
mydata_split<-split(mydata,mydata$species)

for (i in 1:n){
    print(((paste(all_species[i],"_","data"))))
    print(as.data.frame(mydata_split[i]))
    }
rm(i)


[1] "Barley _ data"
  Barley.experimental_plot Barley.species Barley.variable Barley.values
3                        3         Barley          Impact             2
7                        7         Barley          Impact             3
9                        9         Barley           Yield            26
[1] "Maiz _ data"
  Maiz.experimental_plot Maiz.species Maiz.variable Maiz.values
2                      2         Maiz        Impact           3
6                      6         Maiz         Yield          21
[1] "Ray_Grass _ data"
   Ray_Grass.experimental_plot Ray_Grass.species Ray_Grass.variable
4                            4         Ray_Grass            Quality
8                            8         Ray_Grass             Impact
10                          10         Ray_Grass            Quality
   Ray_Grass.values
4              0.55
8              1.00
10             0.80
[1] "Wheat _ data"
  Wheat.experimental_plot Wheat.species Wheat.variable Wheat.values
1                       1         Wheat          Yield           25
5                       5         Wheat          Yield           28

et ceci :

Code : Tout sélectionner

### Subset with a for loop:
for (i in 1:n){
  print(((paste(all_species[i],"_","data"))))
  print(subset(mydata,mydata$species==all_species[i]))
}
rm(i)

[1] "Barley _ data"
  experimental_plot species variable values
3                 3  Barley   Impact      2
7                 7  Barley   Impact      3
9                 9  Barley    Yield     26
[1] "Maiz _ data"
  experimental_plot species variable values
2                 2    Maiz   Impact      3
6                 6    Maiz    Yield     21
[1] "Ray_Grass _ data"
   experimental_plot   species variable values
4                  4 Ray_Grass  Quality   0.55
8                  8 Ray_Grass   Impact   1.00
10                10 Ray_Grass  Quality   0.80
[1] "Wheat _ data"
  experimental_plot species variable values
1                 1   Wheat    Yield     25
5                 5   Wheat    Yield     28

Donc les fonctions subset/split fonctionnent, je peux afficher leur résultat tout au long de la boucle. Ce que je souhaiterais, c'est enregistrer ces data.frame comme objet R (et non pas simplement l'afficher comme ici), et que ce qui est affiché avant avec la fonction "paste" soit le nom de cet objet. Mais un code comme celui ci :

Code : Tout sélectionner

for (i in 1:n){
  ((paste(all_species[i],"_","data")))<-print(subset(mydata,mydata$species==all_species[i]))
}
rm(i)

renvoi sans surprise une erreur :
Error in ((paste(all_species[i], "_", "data"))) <- print(subset(mydata, :
could not find function "(<-"


Quelqu'un saurait-il comment faire ?
Merci d'avance,
Jocelyn

Serge Rapenne
Messages : 1426
Enregistré le : 20 Aoû 2007, 15:17
Contact :

Re: subset ou split dans une boucle for

Messagepar Serge Rapenne » 15 Juin 2016, 13:52

Bonjour,

cf la fonction assign

Code : Tout sélectionner

?assign
pour les détails

Serge

Gabriel Terraz
Messages : 591
Enregistré le : 26 Sep 2011, 15:11

Re: subset ou split dans une boucle for

Messagepar Gabriel Terraz » 15 Juin 2016, 13:55

Salut,
Un exemple avec assign :

Code : Tout sélectionner

lapply(levels(mydata$species), function(x) assign(paste0(x,"_mydata"), droplevels(mydata[mydata$species == x,]), envir = .GlobalEnv))

> Wheat_mydata
  experimental_plot species variable values
1                 1   Wheat    Yield     25
5                 5   Wheat    Yield     28

Jocelyn Carré
Messages : 25
Enregistré le : 13 Juin 2016, 12:54

Re: subset ou split dans une boucle for

Messagepar Jocelyn Carré » 15 Juin 2016, 14:09

Merci pour ces deux réponses !

Jocelyn Carré
Messages : 25
Enregistré le : 13 Juin 2016, 12:54

Re: subset ou split dans une boucle for

Messagepar Jocelyn Carré » 16 Juin 2016, 06:57

Gabriel, je ne comprends pas dans ton code l'utilisation de droplevels. Je vois bien que cela fonctionne, mais après m'être penché dessus, il s'avère que c'est une fonction initialement utilisée pour supprimer des modalités inutilisées d'une colonne. Or ici ce n'est pas le cas, donc même si la fonction renvoi un objet de même classe que "mydata[mydata$species == x", donc un data.frame, je ne comprends pas comment cela fonctionne...

Gabriel Terraz
Messages : 591
Enregistré le : 26 Sep 2011, 15:11

Re: subset ou split dans une boucle for

Messagepar Gabriel Terraz » 16 Juin 2016, 07:14

Salut,
Lorsque tu sélectionnes une modalité de la colonne espèce pour créer un dataframe avec seulement cette modalité, tu vas conserver dans le nouveau dataframe toutes les modalités de ta colonne espèce alors que tu n'en as plus besoin (a priori). Droplevels permet de les retirer

Jocelyn Carré
Messages : 25
Enregistré le : 13 Juin 2016, 12:54

Re: subset ou split dans une boucle for

Messagepar Jocelyn Carré » 16 Juin 2016, 08:08

D'accord, merci beaucoup !

Jocelyn Carré
Messages : 25
Enregistré le : 13 Juin 2016, 12:54

Re: lapply en fonction de plusieurs attributs

Messagepar Jocelyn Carré » 28 Juil 2016, 13:51

Bonjour à tous,
j'ai pu utiliser la fonction proposé plus haut par Gabriel, et elle fonctionne très bien. J'ai aujourd'hui un autre problème. Là où avant je souhaitais seulement créer des dataframe à partir d'un attribut, en l’occurrence "species", je voudrais désormais créer des data frame par espèce et par organe.
Après avoir réfléchis au problème je crois avoir identifié deux types de solutions possibles :
1) Je stocke chaque data frame créée dans une liste, à laquelle j'applique ensuite un lapply par organe. Je pensais stocker les dataframe en intégrant ce genre de fonction, en ayant préalablement créé la liste:

Code : Tout sélectionner

mylist<-list()
mylist[[length(mylist)+1]]<-2
mylist[[length(mylist)+1]]<-c(1,2,3,4)

Cependant je n'arrive pas à l'intégrer dans le lapply. Est-ce la fonction assign qu'il faut utiliser à nouveau ? En dehors du lapply j'ai tenté

Code : Tout sélectionner

assign(mylist[[length(mylist)+1]], 1)
pour assigner 1 à la première place de "mylist", mais cela me renvoi le message "subscript out of bounds", m'indiquant que j'ai tenté d'assigner un objet à une place inexistante de la liste. J'imagine donc qu'on ne peut pas remplacer ici l'opérateur "<-" par "assign".
Et j'aurais conclu mon code par

Code : Tout sélectionner

lapply(mylist, function(x) assign(paste0(x,"_grain_mydata"), droplevels(mydata1[mydata1$organ == "grain",]),envir = .GlobalEnv ))

de telle manière à obtenir les data frame par organe.

2)La seconde solution serait de pouvoir spécifier plusieurs critères dans le lapply initial, par exemple créer des data frame relatifs à une espèce et à un organe en une fois.
Il faudrait donc soit modifier la fonction droplevels, soit en rajouter une seconde dans la fonction, j'imagine.

Code : Tout sélectionner

lapply(levels(mydata1$species), function(x) assign(paste0(x,"_grain_mydata"), droplevels(mydata1[mydata1$species == x,]),droplevels(mydata1[mydata1$organ == "grain",]),envir = .GlobalEnv ))

Rajouter un droplevels de cette manière ne change rien au résultat, et je ne sais pas comment modifier la fonction pour intégrer deux critères, l'opérateur "&" ne répondant pas au problème (où alors je ne l'utilise pas correctement).

Est-ce que quelqu'un aurait une ou des pistes de réponse ?
Merci d'avance :)

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

Re: lapply en fonction de plusieurs attributs

Messagepar Pierre-Yves Berrard » 28 Juil 2016, 14:54

Bonjour,

En revenant à la fonction split avec l'option drop=TRUE qui supprime les croisements vides à la source :

Code : Tout sélectionner

list_df <- split(
  mydata,
  list(mydata$species, mydata$organ),
  drop = TRUE
)

for (nom in names(list_df)) assign(nom, list_df[[nom]])

rm(list_df, nom)


Il y a peut-être une façon de faire avec lapply sans créer une liste intermédiaire...

Jocelyn Carré
Messages : 25
Enregistré le : 13 Juin 2016, 12:54

Re: lapply en fonction de plusieurs attributs

Messagepar Jocelyn Carré » 29 Juil 2016, 06:54

Bonjour,
Merci de cette solution !
Elle convient parfaitement.
J'ai plus une question de curiosité qu'autre chose : si vous avez appelé la liste "list_df", c'est parce que l'abréviation "df" correspond à quelque chose en particulier ? " Default" ?
Merci encore

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

Re: lapply en fonction de plusieurs attributs

Messagepar Pierre-Yves Berrard » 29 Juil 2016, 08:07

Jocelyn Carré a écrit :J'ai plus une question de curiosité qu'autre chose : si vous avez appelé la liste "list_df", c'est parce que l'abréviation "df" correspond à quelque chose en particulier ? " Default" ?

data.frame !
;-)

Jocelyn Carré
Messages : 25
Enregistré le : 13 Juin 2016, 12:54

Re: lapply en fonction de plusieurs attributs

Messagepar Jocelyn Carré » 29 Juil 2016, 08:14

Et oui, bien sûr ! :)
Merci encore !

Nicolas Péru
Messages : 1408
Enregistré le : 07 Aoû 2006, 08:13

Re: lapply en fonction de plusieurs attributs

Messagepar Nicolas Péru » 29 Juil 2016, 12:09

Salut,

Quand on commence à faire des listes de listes = gros mal de tête à l'horizon :)
Plus sérieusement, à partir du moment où tu cherches à travailler par bloc dans un jeu de données alors pense aux librairies qui on été faites pour ça car ça éclairci beaucoup le code et ça fait jamais de mal:

je te donne l'exemple avec le package plyr mais d'autres te parleront de dplyr ou encore de data.table

Code : Tout sélectionner

require plyr
ddply(mydata1,.(species,organ),
   function(tab){
     assign(paste0(unique(tab$species),unique(tab$organ)),tab,envir=.GlobalEnv)
   }
)


Dans les fonctions de plyr, le drop est une option par défaut.

On teste:

Code : Tout sélectionner

Barleygrain
  experimental_plot species variable_plant values_plant organ
1                 3  Barley         Impact            2 grain
2                 9  Barley          Yield           26 grain


Si tu veux encore découper ton jeux de données, il te suffit de rajouter les facteurs dans l'argument .(variable) et éventuellement dans le nom via la fonction assign

Nicolas

Jocelyn Carré
Messages : 25
Enregistré le : 13 Juin 2016, 12:54

Re: lapply en fonction de plusieurs attributs

Messagepar Jocelyn Carré » 08 Aoû 2016, 07:00

Bonjour,
Merci de cette seconde solution !
C'est vrai que je n'ai pas assez le réflexe d'aller voir les packages.
Merci encore !


Retourner vers « Questions en cours »

Qui est en ligne

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