richardzhang
richardzhang

Reputation: 71

How to split a table by one column in R?

If I have a table like this:

Sam   55
Toby  30
Fred  45
gdgv  63
Wed   19
Bill  26
....

I want to split this table to several tables based on the value(>40, (30,40), <30), which means they will look like this:
table 1:

Sam  55
Fred 45
gdgv 63

table 2:

Toby 30

table 3:

Bill  19
Wed   26

how can I do this without a for loop?

Upvotes: 1

Views: 240

Answers (5)

moodymudskipper
moodymudskipper

Reputation: 47300

borrowing data from @markus :

split(dat, (dat$V2 >= 30) + (dat$V2 > 40))
# $`0`
#     V1 V2
# 5  Wed 19
# 6 Bill 26
# 
# $`1`
#     V1 V2
# 2 Toby 30
# 
# $`2`
#     V1 V2
# 1  Sam 55
# 3 Fred 45
# 4 gdgv 63

Upvotes: 0

Pierre Lapointe
Pierre Lapointe

Reputation: 16277

If you do not want to use the split and cut method from @markus, which gives a list as a result, and simply want to create 3 separate objects, you can simply do this.

df<-read.table(text="Name Number
                Sam   55
                Toby  30
                Fred  45
                gdgv  63
                Wed   19
                Bill  26",header=T)
table1 <- df[df$Number>40,]
table2 <- df[df$Number>=30 & df$Number<=40,]
table3 <- df[df$Number<30,]

Upvotes: 0

tmfmnk
tmfmnk

Reputation: 39858

With dplyr you can try something like this:

df %>%
 group_split(grp = ifelse(V2 > 40, 1,
                          ifelse(V2 >= 30 & V2 <= 40, 2, 3)))

[[1]]
# A tibble: 3 x 3
  V1       V2   grp
  <chr> <int> <dbl>
1 Sam      55     1
2 Fred     45     1
3 gdgv     63     1

[[2]]
# A tibble: 1 x 3
  V1       V2   grp
  <chr> <int> <dbl>
1 Toby     30     2

[[3]]
# A tibble: 2 x 3
  V1       V2   grp
  <chr> <int> <dbl>
1 Wed      19     3
2 Bill     26     3

Or the same idea using case_when() instead of ifelse():

df %>%
 group_split(grp = case_when(V2 > 40 ~ 1,
                             V2 >= 30 & V2 <= 40 ~ 2,
                             TRUE ~ 3))

Or with cut():

df %>%
 group_split(grp = cut(V2,
                       breaks = c(-Inf, 30, 40, Inf),
                       labels = c("<30", "(30,40)", ">40"),
                       right = FALSE)) 

[[1]]
# A tibble: 2 x 3
  V1       V2 grp  
  <chr> <int> <fct>
1 Wed      19 <30  
2 Bill     26 <30  

[[2]]
# A tibble: 1 x 3
  V1       V2 grp    
  <chr> <int> <fct>  
1 Toby     30 (30,40)

[[3]]
# A tibble: 3 x 3
  V1       V2 grp  
  <chr> <int> <fct>
1 Sam      55 >40  
2 Fred     45 >40  
3 gdgv     63 >40 

Upvotes: 3

NelsonGon
NelsonGon

Reputation: 13309

Using plyr(data in Note below):

plyr::llply(split(df2,df2$Number<30),function(x) split(x,x$Number>30))

Result:

$`FALSE`
$`FALSE`$`FALSE`
  Name Number
2 Toby     30

$`FALSE`$`TRUE`
  Name Number
1  Sam     55
3 Fred     45
4 gdgv     63


$`TRUE`
$`TRUE`$`FALSE`
  Name Number
5  Wed     19
6 Bill     26

NOTE::

df2<-read.table(text="Name Number
                Sam   55
Toby  30
                Fred  45
                gdgv  63
                Wed   19
                Bill  26",header=T)

Upvotes: 0

markus
markus

Reputation: 26343

You can use split and cut

split(dat,
      cut(dat$V2,
          breaks = c(-Inf, 30, 40, Inf),
          labels = c("<30", "(30,40)", ">40"),
          right = FALSE))
#$`<30`
#    V1 V2
#5  Wed 19
#6 Bill 26

#$`(30,40)`
#    V1 V2
#2 Toby 30

#$`>40`
#    V1 V2
#1  Sam 55
#3 Fred 45
#4 gdgv 63

data

text <- "Sam   55
Toby  30
Fred  45
gdgv  63
Wed   19
Bill  26"

dat <- read.table(text = text, stringsAsFactors = FALSE, header = FALSE)

Upvotes: 5

Related Questions