Skip to Tutorial Content

Mutating

What does mutate() do?

the mutate() function creates a new variable (a column) in your dataset, or can over-write an existing variable in your dataset

run the code below to inspect a few columns from the blood dataset

## Rows: 316
## Columns: 7
## $ Age        <dbl> 72.1, 73.6, 67.5, 65.8, 63.2, 65.4, 65.5, 67.1, 63.9, 63.0,…
## $ AA         <dbl> 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0,…
## $ FamHx      <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ T.Stage    <dbl> 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, NA, 1, 1, 1…
## $ Recurrence <dbl> 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ PVol       <dbl> 54.0, 43.2, 102.7, 46.0, 60.0, 45.9, 42.6, 40.7, 45.0, 67.6…
## $ TVol       <dbl> 3, 3, 1, 1, 2, 2, 2, 3, 2, 2, 1, 3, 2, 2, 2, 2, 1, 2, 2, 2,…

You can see that some of the numeric variables (AA, FamHx, T.Stage, Recurrence) should actually be categorical factor variables, and one (T.Stage) should be an ordered factor (ordinal categorical variable).
You might also want to calculate the ratio of TVol (tumor volume) to PVol (prostate volume) as a new variable, or the ratio of TVol to T.Stage as a new predictor variable.
You can do all of these things with the mutate() function.

Data Types

Example of changing data type

R has a number of data types. Each column in a data frame or tibble contains a consistent data type. These data types can be:

  • Character (aka string)
  • Numeric (in two flavors)
    • integer
    • double (aka real or decimal or float)
  • factor (categorical in two flavors)
    • unordered
    • ordered
  • logical (TRUE/FALSE) also
  • complex/imaginary numbers - not commonly used



Run the example below to create a new variable tvol_int that is an integer version of TVol, and new variable tvol_d which converts it back to a double.

Note that:

  • we created a new variable, and left the old one in place
    • this helps us check that we did the transformation we expected
  • the tibble format tells us whether each variable’s data type - int or dbl here.
  • base R includes as_type() functions for each data type.
  • as_numeric() will change an to a
  • you can put more than one change in a mutate statement(separated by commas, usually on distinct lines), and generate multiple variables.
  • it is generally best practice to mutate new variables, and avoid over-writing your data. This makes it easier to check that you made the correct data transformation.

Your turn

Modify the following code to create a new variable recur_true, which is a logical version (TRUE/FALSE) of Recurrence, then select recur_true and Recurrence to check that you did this correctly

blood %>% 
  mutate(recur_true = ---) %>% 
  select(---, ---)
blood %>% 
  mutate(recur_true = as.logical(Recurrence)) %>% 
  select(recur_true, Recurrence)

Fixing more data types

Modify the following code to create a new factor variables from AA, FamHx, and Recurrence, named aa_f, fhx_f, and recur_f. Select the right variables to check your transformations.

blood %>% 
  mutate(aa_f = ---,
         fhx_f = ---,
         recur_f = ----) %>% 
  select()
blood %>% 
  mutate(aa_f = as.factor(AA),
         fhx_f = as.factor(FamHx),
         recur_f = as.factor(Recurrence)) %>% 
  select(ends_with("_f"), AA, FamHx, Recurrence)

Making an ordered factor

Modify the following code to create a new ordered factor variable from T.Stage, called tstage_ord. The order should be 1 as the lowest (first) stage, and 2 as the highest (2nd) stage.
Then select T.Stage and tstage_ord to check your work.
You can read more about ordered factors at: https://www.dummies.com/programming/r/how-to-work-with-ordered-factors-in-r/

blood %>% 
  mutate(tstage_ord= factor(---,
         levels = c("--", "--"),
         ordered = TRUE))  %>% 
  select()
blood %>% 
  mutate(tstage_ord= factor(T.Stage, 
         levels = c("1", "2"),
         ordered = TRUE)) %>% 
  select(T.Stage, tstage_ord) 

You can see that tstage_ord is now an ordered categorical factor variable.

Calculations to Make new Variables

You will often have to derive new variables from your raw clinical data. For example, it is common to take weight and height variables and calculate BMI (body mass index), or do other calculations for body surface area (BSA) or mean arterial pressure (MAP). The mutate() function shines for making derived variables.

Let’s use the blood dataset, and calculate some derived variables that might be predictors of recurrence of prostate cancer.

Calculating t_pct

In the example below, calculate the ratio of TVol (tumor volume) to PVol (prostate volume) to produce a new variable t_pct, the percentage of the prostate that is tumor.
Then select Tvol, PVol, and t_pct to check your transformations.

blood %>% 
  mutate(t_pct= 100 * --- / ---)  %>% 
  select()
blood %>% 
  mutate(t_pct= 100 * TVol / PVol)  %>% 
  select(TVol, PVol, t_pct)

Calculating t_ratio

as a new variable, or the ratio of TVol to T.Stage as a new predictor variable.

In the example below, calculate the ratio of TVol (tumor volume) to T.Stage (tumor stage) to produce a new variable t_ratio, the ratio of volume to stage.
Then select Tvol, T.Stage, and t_ratio to check your transformations.

blood %>% 
  mutate(--- =  --- / ---)  %>% 
  ---()
blood %>% 
  mutate(t_ratio = TVol / T.Stage)  %>% 
  select(TVol, T.Stage, t_ratio)

Chunking Continuous Data

While is is a statistically bad idea, it is not uncommon to cut continuous variables into ordered factors to help interpretability.
This approach is going to cost you power in any statistical test, and is often derided in clinical outcomes publications as “dichotomania”.

But when you are just categorizing data (i.e. for Table 1), this can be helpful. The cut() function or the mutate(case_when()) construction can be helpful.

You can cut TimeToRecurrence into four quartiles, using the lowest value, 25th, 50th, 75th percentiles, and the highest value as breaks, as seen in the example below

Cutting Age by Decade

Use this approach to mutate a new variable called age_dec that breaks age into decades. Note that the range of blood$Age is 38.4, 79

In the example below, use mutate and cut to break blood$Age into the new variable age_dec. Then select and compare columns to check your work.

You can learn more about the cut() function here: https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/cut

blood %>% 
  mutate(age_dec =cut(---, 
          breaks = c(),
          ordered_result = TRUE,
          include.lowest = TRUE,
          labels = c('30s', '40s', '50s', '60s', '70s'))) %>% 
  select()
blood %>% 
  mutate(age_dec =cut(Age, 
          breaks = c(30, 40,50,60,70, 80),
          ordered_result = TRUE,
          include.lowest = TRUE,
          labels = c('30s', '40s', '50s', '60s', '70s'))) %>% 
  select(age_dec, Age)

Cutting PVol into Low, Medium, High

Now try to cut PVol into a new variable called prost_size with labels of small, medium, and large. Use the {santoku} package function chop_quantiles with quantiles of 0.33 and 0.66.

You can find more about {santoku} here: https://hughjonesd.github.io/santoku/index.html

blood %>% 
  mutate(prost_size = chop_quantiles(--, 
      c(--, --),
  lbl_manual(c("small", "medium", "large")))) %>% 
  select(prost_size, PVol)
blood %>% 
  mutate(prost_size = chop_quantiles(PVol, 
      c(0.33, 0.66),
  lbl_manual(c("small", "medium", "large")))) %>% 
  select(prost_size, PVol)

The Magic of across()

You can modify multiple columns in the same way with the across() function. This function can be used to select multiple columns for mutating or summarizing (or even pivoting).

An Example for Factors

You can use across() to mutate multiple numeric variables to factors in one mutate function.

the across() function has 3 main arguments and one supplementary argument

  • .cols (columns)
  • .fns (functions to apply)
  • … to add additional arguments to functions (like na.rm = TRUE)
  • .names (output names)

An Example for Means

You can use across() to summarize multiple numeric variables to their means and medians in one mutate function.

the across() function has 3 main arguments

  • .cols (columns)
  • .fns (functions to apply)
  • .names (output names)
## Warning: There was 1 warning in `summarize()`.
## ℹ In argument: `across(where(is.numeric), list(mean), na.rm = TRUE)`.
## ℹ In group 1: `AA = 0`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
## 
##   # Previously
##   across(a:b, mean, na.rm = TRUE)
## 
##   # Now
##   across(a:b, \(x) mean(x, na.rm = TRUE))

Tutorial