Comment réorganiser les colonnes d'un bloc de données?

311

Comment changerait-on cette entrée (avec la séquence: heure, entrée, sortie, fichiers):

Time   In    Out  Files
1      2     3    4
2      3     4    5

Vers cette sortie (avec la séquence: time, out, in, files)?

Time   Out   In  Files
1      3     2    4
2      4     3    5

Voici les données factices R:

table <- data.frame(Time=c(1,2), In=c(2,3), Out=c(3,4), Files=c(4,5))
table
##  Time In Out Files
##1    1  2   3     4
##2    2  3   4     5
Catherine
la source
4
help(Extract)également connu sous le nom de?'['
Joris Meys
3
En plus de la suggestion de @ Joris, essayez de lire les sections 2.7 et 5 du manuel "An Introduction to R": cran.r-project.org/doc/manuals/R-intro.html
Gavin Simpson
3
Un problème supplémentaire: toutes les réponses nécessitent la liste complète des colonnes, sinon elles entraînent un sous-ensemble. Que se passe-t-il si nous ne voulons répertorier que quelques colonnes à commander en tant que premières, mais également en conservant toutes les autres?
000andy8484

Réponses:

341

Votre trame de données a quatre colonnes comme ça df[,c(1,2,3,4)]. Notez que la première virgule signifie conserver toutes les lignes, et le 1,2,3,4 fait référence aux colonnes.

Pour changer l'ordre comme dans la question ci-dessus, faites df2[,c(1,3,2,4)]

Si vous voulez sortir ce fichier en tant que csv, faites write.csv(df2, file="somedf.csv")

richiemorrisroe
la source
35
C'est correct lorsque vous avez un nombre limité de colonnes, mais que se passe-t-il si vous avez par exemple 50 colonnes, il faudrait trop de temps pour taper tous les numéros de colonne ou les noms. Quelle serait une solution plus rapide?
Herman Toothrot
54
@ user4050: dans ce cas, vous pouvez utiliser la syntaxe ":", par exemple df [, c (1,3,2,4,5: 50)].
dalloliogm
1
pour placer les colonnes dans idcols au début: idcols <- c ("nom", "id2", "début", "durée"); cols <- c (idcols, names (cts) [- which (names (cts)% in% idcols)]); df <- df [cols]
kasterma
13
@ user4050: vous pouvez également l'utiliser df[,c(1,3,2,4:ncol(df))]lorsque vous ne savez pas combien de colonnes il y a.
arekolek
1
Vous pouvez également utiliser dput (noms de colonnes (df)), il imprime les noms des colonnes au format de caractère R. Vous pouvez ensuite réorganiser les noms.
Chris
168
# reorder by column name
data <- data[c("A", "B", "C")]

#reorder by column index
data <- data[c(1,3,2)]
Xavier Guardiola
la source
1
Question en tant que débutant, pouvez-vous combiner la commande par index et par nom? Par exemple data <- data[c(1,3,"Var1", 2)]?
Bram Vanroy
6
@BramVanroy nope, c(1,3,"Var1", 2)sera lu comme étant c("1","3","Var1", "2")donné que les vecteurs peuvent contenir des données d'un seul type, donc les types sont promus au type le plus général présent. Parce qu'il n'y a pas de colonnes avec les noms de caractères "1", "3", etc., vous obtiendrez des "colonnes non définies". list(1,3,"Var1", 2)conserve les valeurs sans promotion de type, mais vous ne pouvez pas utiliser un listdans le contexte ci-dessus.
Terry Brown
1
Pourquoi le sous- mtcars[c(1,3,2)]ensemble fonctionne-t-il? Je m'attendais à une erreur liée à des dimensions incorrectes ou similaires ... N'est-ce pas mtcars[,c(1,3,2)]?
landroni
data.frames sont des listes sous le capot avec des colonnes comme éléments de premier ordre
petermeissner
106

Vous pouvez également utiliser la fonction de sous-ensemble:

data <- subset(data, select=c(3,2,1))

Vous devriez mieux utiliser l'opérateur [] comme dans les autres réponses, mais il peut être utile de savoir que vous pouvez effectuer un sous-ensemble et une opération de réorganisation de colonne dans une seule commande.

Mettre à jour:

Vous pouvez également utiliser la fonction select du package dplyr:

data = data %>% select(Time, out, In, Files)

Je ne suis pas sûr de l'efficacité, mais grâce à la syntaxe de dplyr, cette solution devrait être plus flexible, surtout si vous avez beaucoup de colonnes. Par exemple, les éléments suivants réorganiseront les colonnes de l'ensemble de données mtcars dans l'ordre inverse:

mtcars %>% select(carb:mpg)

Et ce qui suit ne réorganisera que certaines colonnes et en supprimera d'autres:

mtcars %>% select(mpg:disp, hp, wt, gear:qsec, starts_with('carb'))

En savoir plus sur la syntaxe select de dplyr .

dalloliogm
la source
5
Il y a quelques raisons de ne pas l'utiliser subset(), voir cette question .
MERose
2
Je vous remercie. Dans tous les cas, j'utiliserais maintenant la fonction select du package dplyr, au lieu du sous-ensemble.
dalloliogm
87
Lorsque vous voulez mettre quelques colonnes sur le côté gauche et ne pas laisser tomber les autres, je trouve everything()particulièrement génial; mtcars %>% select(wt, gear, everything())
guyabel
2
Voici une autre façon d'utiliser la fonction everything () select_helper pour réorganiser les colonnes vers la droite / fin. stackoverflow.com/a/44353144/4663008 github.com/tidyverse/dplyr/issues/2838 Il semble que vous devrez utiliser 2 select () pour déplacer certaines colonnes vers la droite et d'autres vers la gauche.
Arthur Yip
1
la nouvelle fonction dplyr :: relocate est exactement pour cela. voir la réponse de H 1 ci
Arthur Yip
39

Comme mentionné dans ce commentaire , les suggestions standard pour réorganiser les colonnes dans un data.framesont généralement lourdes et sujettes aux erreurs, surtout si vous avez beaucoup de colonnes.

Cette fonction permet de réorganiser les colonnes par position: spécifiez un nom de variable et la position souhaitée, et ne vous inquiétez pas pour les autres colonnes.

##arrange df vars by position
##'vars' must be a named vector, e.g. c("var.name"=1)
arrange.vars <- function(data, vars){
    ##stop if not a data.frame (but should work for matrices as well)
    stopifnot(is.data.frame(data))

    ##sort out inputs
    data.nms <- names(data)
    var.nr <- length(data.nms)
    var.nms <- names(vars)
    var.pos <- vars
    ##sanity checks
    stopifnot( !any(duplicated(var.nms)), 
               !any(duplicated(var.pos)) )
    stopifnot( is.character(var.nms), 
               is.numeric(var.pos) )
    stopifnot( all(var.nms %in% data.nms) )
    stopifnot( all(var.pos > 0), 
               all(var.pos <= var.nr) )

    ##prepare output
    out.vec <- character(var.nr)
    out.vec[var.pos] <- var.nms
    out.vec[-var.pos] <- data.nms[ !(data.nms %in% var.nms) ]
    stopifnot( length(out.vec)==var.nr )

    ##re-arrange vars by position
    data <- data[ , out.vec]
    return(data)
}

Maintenant, la demande du PO devient aussi simple que ceci:

table <- data.frame(Time=c(1,2), In=c(2,3), Out=c(3,4), Files=c(4,5))
table
##  Time In Out Files
##1    1  2   3     4
##2    2  3   4     5

arrange.vars(table, c("Out"=2))
##  Time Out In Files
##1    1   3  2     4
##2    2   4  3     5

Pour permuter Timeet Filescolonnes en plus, vous pouvez faire ceci:

arrange.vars(table, c("Out"=2, "Files"=1, "Time"=4))
##  Files Out In Time
##1     4   3  2    1
##2     5   4  3    2
landroni
la source
Très belle fonction. J'ai ajouté une version modifiée de cette fonction à mon package personnel .
Deleet
1
C'est vraiment utile - ça va me faire gagner beaucoup de temps quand je veux juste déplacer une colonne de la fin d'un très gros tibble au début
Mrmoleje
Wow, j'adore ça.
OfTheAzureSky
37

Une dplyrsolution (faisant partie de l' tidyverseensemble de packages) consiste à utiliser select:

select(table, "Time", "Out", "In", "Files") 

# or

select(table, Time, Out, In, Files)
Ben G
la source
2
La meilleure option pour moi. Même si je devais l'installer, c'est clairement la possibilité la plus claire.
Garini
15
Tidyverse (dplyr en fait) a également la possibilité de sélectionner des groupes de colonnes, par exemple pour déplacer la variable espèce à l'avant: select(iris, Species, everything()). Notez également que les devis ne sont pas nécessaires.
Paul Rougieux
3
Il est important de noter que cela supprimera toutes les colonnes qui ne sont pas explicitement spécifiées, sauf si vous incluez everything()comme dans le commentaire de
PaulRougieux
dplyr's groupréorganisera également les variables, alors faites attention lorsque vous les utilisez dans une chaîne.
David Tonhofer
26

C'est peut-être une coïncidence si l'ordre des colonnes que vous souhaitez se trouve avoir des noms de colonnes dans l'ordre alphabétique décroissant. Puisque c'est le cas, vous pouvez simplement faire:

df<-df[,order(colnames(df),decreasing=TRUE)]

C'est ce que j'utilise lorsque j'ai de gros fichiers avec de nombreuses colonnes.

user3482899
la source
!! WARNING !! data.tablese transforme TARGETen un vecteur int: TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)] pour résoudre ce problème: TARGET <- as.data.frame(TARGET) TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]
Zachary Ryan Smith
12

Les trois réponses les mieux notées ont une faiblesse.

Si votre trame de données ressemble à ceci

df <- data.frame(Time=c(1,2), In=c(2,3), Out=c(3,4), Files=c(4,5))

> df
  Time In Out Files
1    1  2   3     4
2    2  3   4     5

alors c'est une mauvaise solution à utiliser

> df2[,c(1,3,2,4)]

Il fait l'affaire, mais vous venez d'introduire une dépendance sur l'ordre des colonnes dans votre entrée.

Ce style de programmation fragile est à éviter.

La dénomination explicite des colonnes est une meilleure solution

data[,c("Time", "Out", "In", "Files")]

De plus, si vous avez l'intention de réutiliser votre code dans un cadre plus général, vous pouvez simplement

out.column.name <- "Out"
in.column.name <- "In"
data[,c("Time", out.column.name, in.column.name, "Files")]

ce qui est également très agréable car il isole complètement les littéraux. En revanche, si vous utilisez dplyr'sselect

data <- data %>% select(Time, out, In, Files)

alors vous mettriez en place ceux qui liront votre code plus tard, vous y compris, pour un peu de tromperie. Les noms de colonne sont utilisés comme littéraux sans apparaître dans le code en tant que tels.

Vrokipal
la source
3

dplyrla version 1.0.0inclut la relocate()fonction pour réorganiser facilement les colonnes:

dat <- data.frame(Time=c(1,2), In=c(2,3), Out=c(3,4), Files=c(4,5))

library(dplyr) # from version 1.0.0 only

dat %>%
  relocate(Out, .before = In)

ou

dat %>%
  relocate(Out, .after = Time)
27 ϕ 9
la source
2
data.table::setcolorder(table, c("Out", "in", "files"))
Hossein Noorazar
la source
Veuillez indiquer la bibliothèque à partir de laquelle vous prenez la fonction setcolorder.
Triamus
1

Le seul que j'ai vu bien fonctionner est d' ici .

 shuffle_columns <- function (invec, movecommand) {
      movecommand <- lapply(strsplit(strsplit(movecommand, ";")[[1]],
                                 ",|\\s+"), function(x) x[x != ""])
  movelist <- lapply(movecommand, function(x) {
    Where <- x[which(x %in% c("before", "after", "first",
                              "last")):length(x)]
    ToMove <- setdiff(x, Where)
    list(ToMove, Where)
  })
  myVec <- invec
  for (i in seq_along(movelist)) {
    temp <- setdiff(myVec, movelist[[i]][[1]])
    A <- movelist[[i]][[2]][1]
    if (A %in% c("before", "after")) {
      ba <- movelist[[i]][[2]][2]
      if (A == "before") {
        after <- match(ba, temp) - 1
      }
      else if (A == "after") {
        after <- match(ba, temp)
      }
    }
    else if (A == "first") {
      after <- 0
    }
    else if (A == "last") {
      after <- length(myVec)
    }
    myVec <- append(temp, values = movelist[[i]][[1]], after = after)
  }
  myVec
}

Utilisez comme ceci:

new_df <- iris[shuffle_columns(names(iris), "Sepal.Width before Sepal.Length")]

Fonctionne comme un charme.

Cybernétique
la source