Rotail
Rotail

Reputation: 1061

How to randomise a vector and keep the frequency of the elements fixed?

Extending this former question, how can I shuffle (randomize) the following vector

a1 = c(1, 1, 2, 2, 2, 2, 3, 3, 4, 5, 5, 5) 

in order to get something like this:

a2 = c(5, 5, 3, 3, 3, 3, 1, 1, 2, 4, 4, 4)

or even better like this:

a3 = c(4, 4, 4, 2, 3, 3, 3, 3, 1, 1, 5, 5)? 

such that each element could randomly change to another but with keeping the number of each element constant?

Upvotes: 1

Views: 120

Answers (3)

IRTFM
IRTFM

Reputation: 263301

Seems like a perfect application for rle and its inverse rep:

rand_inverse_rle <- function(x) { x=sort(x)
   ord=sample (length(rle(x)$values) )  
   unlist( mapply( rep, rle(x)$values[ord], rle(x)$lengths[ord]))}
rand_inverse_rle(a1)
#----------
 [1] 3 3 4 5 5 5 2 2 2 2 1 1

This was my reading of a function needed to satisfy the natural language requirements:

>  a1 = sample( c(1, 1, 2, 2, 2, 2, 3, 3, 4, 5, 5, 5) )
> a1
 [1] 5 2 5 2 5 1 3 4 2 2 3 1
> rand_inverse_rle(a1)
 [1] 5 5 5 4 2 2 2 2 3 3 1 1
> rand_inverse_rle(a1)
 [1] 1 1 3 3 5 5 5 2 2 2 2 4
> rand_inverse_rle(a1)
 [1] 1 1 3 3 4 5 5 5 2 2 2 2

Upvotes: 3

Sven Hohenstein
Sven Hohenstein

Reputation: 81683

The data:

a1 <- c(1, 1, 2, 2, 2, 2, 3, 3, 4, 5, 5, 5)

First steps:

# extract values and their frequencies
val <- unique(a1)

tab <- table(a1)
freq <- tab[as.character(val)]

Keep original order of frequencies but sample values

rep(sample(val), freq)
# [1] 4 4 1 1 1 1 3 3 5 2 2 2

Keep original frequencies but sample order of values

rep(sa <- sample(val), freq[as.character(sa)])
# [1] 4 2 2 2 2 3 3 1 1 5 5 5

Upvotes: 3

akuiper
akuiper

Reputation: 214927

You can try something like this: create a factor from a1 with randomly shuffled levels and then convert it to integers:

as.integer(factor(a1, levels = sample(unique(a1), length(unique(a1)))))
# [1] 5 5 4 4 4 4 3 3 2 1 1 1

Upvotes: 4

Related Questions