Christian
Christian

Reputation: 1052

Reorder vector so no certain items are positioned next to each other

Please consider the following example:

[[1]]
[1] 11 12 13 14

[[2]]
[1] 1 2 3

[[3]]
[1] 4

[[4]]
[1] 5

[[5]]
[1] 6

[[6]]
[1] 7

[[7]]
[1] 8

[[8]]
[1] 9

[[9]]
[1] 10

[[10]]
[1] 15

[[11]]
[1] 16

[[12]]
[1] 17

In this example, I have 12 unique values in a vector that is 17 elements long. For simplicity, let's say that this vector is:

foo_bar <- c("b","b","b","c","d","e","f","g","h","i","a","a","a","a", "j", "k", "l")

The first code block shows the index positions in foo_bar of each of the unique values (the letters a–l).

I am attempting to write an algorithm that reorders foo_bar so that, for all indices except the final one (index 17 in the foo_bar example), position i and position i+1 never contains the same two values. Here's an example of what would be an appropriate outcome:

reordered_foo_bar <- c("b","c","b","d","b","e","f","g","h","a","i","a","j","a","k","a", "l")

Upvotes: 0

Views: 33

Answers (2)

Christian
Christian

Reputation: 1052

First we identify the indices of the unique values in the vector.

indices <- 
  unique(foo_bar) %>% 
  sort() %>% 
  lapply(function(x) which(foo_bar == x))

Then we create a position score based on 1) which order the value has when ordered by decreasing frequency and 2) how many previous occurences of this value has occurred, and we add these two values together. However, to ensure that we get a different value inserted between them, we divide 2) by 2. Finally, we order the position scores and reorder foo_bar with this new order.

This solution is also robust in case it is not possible to prevent duplicate values next to each other (for example because the values are c("a","a","b","a").

out <- 
  lengths(indices) %>% 
  lapply(., function(x) 1:x) %>%
  {lapply(len_seq(.), function(x) (unlist(.[x]) + x / 2))} %>% 
  unlist() %>% 
  order() %>% 
  {unlist(indices)[.]} %>% 
  foo_bar[.]

The output is then:

> "a" "b" "a" "c" "b" "d" "a" "e" "b" "f" "a" "g" "h" "i" "j" "k" "l"

Upvotes: 0

Wimpel
Wimpel

Reputation: 27742

something like this?

foo_bar <- c("b","b","b","c","d","e","f","g","h","i","a","a","a","a", "j", "k", "l")

test == FALSE
while (test == FALSE) {
  new_foo_bar <- sample(foo_bar, size = length(foo_bar), replace = FALSE)
  test <- length(rle(new_foo_bar)$lengths) == length(foo_bar)
}

new_foo_bar
# [1] "f" "a" "g" "b" "h" "d" "j" "c" "e" "i" "a" "b" "k" "a" "l" "a" "b"

Upvotes: 1

Related Questions