Biostatician
Biostatician

Reputation: 111

Sorting rows into non-alphabetical customized order

I have the following df, where: Name displays the concept to pay for and Code the number of times Name must be repeated in order to optimize processes. Initially it looks like this:

Name    Code
Tax      1
Gas      2
Tax      1
Gas      2
Tax      1
Lunch    2
Tax      1
Car      2

df = data.frame(Name = c('Tax', 'Gas', 'Tax', 'Gas', 'Tax', 'Lunch', 'Tax', 'Car'), Code = rep(c(1,2)))

After repeating Gas, Lunch and Car twice, it looks like this:

Name    Code
Tax      1
Gas      2
Gas      2
Tax      1
Gas      2
Gas      2
Tax      1
Lunch    2
Lunch    2
Tax      1
Car      2
Car      2

df = df[rep(seq_len(nrow(df)), df$Code),]

What I'd like to do is to reorder the rows to make my df look like this, where df is just sorted by Code in this manner 2, 1, 2 (each Tax and Gas, Tax and Lunch, Tax and Car are one group):

Is there a way to do so without any libraries? Since the final script will not be used just for me but also for r newbies at work.

I already tried with: df[order(factor(df$Code, levels = c(2,1,2))),] and also adding row.names as column to match like this: df[order(df$Code, df$row.names),] but it does not seem to work as expected. Also tried with this and this answers but could not succeed.

Upvotes: 1

Views: 283

Answers (2)

akrun
akrun

Reputation: 887981

We can do this in base R

i1 <- with(df, ave(seq_len(nrow(df)), as.integer(gl(nrow(df), 3, 
         nrow(df))), FUN = function(i) c(i[c(2, 1, 3)])))
out <- df[i1,]
row.names(out) <- NULL
out
#    Name Code
#1    Gas    2
#2    Tax    1
#3    Gas    2
#4    Gas    2
#5    Tax    1
#6    Gas    2
#7  Lunch    2
#8    Tax    1
#9  Lunch    2
#10   Car    2
#11   Tax    1
#12   Car    2

Or with tidyverse

library(tidyverse)
df %>% # initial dataset
   uncount(Code, .remove = FALSE) %>%
   mutate(rn = row_number()) %>% 
   group_by(grp = gl(n(), 3, n())) %>%
   slice(c(2, 1, 3)) %>% 
   ungroup %>% 
   select(-rn, -grp)

Upvotes: 1

Philippe Poirier
Philippe Poirier

Reputation: 56

This works perfectly, assuming I understood the logic behind your sorting.

df = data.frame(Name = c('Tax', 'Gas', 'Tax', 'Gas', 'Tax', 'Lunch', 'Tax', 'Car'),
                Code = rep(c(1,2)))
df = df[rep(seq_len(nrow(df)), df$Code),]
vect<-c()
for (i in 1:nrow(df)){
  if ((i+1)%%3==0)
      vect<-c(vect,i,i-1,i+1)
}
df<-df[vect,] 
rownames(df)<-NULL

Upvotes: 1

Related Questions