web-dev-qa-db-fra.com

Trier la colonne du bloc de données par facteur

Supposons que j'ai un bloc de données avec 3 colonnes (name, y, sex) où name est un caractère, y est un numérique value et sex est un facteur.

sex<-c("M","M","F","M","F","M","M","M","F")
x<-c("MARK","TOM","SUSAN","LARRY","EMMA","LEONARD","TIM","MATT","Violet")
name<-as.character(x)
y<-rnorm(9,8,1)
score<-data.frame(x,y,sex)
score
     name      y     sex
1    MARK  6.767086   M
2     TOM  7.613928   M
3   SUSAN  7.447405   F
4   LARRY  8.040069   M
5    EMMA  8.306875   F
6 LEONARD  8.697268   M
7     TIM 10.385221   M
8    MATT  7.497702   M
9  Violet 10.177969   F

Si je voulais le commander par y j'utiliserais:

score[order(score$y),]
        x         y sex
1    MARK  6.767086   M
3   SUSAN  7.447405   F
8    MATT  7.497702   M
2     TOM  7.613928   M
4   LARRY  8.040069   M
5    EMMA  8.306875   F
6 LEONARD  8.697268   M
9  Violet 10.177969   F
7     TIM 10.385221   M

Jusqu'ici, tout va bien ... Les noms gardent le bon score MAIS comment pourrais-je le réorganiser pour que les niveaux M et F ne soient pas mélangés. Je dois commander et en même temps garder les niveaux de facteurs séparés.

Enfin, je voudrais aller plus loin pour impliquer le caractère, l'exemple n'aide pas, mais que se passerait-il s'il y avait des valeurs y liées et que je devrais commander à nouveau dans facteur (par exemple TIM et TOM ont obtenu 8.4 et Je dois attribuer un ordre alphabétique).

Je pensais par fonction mais cela crée une liste et n'aide pas vraiment. Je pense qu'il doit y avoir une fonction comme celle-ci à appliquer sur les trames de données et à obtenir des trames de données en retour.

POUR EFFACER LE POINT:

sep<-split(score,score$sex)
sep$M<-sep$M[order(sep$M[,2]),]
sep$M
x         y sex
1    MARK  6.767086   M
8    MATT  7.497702   M
2     TOM  7.613928   M
4   LARRY  8.040069   M
6 LEONARD  8.697268   M
7     TIM 10.385221   M

sep$F<-sep$F[order(sep$F[,2]),]
sep$F
x         y sex
3  SUSAN  7.447405   F
5   EMMA  8.306875   F
9 Violet 10.177969   F

merged<-rbind(sep$M,sep$F)
merged
x         y sex
1    MARK  6.767086   M
8    MATT  7.497702   M
2     TOM  7.613928   M
4   LARRY  8.040069   M
6 LEONARD  8.697268   M
7     TIM 10.385221   M
3   SUSAN  7.447405   F
5    EMMA  8.306875   F
9  Violet 10.177969   F

Je sais comment faire ça si j'ai 2 ou 3 facteurs. Mais que faire si j'avais des niveaux de facteurs sérieux, disons 20, dois-je écrire une boucle for?

16
Matias Andina

order prend plusieurs arguments et fait exactement ce que vous voulez:

with(score, score[order(sex, y, x),])
##         x        y sex
## 3   SUSAN 6.636370   F
## 5    EMMA 6.873445   F
## 9  Violet 8.539329   F
## 6 LEONARD 6.082038   M
## 2     TOM 7.812380   M
## 8    MATT 8.248374   M
## 4   LARRY 8.424665   M
## 7     TIM 8.754023   M
## 1    MARK 8.956372   M
23
Matthew Lundberg

Voici un résumé de toutes les méthodes mentionnées dans d'autres réponses/commentaires (pour servir les futurs chercheurs). J'ai ajouté une méthode de tri data.table.

# Base R
do.call(rbind, by(score, score$sex, function(x) x[order(x$y),]))
with(score, score[order(sex, y, x),])
score[order(score$sex,score$x),]

# Using plyr
arrange(score, sex,y)
ddply(score, c('sex', 'y'))

# Using `data.table`
library("data.table")
score_dt <- setDT(score)

# setting a key works just fine
setkey(score_dt,sex,x)
print(score_dt)

# Explicitly ordering using i
score_dt[i=order(sex,x),]

Voici une autre question qui traite de la même chose

9
marbel

Je pense qu'il doit y avoir une fonction comme celle-ci à appliquer sur les trames de données et à obtenir des trames de données en retour

Oui il y a:

library(plyr)

ddply(score, c('y', 'sex'))
3
John

Il me semble que vous essayez d'ordonner par score parmi les hommes et les femmes et de renvoyer une base de données combinée de hommes et de femmes triés.

Vous avez raison de dire que by(score, score$sex, function(x) x[order(x$y),]) renvoie une liste de trames de données triées, une pour les hommes et une pour les femmes. Vous pouvez utiliser do.call avec la fonction rbind pour combiner ces trames de données en une seule trame de données finale:

do.call(rbind, by(score, score$sex, function(x) x[order(x$y),]))
#           x         y sex
# F.5    EMMA  7.526866   F
# F.9  Violet  8.182407   F
# F.3   SUSAN  9.677511   F
# M.4   LARRY  6.929395   M
# M.8    MATT  7.970015   M
# M.7     TIM  8.297137   M
# M.6 LEONARD  8.845588   M
# M.2     TOM  9.035948   M
# M.1    MARK 10.082314   M
2
josliber