How to split a dataframe into a list of dataframes based on distinct value ranges

Viewed 87

I want to split a dataframe into a list of dataframes based on distinct ranges of a numeric variable.

ILLUSTRATIVE DATA:

set.seed(123)
df <- data.frame(
  subject = LETTERS[1:10],
  weight = sample(1:1000, 10)
)
df
   subject weight
1        A    288
2        B    788
3        C    409
4        D    881
5        E    937
6        F     46
7        G    525
8        H    887
9        I    548
10       J    453

I'd like to have a list of 4 smaller dataframes based on these limits of the variable weight:

limits <- c(250, 500, 750, 1000)

That is, what I'm after, in the list of dataframes, is one dataframe where weight is in the range of 0-250, another where weight ranges between 251-500, another where the range is from 501-750, and so on--in other words, the ranges are distinct.

What I've tried so far is this dyplr solution, which outputs a list of 5 dataframes but with cumulative ranges:

 limits <- c(250, 500, 750, 1000)
 lapply(limits, function(x) {df %>% filter(weight <= x)})
[[1]]
[1] subject weight 
<0 rows> (or 0-length row.names)

[[2]]
  subject weight
1       F     46

[[3]]
  subject weight
1       A    288
2       C    409
3       F     46
4       J    453

[[4]]
  subject weight
1       A    288
2       C    409
3       F     46
4       G    525
5       I    548
6       J    453

[[5]]
   subject weight
1        A    288
2        B    788
3        C    409
4        D    881
5        E    937
6        F     46
7        G    525
8        H    887
9        I    548
10       J    453 

How could this code be fixed, or which other code can be used, so that a list of dataframes is obtained based on distinct weight ranges?

2 Answers

Perhaps:

library(dplyr)

df %>%
  group_split(group = findInterval(weight, limits))

Output:

[4]>
[[1]]
# A tibble: 4 x 3
  subject weight group
  <fct>    <int> <int>
1 C          179     0
2 E          195     0
3 H          118     0
4 J          229     0

[[2]]
# A tibble: 3 x 3
  subject weight group
  <fct>    <int> <int>
1 A          415     1
2 B          463     1
3 I          299     1

[[3]]
# A tibble: 1 x 3
  subject weight group
  <fct>    <int> <int>
1 D          526     2

[[4]]
# A tibble: 2 x 3
  subject weight group
  <fct>    <int> <int>
1 F          938     3
2 G          818     3

Just use keep = FALSE as additional argument to group_split if you want to remove the group column in your output.

A base R one-liner can split the data by limits.

split(df, findInterval(df$weight, limits))
#$`0`
#   subject weight
#3        C    179
#5        E    195
#8        H    118
#10       J    229
#
#$`1`
#  subject weight
#1       A    415
#2       B    463
#9       I    299
#
#$`2`
#  subject weight
#4       D    526
#
#$`3`
#  subject weight
#6       F    938
#7       G    818
Related