Method 1

1. This method is for well structred dataset, which has same seperation range length for the certain ranges

2. cut() can help you with range, data.frame(table()) can help you count the frequency

3. Right = TRUE means the lest-side value will not be included, for example: (15-19]

a1 = as.data.frame(table(cut(data, breaks=seq(15, 35, 4), right = TRUE, left = FALSE)))
a1
##      Var1 Freq
## 1 (15,19]  109
## 2 (19,23]   99
## 3 (23,27]  161
## 4 (27,31]  152
## 5 (31,35]  194
a2<-data.frame(sapply(a1,function(x) gsub("\\(|\\]","",gsub("\\,","-",x))))
colnames(a2)<-c("numbers","Freq")
a2
##   numbers Freq
## 1   15-19  109
## 2   19-23   99
## 3   23-27  161
## 4   27-31  152
## 5   31-35  194

Method 2

1. use powerful plyr

2. You can control lower bound and upper bound seperately

3. You can decide how you want the frequency to be calucated

library(plyr)
my.summary1 <- data.frame(low = seq(15, 31,5), 
                          high = seq(19, 35,5))
data1 =adply(my.summary1, 1, transform, freq = sum(data  >= low &
                                                     data <= high))
range = paste0(data1$low, "-", data1$high)
data1 = cbind(range,data1)
data1
##   range low high freq
## 1 15-19  15   19  144
## 2 20-24  20   24  136
## 3 25-29  25   29  198
## 4 30-34  30   34  222