```{R, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
library(zeallot)
## Getting Started The *zeallot* package defines an operator for *unpacking assignment*, sometimes called *parallel assignment* or *destructuring assignment* in other programming languages. The operator is written as `%<-%` and used like this. ```r c(lat, lng) %<-% list(38.061944, -122.643889)
The result is that the list is unpacked into its elements,
and the elements are assigned to lat
and lng
.
lat lng
You can also unpack the elements of a vector.
c(lat, lng) %<-% c(38.061944, -122.643889) lat lng
You can unpack much longer structures, too, of course, such as the 6-part summary of a vector.
c(min_wt, q1_wt, med_wt, mean_wt, q3_wt, max_wt) %<-% summary(mtcars$wt) min_wt q1_wt med_wt mean_wt q3_wt max_wt
If the left-hand side and right-hand sides do not match, an error is raised. This guards against missing or unexpected values.
c(stg1, stg2, stg3) %<-% list("Moe", "Donald")
c(stg1, stg2, stg3) %<-% list("Moe", "Larry", "Curley", "Donald")
A common use-case is when a function returns a list of values
and you want to extract the individual values.
In this example, the list of values returned by coords_list()
is unpacked
into the variables lat
and lng
.
# # A function which returns a list of 2 numeric values. # coords_list <- function() { list(38.061944, -122.643889) } c(lat, lng) %<-% coords_list() lat lng
In this next example, we call a function that returns a vector.
# # Convert cartesian coordinates to polar # to_polar = function(x, y) { c(sqrt(x^2 + y^2), atan(y / x)) } c(radius, angle) %<-% to_polar(12, 5) radius angle
You can directly unpack the coefficients of a simple linear regression into the intercept and slope.
c(inter, slope) %<-% coef(lm(mpg ~ cyl, data = mtcars)) inter slope
safely
The purrr package includes the safely
function.
It wraps a given function to create a new, "safe" version of the original function.
```{R, eval = require("purrr")} safe_log <- purrr::safely(log)
The safe version returns a list of two items. The first item is the result of calling the original function, assuming no error occurred; or `NULL` if an error did occur. The second item is the error, if an error occurred; or `NULL` if no error occurred. Whether or not the original function would have thrown an error, the safe version will never throw an error. ```r pair <- safe_log(10) pair$result pair$error
pair <- safe_log("donald") pair$result pair$error
You can tighten and clarify calls to the safe function by using %<-%
.
c(res, err) %<-% safe_log(10) res err
A data frame is simply a list of columns, so the zeallot assignment does what you expect. It unpacks the data frame into individual columns.
c(mpg, cyl, disp, hp) %<-% mtcars[, 1:4] head(mpg) head(cyl) head(disp) head(hp)
Bear in mind that a list of data frames is still just a list. The assignment will extract the list elements (which are data frames) but not unpack the data frames themselves.
quartet <- lapply(1:4, function(i) anscombe[, c(i, i + 4)]) c(an1, an2, an3, an4) %<-% lapply(quartet, head, n = 3) an1 an2 an3 an4
The %<-%
operator assigned four data frames to four variables, leaving the
data frames intact.
In addition to unpacking flat lists, you can unpack lists of lists.
c(a, c(b, d), e) %<-% list("begin", list("middle1", "middle2"), "end") a b d e
Not only does this simplify extracting individual elements, it also adds a level of checking. If the described list structure does not match the actual list structure, an error is raised.
c(a, c(b, d, e), f) %<-% list("begin", list("middle1", "middle2"), "end")
The previous examples dealt with unpacking a list or vector into its elements. You can also split certain kinds of individual values into subvalues.
You can assign individual characters of a string to variables.
c(ch1, ch2, ch3) %<-% "abc" ch1 ch2 ch3
You can split a Date into its year, month, and day, and assign the parts to variables.
c(y, m, d) %<-% Sys.Date() y m d
zeallot includes implementations of destructure
for character strings,
complex numbers, data frames, date objects, and linear model summaries.
However, because destructure
is a generic function, you can define new
implementations for custom classes. When defining a new implementation keep in
mind the implementation needs to return a list so that values are properly
unpacked.
In some cases, you want the first few elements of a list or vector but do not
care about the trailing elements. The summary
function of lm
, for example,
returns a list of 11 values, and you might want only the first few. Fortunately,
there is a way to capture those first few and say "don't worry about everything
else".
f <- lm(mpg ~ cyl, data = mtcars) c(fcall, fterms, resids, ...rest) %<-% summary(f) fcall fterms head(resids)
Here, rest
will capture everything else.
str(rest)
The assignment operator noticed that ...rest
is prefixed with ...
, and it
created a variable called rest
for the trailing values of the list. If you
omitted the "everything else" prefix, there would be an error because the
lengths of the left- and right-hand sides of the assignment would be mismatched.
c(fcall, fterms, resids, rest) %<-% summary(f)
If multiple collector variables are specified at a particular depth it is ambiguous which values to assign to which collector and an error will be raised.
In addition to collecting trailing values, you can also collect initial values and assign specific remaining values.
c(...skip, e, f) %<-% list(1, 2, 3, 4, 5) skip e f
Or you can assign the first value, skip values, and then assign the last value.
c(begin, ...middle, end) %<-% list(1, 2, 3, 4, 5) begin middle end
You can skip one or more values without raising an error by using a period (.
)
instead of a variable name. For example, you might care only about the min,
mean, and max values of a vector's summary
.
c(min_wt, ., ., mean_wt, ., max_wt) %<-% summary(mtcars$wt) min_wt mean_wt max_wt
By combining an anonymous element (.
) with the collector prefix, (...
), you
can ignore whole sublists.
c(begin, ..., end) %<-% list("hello", "blah", list("blah"), "blah", "world!") begin end
You can mix periods and collectors together to selectively keep and discard elements.
c(begin, ., ...middle, end) %<-% as.list(1:5) begin middle end
It is important to note that although value(s) are skipped they are still expected. The next section touches on how to handle missing values.
You can specify a default value for a left-hand side variable using =
, similar
to specifying the default value of a function argument. This comes in handy when
the number of elements returned by a function cannot be guaranteed. tail
for
example may return fewer elements than asked for.
nums <- 1:2 c(x, y) %<-% tail(nums, 2) x y
However, if we tried to get 3 elements and assign them an error would be raised
because tail(nums, 3)
still returns only 2 values.
c(x, y, z) %<-% tail(nums, 3)
We can fix the problem and resolve the error by specifying a default value for
z
.
c(x, y, z = NULL) %<-% tail(nums, 3) x y z
A handy trick is swapping values without the use of a temporary variable.
c(first, last) %<-% c("Ai", "Genly") first last c(first, last) %<-% c(last, first) first last
or
cat <- "meow" dog <- "bark" c(cat, dog, fish) %<-% c(dog, cat, dog) cat dog fish
The magrittr
package provides a pipe operator %>%
which allows functions
to be called in succession instead of nested. The left operator %<-%
does not work well with these function chains. Instead, the right operator
%->%
is recommended. The below example is adapted from the magrittr
readme.
library(magrittr) mtcars %>% subset(hp > 100) %>% aggregate(. ~ cyl, data = ., FUN = . %>% mean() %>% round(2)) %>% transform(kpl = mpg %>% multiply_by(0.4251)) %->% c(cyl, mpg, ...rest) cyl mpg rest
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.