Mel
Mel

Reputation: 118

Unexpected result with lapply when using custom function

Assume that I have three data, each containing random 20 numbers:

> dataone
 [1]  5.7 15.3 14.0 13.2 14.0  9.4 19.0 13.7 19.4 19.3 11.5 16.6 17.7  3.2  4.7  1.4 18.4 10.1  9.6 16.4
> datatwo
 [1] 21 13 29 29 22 13 18 13 29 13 23 17 17 11 29 15 20 21 26 17
> datathree
 [1]  6.7  9.8  1.9  5.9  5.3  5.2  1.2  5.3  2.2  8.6  2.9  4.6  4.9  6.1  9.4 10.0  5.1  9.8  3.1  3.2

I want to calculate means for every four numbers in order. To be clear, I want means of [1:4], [5:8], [9:12] and so on, for each data. So, I wrote this function:

foursmean <- function (x) {
  starts <- seq(1, 20, by = 4) 
  means <- numeric(length(starts))
  for (i in 1:length(starts)){
    a <- mean(x[starts[i]:(starts[i]+3)] )
    means[i] <- a  }
  print(means)
}

It works pretty well so far:

> foursmean(dataone)
[1] 12.1 14.0 16.7  6.7 13.6

But, I don't want to spend time to use function for all data. So I collect them in a single list:

dataall <- list(dataone, datatwo, datathree)

Then I use lapply() to run function on a list:

> lapply(dataall, foursmean)
[1] 12.1 14.0 16.7  6.7 13.6
[1] 23 16 21 18 21
[1] 6.0 4.2 4.6 7.6 5.3
[[1]]
[1] 12.1 14.0 16.7  6.7 13.6

[[2]]
[1] 23 16 21 18 21

[[3]]
[1] 6.0 4.2 4.6 7.6 5.3

I don't understand why it gives duplicate results. When I checked the structure, it says "List of 3 of List of 5s". I couldn't figure it out, couldn't solve it.

I was expecting only this part:

[[1]]
[1] 12.1 14.0 16.7  6.7 13.6

[[2]]
[1] 23 16 21 18 21

[[3]]
[1] 6.0 4.2 4.6 7.6 5.3

Upvotes: 3

Views: 75

Answers (1)

Sathish
Sathish

Reputation: 12723

You have to remove print in your code.

May be you could try this form of your function.

Data:

dataone <-   c( 5.7, 15.3, 14.0, 13.2 ,14.0,  9.4 ,19.0 ,13.7, 19.4, 19.3 ,11.5, 16.6, 17.7,  3.2 , 4.7,  1.4, 18.4, 10.1,  9.6, 16.4 )
datatwo <- c( 21, 13, 29 ,29 ,22 ,13 ,18 ,13 ,29 ,13, 23, 17 ,17 ,11, 29, 15, 20, 21 ,26, 17)
datathree <- c( 6.7,  9.8 , 1.9,  5.9,  5.3,  5.2  ,1.2  ,5.3 , 2.2,  8.6 , 2.9,  4.6 , 4.9 , 6.1,  9.4, 10.0 , 5.1 , 9.8 , 3.1,  3.2)
dataall <- list(dataone = dataone, 
                datatwo = datatwo, 
                datathree = datathree )

Code:

foursmean <- function (x, by ) {
  x <- split( x = x, f = ceiling( seq_along(x) / by) )
  sapply( x, mean, na.rm = TRUE )
}

Output: by specifies how much you want split your vector. You can have any positive number

sapply(dataall, foursmean, by = 4)
#   dataone datatwo datathree
# 1  12.050    23.0     6.075
# 2  14.025    16.5     4.250
# 3  16.700    20.5     4.575
# 4   6.750    18.0     7.600
# 5  13.625    21.0     5.300

sapply(dataall, foursmean, by = 5)
#   dataone datatwo datathree
# 1   12.44    22.8      5.92
# 2   16.16    17.2      4.50
# 3   10.74    19.4      5.58
# 4   11.18    19.8      6.24

Upvotes: 1

Related Questions