--- title: "Labelr - Special Topics" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Labelr - Special Topics} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, error = TRUE, purl = FALSE, comment = "### >" ) ``` ## Overview While the Introductory vignette gives an overview of core labelr functions, here, we offer an ad hoc dive into a range of miscellaneous special topics and additional functionalities. Let's go. ## Larger Data Frames labelr is not intended for "large" data.frames, which is a fuzzy concept. To give a sense of what labelr **can** handle, let's see it in action with the NYC Flights 2013 data set: a moderate-not-big data.frame of ~340K rows. Let's load labelr and the nycflights13 package. ```{r} opening_ding <- Sys.time() # to time labelr library(labelr) library(nycflights13) ``` We'll assign the data.frame to one we call df. ```{r} df <- flights nrow(df) ``` We'll add a "frame label," which describes the data.frame overall. ```{r} df <- add_frame_lab(df, frame.lab = "On-time data for all flights that departed NYC (i.e. JFK, LGA or EWR) in 2013.") ``` Note that the source data.frame (`nycflights13::flights`) is a tibble. The labelr package coerces augmented data.frames, such as tibbles and data.tables, into "pure" Base R data.frames -- and alerts you that it has done so. The intent is to avoid the dependencies, errors, or inconsistent and unpredictable behaviors that might result from labelr trying to integrate with or make sense of these or other competing, alternative data.frame constructs, which (a) by design behave differently from standard R data.frames in various subtle or not-so-subtle ways and which (b) may continue to evolve in the future. Let's see what this did. ```{r} attr(df, "frame.lab") # check for attribute get_frame_lab(df) # return frame.lab alongside data.frame name as a data.frame get_frame_lab(df)$frame.lab ``` Now, let's assign variable NAME labels. ```{r} names_labs_vec <- c( "year" = "Year of departure", "month" = "Month of departure", "year" = "Day of departure", "dep_time" = "Actual departure time (format HHMM or HMM), local tz", "arr_time" = "Actual arrival time (format HHMM or HMM), local tz", "sched_dep_time" = "Scheduled departure times (format HHMM or HMM)", "sched_arr_time" = "Scheduled arrival time (format HHMM or HMM)", "dep_delay" = "Departure delays, in minutes", "arr_delay" = "Arrival delays, in minutes", "carrier" = "Two letter airline carrier abbreviation", "flight" = "Flight number", "tailnum" = "Plane tail number", "origin" = "Flight origin airport code", "dest" = "Flight destination airport code", "air_time" = "Minutes spent in the air", "distance" = "Miles between airports", "hour" = "Hour of scheduled departure time", "minute" = "Minutes component of scheduled departure time", "time_hour" = "Scheduled date and hour of the flight as a POSIXct date" ) df <- add_name_labs(df, name.labs = names_labs_vec) get_name_labs(df) # show that they've been added ``` Let's add variable VALUE labels for variable "carrier." Helpfully, a mapping of airlines' carrier codes to their full names ships with the nycflights13 package itself. ```{r} airlines <- nycflights13::airlines head(airlines) ``` The carrier field of airlines matches the carrier column of df (formerly, flights) ```{r} ny_val <- airlines$carrier ``` The name field of airlines gives us the full airline names. ```{r} ny_lab <- airlines$name ``` Let's use these vectors to add value labels to df. We'll demo `add_val1()`, which accepts only one variable but allows you to pass its name unquoted. ```{r} df <- add_val1(df, var = carrier, vals = ny_val, labs = ny_lab, max.unique.vals = 20 ) ``` (Side note on warnings: The package issues the first in what will become a series of potentially annoying warnings that you are applying value labels to a larger data.frame than labelr was built to handle. There is a reason that this is a warning, not an error: labelr will work on larger data.frames until it doesn't, which is to say that the burdens of computational intensiveness will become a drag on speed and R's in-session memory capacity. In the present case, labelr handles the data.frame just fine, but things take a little longer, and labelr seizes most opportunities to remind you that you're making it work overtime.) Okay, back to the value-labeling. Our data.frame also has a month variable, expressed in integer terms (e.g., 1 indicates January, 9 indicates September). We will "hand-jam" month value labels,using `add_val_labs()`. This command is equivalent to `add_val1()`, except that it requires variable names to be quoted but allows you to supply more than one of them at a time (i.e., you can supply a character vector of variable names). In this case, we'll use it on just one variable. First, we'll create our vectors of unique values and labels. ```{r} ny_month_vals <- c(1:12) # values ny_month_labs <- c( "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" ) # labels ``` Note that order is important here: We need to supply exactly as many values as value labels, with each value label being uniquely associated with the value that shares its index. For example, in the above case, `ny_month_vals[3]` (here, `3`) is associated with the ny_month_labs[3] (here, `"MAR"`)). Now, let's use these two vectors to add value labels for the variable "month". ```{r} df <- add_val_labs(df, vars = "month", vals = ny_month_vals, labs = ny_month_labs, max.unique.vals = 20 ) ``` Finally, we'll use `add_quant_labs()` to provide numerical range value labels for five quintiles of the variable "dep_time." ```{r} df <- add_quant_labs(df, "dep_time", qtiles = 5) ``` Let's see where these value-labeling operations have left us. ```{r} get_val_labs(df) ``` We can use `head()` to get a baseline look at select rows and variables ```{r} head(df[c("origin", "dep_time", "dest", "year", "month", "carrier")]) ``` Now, let's do the same for a version of df that we've modified with `use_val_labs()`, which converts all values of value-labeled variables to their corresponding labels. ```{r} df_swapd <- use_val_labs(df) head(df_swapd[c("origin", "dep_time", "dest", "year", "month", "carrier")]) ``` Instead of replacing values using `use_val_labs()` -- something we can't directly undo -- it might be safer to simply add "value-labels-on" character variables to the data.frame, while preserving the parent variables. This adds nearly 1M new cells to our df (!), but let's throw caution to the wind with `add_lab_cols()`. ```{r} df_plus <- add_lab_cols(df, vars = c("carrier", "month", "dep_time")) head(df_plus[c( "origin", "dest", "year", "month", "month_lab", "dep_time", "dep_time_lab", "carrier", "carrier_lab" )]) ``` We can use `flab()` to filter df based on month and carrier, even when value labels are "invisible" (i.e., existing only as attributes() meta-data. ```{r} # labels are not visible (they exist only as attributes() meta-data) head(df[c("carrier", "arr_delay")]) # we still can use them to filter (note: we're filtering on "JetBlue Airways", # ...NOT its obscure code "B6") df_fl <- flab(df, carrier == "JetBlue Airways" & arr_delay > 20) # here's what's returned when we filtered on "JetBlue Airways" using flab() head(df_fl[c("carrier", "arr_delay")]) # double-check that this is JetBlue head(use_val_labs(df_fl)[c("carrier", "arr_delay")]) ``` How long did this entire NYC Flights session take (results will vary)? ```{r} the_buzzer <- Sys.time() the_buzzer - opening_ding ``` ## NA and "Irregular" Values labelr is not a fan of NA values or other "irregular" values, which are defined as infinite values, not-a-number values, and character values that look like them (e.g., "NAN", "INF", "inf", "Na"). When value-labeling a column / variable, such values are automatically given the catch-all label "NA" (which will be converted to an actual NA in any columns created by `add_lab_cols()` or `use_val_labs()`). You do not need (and should not try) to specify this yourself, and you should not try to over-ride labelr on this. If you want to use labelr AND you present with these sorts of values, your options are to accept the default "NA" label or convert these sorts of values to something else before labeling. With that said, let's see how labelr handles this, with an assist from our old friend mtcars (packaged with R's base distribution). First, let's assign mtcars to a new data.frame object that we will besmirch. ```{r} mtbad <- mtcars ``` Let's get on with the besmirching. ```{r} mtbad[1, 1:11] <- NA rownames(mtbad)[1] <- "Missing Car" mtbad[2, "am"] <- Inf mtbad[3, "gear"] <- -Inf mtbad[5, "carb"] <- NaN mtbad[2, "mpg"] <- Inf mtbad[3, "mpg"] <- NaN # add a character variable, for demonstration purposes # if it makes you feel better, you can pretend these are Consumer Reports or # ...JD Power ratings or something set.seed(9202) # for reproducibility mtbad$grade <- sample(c("A", "B", "C"), nrow(mtbad), replace = TRUE) mtbad[4, "grade"] <- NA mtbad[5, "grade"] <- "NA" mtbad[6, "grade"] <- "Inf" # see where this leaves us head(mtbad) sapply(mtbad, class) ``` Now, let's add value labels to this unruly data.frame. ```{r} mtlabs <- mtbad |> add_val1(grade, vals = c("A", "B", "C"), labs = c("Gold", "Silver", "Bronze") ) |> add_val1(am, vals = c(0, 1), labs = c("auto", "stick") ) |> add_val1(carb, vals = c(1, 2, 3, 4, 6, 8), # not the most inspired use of labels labs = c( "1c", "2c", "3c", "4c", "6c", "8c" ) ) |> add_val1(gear, vals = 3:5, # again, not the most compelling use case labs = c( "3-speed", "4-speed", "5-speed" ) ) |> add_quant1(mpg, qtiles = 4) # add quartile-based value labels ``` ```{r} get_val_labs(mtlabs, "am") # NA values were detected and dealt with ``` Let's streamline the data.frame with `sselect()` to make it more manageable. ```{r} mtless <- sselect(mtlabs, mpg, cyl, am, gear, carb, grade) # safely select head(mtless, 5) # note that the irregular values are still here ``` Notice how all irregular values are coerced to NA when we substitute labels for values with `use_val_labs()`. ```{r} head(use_val_labs(mtless), 5) # but they all go to NA if we `use_val_labs` ``` Now, let's try an `add_lab_cols()` view. ```{r} mtlabs_plus <- add_lab_cols(mtlabs, c("mpg", "am")) # creates, adds "am_lab" col mtlabs_plus <- sselect(mtlabs_plus, mpg, mpg_lab, am, am_lab) # select cols head(mtlabs_plus) # where we landed ``` What if we had tried to explicitly label the NA values and/or irregular values themselves? We would have failed. ```{r} # Trying to Label an Irregular Value (-Inf) mtbad <- add_val1( data = mtcars, var = gear, vals = -Inf, labs = c("neg.inf") ) # Trying to Label an Irregular Value (NA) mtbad <- add_val_labs( data = mtbad, vars = "grade", vals = NA, labs = c("miss") ) # Trying to Label an Irregular Value (NaN) mtbad <- add_val_labs( data = mtbad, vars = "carb", vals = NaN, labs = c("nan-v") ) # labelr also treats "character variants" of irregular values as irregular values. mtbad <- add_val1( data = mtbad, var = carb, vals = "NAN", labs = c("nan-v") ) ``` Again, labelr handles NA and irregular values and resists our efforts to take such matters into our own hands. ## Factors and Value Labels R's concept of a factor variable shares some affinities with the concept of a value-labeled variable and can be viewed as one approach to value labeling. However, factors can manifest idiosyncratic and surprising behaviors depending on the function to which you're trying to apply them. They are character-like, but they are not character values. They are built on top of integers, but they won't submit to all of the operations that integers do. They do some very handy things in certain model-fitting applications, but their behavior "under the hood" can be counter-intuitive or opaque. Simply put, they are their own thing. So, while factors have their purposes, it would be nice to associate value labels with the distinct values of data.frame variables in a manner that preserves the integrity and transparency of the underlying values (factors tend to be a bit opaque about this) and that allows you to view or use the labels in flexible ways. And if you wanted to work with a factor, it would be nice if you could add value labels to it without it ceasing to exist and behave as a factor. ### Adding Labels to a Factor With that said, let's see if we can have our label-factor cake and eat it, too, using the iris data.frame that comes pre-packaged with R. ```{r} unique(iris$Species) sapply(iris, class) # nothing up our sleeve -- "Species" is a factor ``` Let's add value labels to "Species" and assign the result to a new data.frame that we'll call irlab. For our value labels, we'll use "se", "ve", and "vi", which are not adding much new information, but they will help to illustrate what we can do with labelr and a factor variable. ```{r} irlab <- add_val_labs(iris, vars = "Species", vals = c("setosa", "versicolor", "virginica"), labs = c("se", "ve", "vi") ) # this also would've worked # irlab_dos <- add_val1(iris, Species, # vals = c("setosa", "versicolor", "virginica"), # labs = c("se", "ve", "vi") # ) ``` Note that we could have just as (or even more) easily used `add_val1()`, which works for a single variable at a time and allows us to avoid quoting our column name, if that matters to us. In contrast, `add_val_labs()` requires us to put our variable name(s) in quotes, but it also gives us the option to apply a common value-label scheme to several variables at once (e.g., Likert-style survey responses). We'll see an example of this type of use case in action in a little bit. For now, though, let's prove that the iris and irlab data.frames are functionally identical. First, note that irlab looks and acts just like iris in the usual ways that matter ```{r} summary(iris) summary(irlab) head(iris, 4) head(irlab, 4) lm(Sepal.Length ~ Sepal.Width + Species, data = iris) lm(Sepal.Length ~ Sepal.Width + Species, data = irlab) # values are same ``` Note also that irlab's "Species" is still a factor, just like its iris counterpart/parent. ```{r} sapply(irlab, class) levels(irlab$Species) ``` But irlab's "Species" has value labels! ```{r} get_val_labs(irlab, "Species") ``` And they work. ```{r} head(use_val_labs(irlab)) ir_v <- flab(irlab, Species == "vi") head(ir_v, 5) ``` Our take-aways so far? Factors can be value-labeled while staying factors, and we can use the labels to do labelr-y things with those factors. We can have both. We may want to go further and add the labeled variable alongside the factor version. ```{r} irlab_aug <- add_lab_cols(irlab, vars = "Species") ``` This gives us a new variable called "Species_lab". Let's get select rows of the resulting data.frame, since we want to see all the different species. ```{r} set.seed(231) sample_rows <- sample(seq_len(nrow(irlab)), 10, replace = FALSE) irlab_aug[sample_rows, ] sapply(irlab_aug, class) with(irlab_aug, table(Species, Species_lab)) ``` Caution: Replacing the entire data.frame using `use_val_labs()` WILL coerce factors to character, since the value labels are character values, not recognized factor levels ```{r} ir_char <- use_val_labs(irlab) # we assign this to a new data.frame sapply(ir_char, class) head(ir_char, 3) class(ir_char$Species) # it's character ``` Of course, even then, we could explicitly coerce the labels to be factors if we wanted ```{r} ir_fact <- use_val_labs(irlab) ir_fact$Species <- factor(ir_char$Species, levels = c("se", "ve", "vi"), labels = c("se", "ve", "vi") ) head(ir_fact, 3) class(ir_fact$Species) # it's a factor levels(ir_fact$Species) # it's a factor ``` We've recovered. ```{r} with(ir_fact, tapply(Sepal.Width, Species, mean)) with(irlab, tapply(Sepal.Width, Species, mean)) with(iris, tapply(Sepal.Width, Species, mean)) ``` ### Ordered factors Value labels work with ordered factors, too. Let's make a fictional ordered factor that we add to ir_ord. We can pretend that this is some sort of judge's overall quality rating, if that helps. ```{r} ir_ord <- iris set.seed(293) qrating <- c("AAA", "AA", "A", "BBB", "AA", "BBB", "A") ir_ord$qrat <- sample(qrating, 150, replace = TRUE) ir_ord$qrat <- factor(ir_ord$qrat, ordered = TRUE, levels = c("AAA", "AA", "A", "BBB") ) ``` Where do we stand with this factor? ```{r} levels(ir_ord$qrat) class(ir_ord$qrat) ``` Now, let's add value labels to it. ```{r} ir_ord <- add_val_labs(ir_ord, vars = "qrat", vals = c("AAA", "AA", "A", "BBB"), labs = c( "unimpeachable", "excellent", "very good", "meh" ) ) ``` Let's add a separate column with those labels as a distinct (character) variable unto itself, existing in addition to (not replacing) "qrat". ```{r} ir_ord <- add_lab_cols(ir_ord, vars = "qrat") head(ir_ord, 10) with(ir_ord, table(qrat_lab, qrat)) class(ir_ord$qrat) levels(ir_ord$qrat) class(ir_ord$qrat_lab) get_val_labs(ir_ord, "qrat") # labs are still there for qrat get_val_labs(ir_ord, "qrat_lab") # no labs here; this is just a character var ``` ### Other Factor and Categorical Variable Possibilities labelr offers some additional facilities for working with factors and categorical variables. For example, functions `add_lab_dummies()` (alias `ald()`) and `add_lab_dumm1()` (alias `ald1()`) will generate and assign a dummy (aka binary aka indicator) variable for each unique value label of a value-labeled variable -- factor or otherwise. Alternatively, `lab_int_to_factor()` (alias `int2f()`) allows you to convert a value-labeled integer variable (or other non-decimal-having numeric column) to a factor, while `factor_to_lab_int()` (alias `f2int()`) allows you to convert a factor to a value-labeled integer variable. Note that the latter is **NOT** a straightforward "undo" for the former: the resulting unique integer values and their ordering may differ, as we demonstrate. First, let's convert a factor to a value-labeled integer. ```{r} class(iris[["Species"]]) iris_df <- factor_to_lab_int(iris, Species) class(iris_df[["Species"]]) head(iris_df$Species) get_val_labs(iris_df, "Species") ``` Now, let's value-label an integer and convert it to a factor. Note that our variable is not a strict `as.integer()` integer, but it's a numeric variable with no decimal values, and that's good enough for `lab_int_to_factor()`. ```{r} carb_orig <- mtcars carb_orig <- add_val_labs( data = mtcars, vars = "carb", vals = c(1, 2, 3, 4, 6, 8), labs = c( "1c", "2c", # a tad silly, but these value labels will demo the principle "3c", "4c", "6c", "8c" ) ) # carb as labeled numeric is.integer(carb_orig$carb) # note: carb not technically an "as.integer()" integer class(carb_orig$carb) # but it IS numeric has_decv(carb_orig$carb) # and does NOT have decimals; so, lab_int_to_fac() works levels(carb_orig$carb) # none, not a factor head(carb_orig$carb, 3) mean(carb_orig$carb) # compare to carb_to_int (below) lm(mpg ~ carb, data = carb_orig) # compare to carb_to_int (below) # note this for comparison to below (adj_r2_orig <- summary(lm(mpg ~ carb, data = carb_orig))$adj.r.squared) # compare to counterparts below AIC(lm(mpg ~ carb, data = carb_orig)) # Make carb a factor carb_fac <- lab_int_to_factor(carb_orig, carb) # alias int2f() also works class(carb_fac$carb) # now it's a factor levels(carb_fac$carb) # like any good factor, it has levels head(carb_fac$carb, 3) lm(mpg ~ carb, data = carb_fac) # again: carb is a factor # compare these model fit stats to counterparts above and below (adj_r2_fac <- summary(lm(mpg ~ carb, data = carb_fac))$adj.r.squared) # compare to counterparts above and below AIC(lm(mpg ~ carb, data = carb_fac)) ``` Note that we can use `factor_to_lab_int()` to convert "carb" from a factor to a labeled integer variable. However, this is not a straightforward "undo" of what we just did: the resulting labeled integer won't be identical to the "carb" column of mtcars that we started with, because `factor_to_lab_int()` converts the supplied factor variable's values to sequentially ordered integers (from 1 to k, where k is the number of unique factor levels), ordered in terms of the levels of the factor variable being converted. ```{r} # ??"back"?? to integer? Not quite. Compare below to carb_orig above carb_to_int <- factor_to_lab_int(carb_fac, carb) # alias f2int() also works class(carb_to_int$carb) # Is an integer levels(carb_to_int$carb) # NOT a factor mean(carb_to_int$carb) # NOT the same as carb_orig identical(carb_to_int$carb, carb_orig$carb) # really! lm(mpg ~ carb, data = carb_to_int) # NOT the same as carb_orig # Compare to counterpart calls from earlier iterations of carb (above) (adj_r2_int <- summary(lm(mpg ~ carb, data = carb_to_int))$adj.r.squared) AIC(lm(mpg ~ carb, data = carb_to_int)) ``` Now, let's quickly demo `add_lab_dummies()`. To do so, we'll revisit the "Species" column of irlab, our factor variable from iris that we value-labeled a few moments ago. It's still here and still has value labels. ```{r} get_val_labs(irlab, "Species") ``` Let's use `add_lab_dummies()` to create a dummy variable for each of its labels. ```{r} irl_dumm <- add_lab_dummies(irlab, "Species") head(irl_dumm) # they're there! tail(irl_dumm) # again, they're there! ``` We can use `add_lab_dumm1()` to achieve the same result without quoting the column name. The countervailing advantage of `add_lab_dummies()` is that it lets you create dummy variables for more than one value-labeled variable at a time (`add_lab_dumm1()` does not). ```{r} irl_dumm2 <- add_lab_dumm1(irlab, Species) head(irl_dumm2) # again, they're there! tail(irl_dumm2) # again, they're there! ``` ## Value-Labeling Many Variables at Once Functions for adding value labels (e.g., `add_val_labs`, `add_quant_labs` and `add_m1_lab`) will do partial matching if the partial argument is set to TRUE. Let's use labelr's `make_likert_data()` function to generate some fake Likert scale-style survey data to demonstrate this more fully. ```{r} set.seed(272) # for reproducibility dflik <- make_likert_data(scale = 1:7) # another labelr function head(dflik) ``` We'll put the values we wish to label and the labels we wish to use in stand-alone vectors, which we will supply to `add_val_labs` in a moment. ```{r} vals2label <- 1:7 labs2use <- c( "VSD", "SD", "D", "N", "A", "SA", "VSA" ) ``` Now, let's associate/apply the value labels to ALL vars with "x" in their name and also to var "y3." Note: partial = TRUE. ```{r} dflik <- add_val_labs( data = dflik, vars = c("x", "y3"), ### note the vars args vals = vals2label, labs = labs2use, partial = TRUE # applying to all cols with "x" or "y3" substring in names ) ``` Let's compare dflik with value labels present but "off" to labels "on." First, present but "off." ```{r} head(dflik) ``` Now, let's "turn on" (use) these value labels. ```{r} lik1 <- uvl(dflik) # assign to new object, since we can't "undo" head(lik1) # we could have skipped previous call by using labelr::headl(dflik) ``` Yea, verily: All variables with "x" in their name (and "y3") got the labels! Suppose we want to drop these value labels for a select few, but not all, of these variables. `drop_val_labs` can get the job done. ```{r} dfdrop <- drop_val_labs(dflik, c("x2", "y3"), partial = FALSE ) ``` Most of our previously labeled columns remain so; but not "x2" and "y3." ```{r} get_val_labs(dfdrop, c("x2", "y3")) ``` Compare to values for variable "x1" (we did not drop value labels from this one) ```{r} get_val_labs(dfdrop, "x1") ``` Just like we did with `add_val_labs()`, we also can use a single command to drop value labels from all variables with "x" in their variable names. ```{r} dfxgone <- drop_val_labs(dflik, c("x"), partial = TRUE # note ) ``` "y3" still has value labels, but now **all** "x" var value labels are gone. ```{r} get_val_labs(dfxgone) ``` ### Tabulating Frequencies with `tabl()` Finally, let's get to know labelr's `tabl()` function, which supports count or proportion tabulations with labels turned "on" or "off" and offers some other functionalities. ```{r} set.seed(4847) # for reproducibility df <- make_demo_data(n = 1000) # make a fictional n = 1000 data set df <- add_val1(df, # data.frame var = raceth, # var to label, unquoted since this is add_val1() vals = c(1:7), # label values 1 through 7, inclusive labs = c( "White", "Black", "Hispanic", # ordered labels for sequential vals 1-7 "Asian", "AIAN", "Multi", "Other" ) ) df <- add_val1( data = df, var = gender, vals = c(0, 1, 2, 3, 4), # the values to be labeled labs = c("M", "F", "TR", "NB", "Diff-Term"), # labs order should reflect vals order max.unique.vals = 10 ) # label values of var "x1" according to quantile ranges df <- add_quant1( data = df, var = x1, # apply quantile range value labels to this var qtiles = 3 # first, second, and third tertiles ) # apply many-vals-get-one-label labels to "edu" (note vals 3-5 all get same lab) df <- add_m1_lab(df, "edu", vals = c(3:5), lab = "Some College+") df <- add_m1_lab(df, "edu", vals = 1, lab = "Not HS Grad") df <- add_m1_lab(df, "edu", vals = 2, lab = "HSG, No College") # show value labels get_val_labs(df) ``` With `tabl()`, tables can be generated... ...in terms of values ```{r} tabl(df, vars = "gender", labs.on = FALSE) ``` ...or in terms of labels ```{r} tabl(df, vars = "gender", labs.on = TRUE) # labs.on = TRUE is the default ``` ...in proportions ```{r} tabl(df, vars = c("gender", "edu"), prop.digits = 3) ``` ...cross-tab style ```{r} head(tabl(df, vars = c("raceth", "edu"), wide.col = "gender"), 20) ``` ...with non-value-labeled data.frames ```{r} tabl(iris, "Species") # explicit vars arg with one-var ("Species") # many-valued numeric vars automatically converted to quantile categories tabl(mtcars, c("am", "gear", "cyl", "disp", "mpg"), qtiles = 4, zero.rm = TRUE ) ``` ## Conclusion This is the suitably abrupt ending to our choppy, ad hoc overview of some additional labelr capabilities and special topics. Thanks for reading.