Paul
Paul

Reputation: 97

Group a vector of numbers by range

I have a sorted vector:

c(1, 10, 31, 80, 100, 120, 160)

I would like to split its elements into several groups by range, which I set to 31 in this example.

The result is like this:

[[1]] 1, 10, 31
[[2]] 80, 100
[[3]] 100, 120
[[4]] 160

The ranges in each group is less than 31. I have tried a loop, but I do not like it. Also, I tried the outer function, where I calculated all pairwise differences:

res <- outer(vec, vec, "-")

Then filter each column by the condition > 0 and < 31.

apply(res, 2, function(x) x[x > 0 & x < 31])

The result is not good enough though...

Upvotes: 4

Views: 403

Answers (2)

pseudospin
pseudospin

Reputation: 2777

Here's a neat solution

x <- c(1, 10, 31, 80, 100, 120, 160)
y <- findInterval(x+30, x)
lapply(seq_along(x)[!duplicated(y)], function(z) x[z:y[z]])

#> [[1]]
#> [1]  1 10 31
#> 
#> [[2]]
#> [1]  80 100
#> 
#> [[3]]
#> [1] 100 120
#> 
#> [[4]]
#> [1] 160

Upvotes: 1

AnilGoyal
AnilGoyal

Reputation: 26238

I think this will serve your purpose finally

First list will extract items fulfilling your condition of range, whereas final_list will remove items that are actually contained in some other items.

vec <- c(1, 10, 31, 80, 100, 120, 160)

first_list <- unique(apply(outer(vec, vec, "-"), 1, function(x){vec[(x < 31 & x >= 0)] }))

final_list <- first_list[!sapply(seq_along(first_list), function(i) max(sapply(first_list[-i],function(L) all(first_list[[i]] %in% L))))]

> final_list
[[1]]
[1]  1 10 31

[[2]]
[1]  80 100

[[3]]
[1] 100 120

[[4]]
[1] 160

Upvotes: 1

Related Questions