Como alguém reordena colunas em um quadro de dados?

311

Como alguém alteraria essa entrada (com a sequência: hora, entrada, saída, arquivos):

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

Para esta saída (com a sequência: hora, saída, entrada, arquivos)?

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

Aqui estão os dados fictícios do 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
fonte
4
help(Extract)também conhecido como?'['
Joris Meys
3
Além @ Joris está sugerindo, Tente ler seções 2.7 e secção 5 do "An Introduction to R" manual: cran.r-project.org/doc/manuals/R-intro.html
Gavin Simpson
3
Uma questão adicional: todas as respostas requerem a lista completa de colunas; caso contrário, resultam em subconjuntos. E se quisermos listar apenas algumas colunas para serem ordenadas como as primeiras, mas também reter todas as outras?
000andy8484

Respostas:

341

Seu quadro de dados possui quatro colunas assim df[,c(1,2,3,4)]. Observe que a primeira vírgula significa manter todas as linhas e o 1,2,3,4 refere-se às colunas.

Para alterar a ordem como na pergunta acima, faça df2[,c(1,3,2,4)]

Se você deseja gerar este arquivo como um CSV, faça write.csv(df2, file="somedf.csv")

richiemorrisroe
fonte
35
Tudo bem quando você tem um número limitado de colunas, mas e se você tiver, por exemplo, 50 colunas, levaria muito tempo para digitar todos os números ou nomes de colunas. O que seria uma solução mais rápida?
Herman Toothrot 30/08/2013
54
@ user4050: nesse caso, você pode usar a sintaxe ":", por exemplo, df [, c (1,3,2,4,5: 50)].
Dalloliogm
1
para colocar as colunas em idcols no início: idcols <- c ("nome", "id2", "start", "duration"); cols <- c (idcols, nomes (cts) [- quais (nomes (cts)% em% idcols)]); df <- df [cols]
kasterma 10/06
13
@ user4050: você também pode usar df[,c(1,3,2,4:ncol(df))]quando não souber quantas colunas existem.
arekolek
1
Você também pode usar dput (colnames (df)), ele imprime os nomes das colunas no formato de caractere R. Você pode reorganizar os nomes.
31416 Chris
168
# reorder by column name
data <- data[c("A", "B", "C")]

#reorder by column index
data <- data[c(1,3,2)]
Xavier Guardiola
fonte
1
Pergunta como iniciante, você pode combinar a ordem por índice e por nome? Por exemplo data <- data[c(1,3,"Var1", 2)]?
Bram Vanroy
6
@BramVanroy não, c(1,3,"Var1", 2)será lido como os c("1","3","Var1", "2")vetores podem conter dados de apenas um tipo, portanto, os tipos são promovidos para o tipo mais geral presente. Como não há colunas com os nomes de caracteres "1", "3" etc., você receberá "colunas indefinidas". list(1,3,"Var1", 2)mantém valores sem promoção de tipo, mas você não pode usar um listno contexto acima.
Terry Brown
1
Por que o mtcars[c(1,3,2)]subconjunto funciona? Eu esperava um erro relacionado a dimensões incorretas ou similares ... Não deveria ser mtcars[,c(1,3,2)]?
landroni
data.frames são listas sob o capô com colunas como itens de primeira ordem
petermeissner
106

Você também pode usar a função de subconjunto:

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

Você deve usar melhor o operador [] como nas outras respostas, mas pode ser útil saber que você pode executar uma operação de reorganização de subconjunto e coluna em um único comando.

Atualizar:

Você também pode usar a função select do pacote dplyr:

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

Não tenho certeza da eficiência, mas graças à sintaxe do dplyr, essa solução deve ser mais flexível, especialmente se você tiver muitas colunas. Por exemplo, o seguinte reordenará as colunas do conjunto de dados mtcars na ordem oposta:

mtcars %>% select(carb:mpg)

E o seguinte reorganizará apenas algumas colunas e descartará outras:

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

Leia mais sobre a sintaxe de seleção do dplyr .

dalloliogm
fonte
5
Existem alguns motivos para não usar subset(), consulte esta pergunta .
MERose
2
Obrigado. De qualquer forma, agora eu usaria a função select do pacote dplyr, em vez do subconjunto.
dalloliogm
87
Quando você deseja trazer algumas colunas para o lado esquerdo e não largar as outras, acho everything()particularmente incrível; mtcars %>% select(wt, gear, everything())
guyabel
2
Aqui está outra maneira de usar a função select_helper everything () para reorganizar as colunas para a direita / extremidade. stackoverflow.com/a/44353144/4663008 github.com/tidyverse/dplyr/issues/2838 Parece que você precisará usar 2 select () para mover algumas colunas para a extremidade direita e outras para a esquerda.
Arthur Yip
1
A nova função dplyr :: relocate é exatamente para isso. veja a resposta de H 1 abaixo
Arthur Yip
39

Conforme mencionado neste comentário , as sugestões padrão para reordenar colunas em a data.framegeralmente são complicadas e propensas a erros, especialmente se você tiver muitas colunas.

Esta função permite reorganizar as colunas por posição: especifique um nome de variável e a posição desejada e não se preocupe com as outras colunas.

##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)
}

Agora, a solicitação do OP se torna tão simples quanto isto:

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

Para trocar Timee Filescolunas adicionalmente, você pode fazer isso:

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
fonte
Função muito boa. Eu adicionei uma versão modificada dessa função ao meu pacote pessoal .
Deleet 6/07
1
Isto é realmente útil - ele vai me salvar um monte de tempo em que eu só quero mover uma coluna a partir do final realmente uma grande Tibble ao início
Mrmoleje
Uau, eu amo isso.
OfTheAzureSky 19/03
37

Uma dplyrsolução (parte do tidyverseconjunto de pacotes) é usar select:

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

# or

select(table, Time, Out, In, Files)
Ben G
fonte
2
A melhor opção para mim. Mesmo se eu tivesse que instalá-lo, é claramente a possibilidade mais clara.
Garini
15
Tidyverse (dplyr de facto) também tem a opção de seleccionar grupos de colunas de, por exemplo para mover a variável de espécie para a frente: select(iris, Species, everything()). Observe também que aspas não são necessárias.
Paul Rougieux 16/08/19
3
É importante notar que este vai cair todas as colunas que não são especificados explicitamente a menos que você incluir everything()como na de PaulRougieux comentário
divibisan
dplyr's grouptambém irá reorganizar as variáveis, por isso esteja atento ao usar isso em uma cadeia.
David Tonhofer 18/10/19
26

Talvez seja uma coincidência que a ordem das colunas que você deseja tenha nomes de colunas em ordem alfabética decrescente. Como esse é o caso, você pode simplesmente fazer:

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

É isso que eu uso quando tenho arquivos grandes com muitas colunas.

user3482899
fonte
!! WARNING !! data.tablese transforma TARGETem um vetor int: TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)] para consertar: TARGET <- as.data.frame(TARGET) TARGET <- TARGET[ , order(colnames(TARGET), decreasing=TRUE)]
Zachary Ryan Smith
12

As três respostas mais bem avaliadas têm um ponto fraco.

Se o seu quadro de dados se parecer com isso

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

então é uma solução ruim de usar

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

Ele faz o trabalho, mas você acabou de introduzir uma dependência da ordem das colunas em sua entrada.

Esse estilo de programação frágil deve ser evitado.

A nomeação explícita das colunas é uma solução melhor

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

Além disso, se você pretende reutilizar seu código em uma configuração mais geral, pode simplesmente

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

o que também é bastante bom porque isola completamente literais. Por outro lado, se você usar o dplyr'sselect

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

você configuraria aqueles que lerão seu código mais tarde, inclusive a si mesmo, para um pouco de decepção. Os nomes das colunas estão sendo usados ​​como literais sem aparecer no código como tal.

Vrokipal
fonte
3

dplyrA versão 1.0.0inclui a relocate()função para reordenar facilmente as colunas:

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
fonte
2
data.table::setcolorder(table, c("Out", "in", "files"))
Hossein Noorazar
fonte
pls declara a biblioteca da qual você aceita a função setcolorder.
Triamus
1

O único que vi funcionar bem é daqui .

 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
}

Use assim:

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

Funciona como um encanto.

Cibernético
fonte