# Factors ## Introduction In R, factors are used to work with categorical variables, variables that have a fixed and known set of possible values. They are also useful when you want to display character vectors with non-alphabetical order. Historically, factors were much easier to work with than characters so many functions in base R automatically convert characters to factors (controlled by the dread `stringsAsFactors` argument). To get more historical context, you might want to read [_stringsAsFactors: An unauthorized biography_](http://simplystatistics.org/2015/07/24/stringsasfactors-an-unauthorized-biography/) by Roger Peng or [_stringsAsFactors = \_](http://notstatschat.tumblr.com/post/124987394001/stringsasfactors-sigh) by Thomas Lumley. Factors aren't as common in the tidyverse, because no function will automatically turn a character vector into a factor. It is, however, a good idea to use factors when appropriate, and controlling their levels can be particularly useful for tailoring visualisations of categorical data. ### Prerequisites To work with factors, we'll use the __forcats__ packages (tools for dealing **cat**egorical variables + anagram of factors). It provides a wide range of helpers for working with factors. We'll also use ggplot2 because factors are particularly important for visualisation. ```{r setup, message = FALSE} # devtools::install_github("hadley/forcats") library(forcats) library(ggplot2) library(dplyr) ``` ## Creating factors There are two ways to create a factor: during import with readr, using `col_factor()`, or after the fact, turning a string into a factor. Often you'll need to do a little experimetation, so I recommend starting with strings. To turn a string into a factor, call `factor()`, supplying list of possible values: ```{r} ``` For the rest of this chapter, we're going to focus on `forcats::gss_cat`. It's a sample of variables from the [General Social Survey](https://gssdataexplorer.norc.org/). The variables have been selected to illustrate a number of challenges with working with factors. ```{r} gss_cat ```` You can see the levels of a factor with `levels()`: ```{r} levels(gss_cat$race) ``` And this order is preserved in operations like `count()`: ```{r} gss_cat %>% count(race) ``` And in visualisations like `geom_bar()`: ```{r} ggplot(gss_cat, aes(race)) + geom_bar() ``` Note that by default, ggplot2 will drop levels that don't have any values. You can force them to appear with : ```{r} ggplot(gss_cat, aes(race)) + geom_bar() + scale_x_discrete(drop = FALSE) ``` Currently dplyr doesn't have a `drop` option, but it will in the future. ## Modifying factor order ```{r} relig <- gss_cat %>% group_by(relig) %>% summarise( age = mean(age, na.rm = TRUE), tvhours = mean(tvhours, na.rm = TRUE), n = n() ) ggplot(relig, aes(tvhours, relig)) + geom_point() ggplot(relig, aes(tvhours, fct_reorder(relig, tvhours))) + geom_point() ``` If you just want to pull a couple of levels out to the front, you can use `fct_relevel()`. ```{r} rincome <- gss_cat %>% group_by(rincome) %>% summarise( age = mean(age, na.rm = TRUE), tvhours = mean(tvhours, na.rm = TRUE), n = n() ) ggplot(rincome, aes(age, rincome)) + geom_point() gss_cat %>% count(fct_rev(rincome)) ``` `fct_rev(rincome)` `fct_reorder(religion, rincome)` `fct_reorder2(religion, year, rincome)` ```{r} by_year <- gss_cat %>% group_by(year, marital) %>% count() %>% mutate(prop = n / sum(n)) ggplot(by_year, aes(year, prop, colour = marital)) + geom_line() ggplot(by_year, aes(year, prop, colour = fct_reorder2(marital, year, prop))) + geom_line() ``` ## Modifying factor levels `fct_recode()` is the most general. It allows you to transform levels. ### Manually grouping ```{r} fct_count(fct_collapse(gss_cat$partyid, other = c("No answer", "Don't know", "Other party"), rep = c("Strong republican", "Not str republican"), ind = c("Ind,near rep", "Independent", "Ind,near dem"), dem = c("Not str democrat", "Strong democrat") )) ``` ### Lumping small groups together ```{r} gss_cat %>% mutate(relig = fct_lump(relig)) %>% count(relig) gss_cat %>% mutate(relig = fct_lump(relig, 5)) %>% count(relig, sort = TRUE) ``` ```{r} gss_cat$relig %>% fct_infreq() %>% fct_lump(5) %>% fct_count() gss_cat$relig %>% fct_lump(5) %>% fct_infreq() %>% fct_count() ``` `fct_reorder()` is sometimes also useful. It...