---
title: "More on Apply"
author: "Statistical Computing, 36-350"
date: "Friday October 14, 2016"
---

`lapply()`, elements of a list or vector
===

The `lapply()` function takes inputs as in: `lapply(x, FUN=my.fun)`, to apply `my.fun()` across elements of a list or vector `x`. The output is a list

```{r}
my.list = list(nums=seq(0.1,0.6,by=0.1), chars=letters[1:12], 
               bools=sample(c(TRUE,FALSE), 6, replace=TRUE))
my.list

lapply(my.list, FUN=mean) # Warning, because mean() can't be applied to chars
```

The return value is always a list
===

With `lapply()` and say, `FUN=my.fun`, we'll get back a list, no matter the output type of `my.fun()`

```{r}
lapply(my.list, FUN=summary)
```

Custom functions, "on-the-fly" functions, extra arguments
===

These all work like they do in `apply()`. E.g., can use: `lapply(x, FUN=my.fun, extra.arg.1, extra.arg.2)`, for two extra arguments `extra.arg.1`, `extra.arg.2` to be passed to `my.fun()`

```{r}
# How to compute the leave-one-out means, also called jackknife means,
# without a for() loop?
mean.omitting.one = function(i, vec) { return(mean(vec[-i])) }
my.vec = state.x77[,"Frost"] # Frost variable in the states data
n = length(my.vec)
my.vec.jack = lapply(1:n, FUN=mean.omitting.one, vec=my.vec)
head(my.vec.jack) # It's a list, and here are the first 5 elements
```

`sapply()`, elements of a list or vector
===

The `sapply()` function works just like `lapply()`, but tries to **simplify** the return value whenever possible. E.g., most common is the conversion from a list to a vector

```{r}
my.vec.jack = sapply(1:n, FUN=mean.omitting.one, vec=my.vec)
head(my.vec.jack) # Now this is a vector, with same elements as before
sqrt((n-1)^2/n) * sd(my.vec.jack) # Jackknife standard error
sd(my.vec)/sqrt(n) # Compare to "usual" standard error of the mean
```

`tapply()`, levels of a factor vector
===

The function `tapply()` takes inputs as in: `tapply(x, INDEX=my.index, FUN=my.fun)`, to apply `my.fun()` to subsets of entries in `x` that share a common level in `my.index`

```{r}
head(state.x77) # Matrix of states data, 50 states x 8 variables
head(state.region) # Factor of regions for the 50 states
# Now, let's average the Frost variable, within in each region
tapply(state.x77[,"Frost"], INDEX=state.region, FUN=mean)
```

`split()`, split by levels of a factor
===

Sometimes we want to split up the rows of a data frame or entries of a vector by levels of a factor. The function `split()` does this, as in: `split(x, f=my.index)` to split a data frame or vector `x` according to levels of `my.index`

```{r}
# Let's split up the state.x77 matrix according to region
state.x77.df = data.frame(state.x77) # First cast into a data frame
state.x77.by.reg = split(state.x77.df, f=state.region) # Now split
class(state.x77.by.reg) # The result is a list
names(state.x77.by.reg) # This has 4 elements for the 4 regions
class(state.x77.by.reg[[1]]) # Each element is a data frame
```

(Continued)
===

```{r}
# For each region, display the first 3 rows of the data frame
lapply(state.x77.by.reg, FUN=head, 3) 
```

(Continued)
===

```{r}
# For each region, average each of the 8 numeric variables
lapply(state.x77.by.reg, FUN=function(df) { apply(df, MARGIN=2, mean) })
```