Give fixed order for columns based on column names for different dfs

Viewed 31

I have multiple dataframes that all contain one or more of the columns dogs, cats, birds or fish. For all the dataframes I would like to have the order fish, cats, dogs, birds for the columns.

df <- data.frame(dogs = c(1:2), cats = c(33:34), birds = c(2:3), fish = c(7:8))
  dogs cats birds fish
1    1   33     2    7
2    2   34     3    8

df2 <- data.frame(dogs = c(5:6), cats = c(103:104), birds = c(4:5))
  dogs cats birds
1    5  103     4
2    6  104     5

I would like to get

#order fish, cats, dogs, birds
df
  fish cats dogs birds
1    7   33    1     2
2    8   34    2     3

df2
  cats dogs birds
1  103    5     4
2  104    6     5

Right now I'm changing the column order for all dataframes manually (df[,c(4,2,1,3)] etc) and can't seem to find a way to solve this otherwise. Any suggestions? Thanks in advance!

3 Answers

Easier option in base R is match to get the index, specify the nomatch = 0 so that if there are columns that are not matching, it will not go as NA. Here, we create a function that be used for multiple datasets

f1 <- function(dat, names_ord) {
      dat[match(names_ord, names(dat), nomatch = 0)]
 }


nm1 <- c('fish', 'cats', 'dogs', 'birds')
f1(df2, nm1)    
#    cats dogs birds
#1  103    5     4
#2  104    6     5

f1(df, nm1)
#  fish cats dogs birds
#1    7   33    1     2
#2    8   34    2     3

Or it can be applied at once in a list of datasets

lapply(list(df, df2), f1, names_ord = nm1)

One dplyr option could be:

df %>%
 select(any_of(c("fish", "cats", "dogs", "birds")))

  fish cats dogs birds
1    7   33    1     2
2    8   34    2     3

df2 %>%
 select(any_of(c("fish", "cats", "dogs", "birds")))

  cats dogs birds
1  103    5     4
2  104    6     5

If you have many of such datasets, then with the addition of purrr, you can do:

map(.x = list(df, df2),
    ~ .x %>%
     select(any_of(c("fish", "cats", "dogs", "birds"))))

[[1]]
  fish cats dogs birds
1    7   33    1     2
2    8   34    2     3

[[2]]
  cats dogs birds
1  103    5     4
2  104    6     5

Maybe you can try match + na.omit like below

> nmord <- c("fish", "cats", "dogs", "birds")

> df[na.omit(match(nmord, names(df)))]
  fish cats dogs birds
1    7   33    1     2
2    8   34    2     3

> df2[na.omit(match(nmord, names(df2)))]
  cats dogs birds
1  103    5     4
2  104    6     5
Related