---
title: "Plyr: `a*ply()` and `l*aply()`"
author: "Statistical Computing, 36-350"
date: "Wednesday November 9, 2016"
---

Reminder: iterating in R without `for()`
===

We've learned some tools in R for iteration without explicit `for()` loops:

- Indexing with conditionals + vectorization 
- `apply()`: apply a function to rows or columns of a matrix or data frame
- `lapply()`: apply a function to elements of a list or vector
- `sapply()`: same as the above, but simplify the output (if possible)
- `tapply()`: apply a function to levels of a factor vector

Clever indexing + vectorization is always useful, when possible

The `apply()` family is often useful, but it has some issues: primarily, inconsistent output

The `plyr` package
===

Most popular R package of all time (most downloads): `plyr`

This is true for a good reason! Provides us with an extremely useful family of apply-like functions. Advantage over the built-in `apply()` family is its consistency

All `plyr` functions are of the form `**ply()`. Replace `**` with characters denoting types:

- First character: input type, one of `a`, `d`, `l` 
- Second character: output type, one of `a`, `d`, `l`, or `_` (drop)

`a*ply()`: the input is an array
===

The signature for all `a*ply()` functions is:

```
a*ply(.data, .margins, .fun, ...)
```

- `.data` : an array
- `.margins` : index (or indices) to split the array by
- `.fun` : the function to be applied to each piece
- `...` : additional arguments to be passed to the function

Note that this looks like:

```
apply(X, MARGIN, FUN, ...)
```

Examples
===

```{r}
my.array = array(1:27, c(3,3,3))
rownames(my.array) = c("Curly", "Larry", "Moe")
colnames(my.array) = c("Groucho", "Harpo", "Zeppo")
dimnames(my.array)[[3]] = c("Bart", "Lisa", "Maggie")
my.array
```

(Continued)
===

```{r}
library(plyr)
aaply(my.array, 1, sum) # Get back an array
adply(my.array, 1, sum) # Get back a data frame
alply(my.array, 1, sum) # Get back a list
```

(Continued)
===

```{r}
aaply(my.array, 2:3, sum) # Get back a 3 x 3 array
adply(my.array, 2:3, sum) # Get back a data frame
alply(my.array, 2:3, sum) # Get back a list
```

`l*ply()` : the input is a list
===

The signature for all `l*ply()` functions is:

```
l*ply(.data, .fun, ...)
```

- `.data` : a list
- `.fun` : the function to be applied to each element
- `...` : additional arguments to be passed to the function

Note that this looks like:

```
lapply(X, FUN, ...)
```

Examples
===

```{r}
my.list = list(nums=rnorm(1000), lets=letters, pops=state.x77[,"Population"])
laply(my.list, range) # Get back an array
ldply(my.list, range) # Get back a data frame
llply(my.list, range) # Get back a list
```

(Continued)
===

```{r, error=TRUE}
laply(my.list, summary) # Doesn't work! Outputs have different types/lengths
ldply(my.list, summary) # Doesn't work! Outputs have different types/lengths
llply(my.list, summary) # Works just fine
```

The fourth option for `*`
===

The fourth option for `*` is `_`: the function `a_ply()` (or `l*ply()`) has no explicit return object, but still runs the given function over the given array (or list), possibly producing side effects

```{r}
par(mfrow=c(3,3), mar=c(4,4,1,1))
a_ply(my.array, 2:3, plot, ylim=range(my.array), pch=19, col=6)
```