4

I have a dataset which looks like this:

ID  a   b
ID1 0.1 20.3
ID2 0.2 21.6
ID3 1.2 1.5
etc.

I would like to group these values into equal-sized groups. This can be done with:

data$bin1 <- as.numeric(cut2(data$b,g=50))

This takes the values in column b and divides them into 50 equal-sized groups:

ID  a   b    bin
ID1 0.1 20.3 2
ID2 0.2 21.6 2
ID3 1.2 1.5  1
etc.

However, I need to do this multiple times, with different amount of groups. I tried:

for (i in 1:5){
data$bin[i] <- as.numeric(cut2(data$values,g=i*50))
}

But then I get this warning: "number of items to replace is not a multiple of replacement length".

After grouping the values, I want to calculate the means within each group, which can be done with:

means <- ddply(data,.(bin),summarise,mean.a=mean(a),mean.b=mean(b))

I would like to do this for all the bin sizes. Do I need to use another for loop? Or can it be implemented in the first loop?

Henrik
  • 65,555
  • 14
  • 143
  • 159
Hill
  • 89
  • 8

2 Answers2

1
for (i in 1:5){
    data[paste('bin', i, sep = '')] <- as.numeric(cut2(data$values,g=i*50))
}

will add columns bin1 to bin5to you data frame using paste.

germcd
  • 954
  • 1
  • 12
  • 24
1

Here's a possibility where you use lapply to loop over columns in the data frame, and sapply to loop over number of intervals into which the values is to be cut ("n_int"). The resulting list is melted to a long format.

The means are calculated using aggregate, grouped by "L1" (corresponds to columns in the original data), "cut_set" (the set of cuts, 2 or 4 intervals), and "interval" (the interval number).

# some toy data
d1 <- data.frame(a = 1:10,
                 b = seq(100, 1000, len = 10))
d1

# a vector of number of intervals
n_int <- 2 * 1:2

library(reshape2)
d2 <- melt(lapply(d1, function(x){
  data.frame(x, sapply(n_int, function(i){
    as.integer(cut(x, i))
  })
  )
  }),
  id.vars = "x", variable.name = "cut_set", value.name = "interval")

d3 <- aggregate(x ~ L1 + cut_set + interval, data = d2, mean)
d3[order(d3$L1, d3$cut_set, d3$interval), ]
#    L1 cut_set interval     x
# 1   a      X1        1   3.0
# 5   a      X1        2   8.0
# 3   a      X2        1   2.0
# 7   a      X2        2   4.5
# 9   a      X2        3   6.5
# 11  a      X2        4   9.0
# 2   b      X1        1 300.0
# 6   b      X1        2 800.0
# 4   b      X2        1 200.0
# 8   b      X2        2 450.0
# 10  b      X2        3 650.0
# 12  b      X2        4 900.0

Another way using dplyr:

library(dplyr)
d1 %>%
  melt(id.vars = NULL) %>%
  group_by(variable) %>%
  do(data.frame(., sapply(n_int, function(i) as.integer(cut(.$value, i))))) %>%
  melt(id.vars = c("variable", "value"), variable.name = "cut_set", value.name = "interval") %>%
  group_by(variable, cut_set, interval) %>%
  summarise(mean = mean(value)) 
Henrik
  • 65,555
  • 14
  • 143
  • 159