Author: David Sjoberg

# convert

#### David Sjoberg

## `convert` your data types

### Before everything there was data type conversion

Best practise of data analysis is to fix data types directly after importing data into R. This helps in many ways:

- You only need to do it once
- If there are any errors you know where in the script it should be fixed
- It scales up your code. For example, all columns that should be numeric could be converted at the same time.

Additionally, if every column is converted to its appropriate data type then you won’t be surprised by data type errors the next time you run the script.

### Usage

`convert(.x, ...)` where `.x` is a data frame. `...` is a placeholder for data type specific conversion functions.

### Support functions

`convert` must be used in conjunction with data type conversion functions:

- `chr` converts to character.
- `num` converts to numeric.
- `int` converts to integer.
- `lgl` converts to logical.
- `fct` converts to factor.
- `dte` converts to date.
- `dtm` converts to date time.

### The syntax

Imagine you have a data frame where you want to change columns:

- `a` and `b` to numerical
- `c` to date
- `d` and `e` to character

Then you can write:

`df %>% convert(num(a, b), dte(c), chr(d, e))`

### Examples

The easiest way to understand how simple `convert` is to use is with examples. Have a look at the a gapminder dataset from the package `gapminder`:

    library(gapminder)
    gapminder

    #> # A tibble: 1,704 × 6
    #>   country     continent  year lifeExp      pop gdpPercap
    #>   <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
    #> 1 Afghanistan Asia       1952    28.8  8425333      779.
    #> 2 Afghanistan Asia       1957    30.3  9240934      821.
    #> 3 Afghanistan Asia       1962    32.0 10267083      853.
    #> 4 Afghanistan Asia       1967    34.0 11537966      836.
    #> # … with 1,700 more rows
    #> # ℹ Use `print(n = ...)` to see more rows

We might want to change the country column to character instead of factor. To do this we use `chr` together with the column name inside `convert`:

    gapminder %>%
      convert(chr(country))

    #> # A tibble: 1,704 × 6
    #>   country     continent  year lifeExp      pop gdpPercap
    #>   <chr>       <fct>     <int>   <dbl>    <int>     <dbl>
    #> 1 Afghanistan Asia       1952    28.8  8425333      779.
    #> 2 Afghanistan Asia       1957    30.3  9240934      821.
    #> 3 Afghanistan Asia       1962    32.0 10267083      853.
    #> 4 Afghanistan Asia       1967    34.0 11537966      836.
    #> # … with 1,700 more rows
    #> # ℹ Use `print(n = ...)` to see more rows

This converted the country column to the data type character. But we do not have to make this whole procedure for each column if we want to convert more columns. Let’s say that we also want to convert continent to character and the column lifeExp to integer, pop to double and gdpPercap to numeric. It is simply done:

    gapminder %>%
      convert(chr(country,
                  continent),
              int(lifeExp),
              dbl(pop),
              num(gdpPercap))

    #> # A tibble: 1,704 × 6
    #>   country     continent  year lifeExp      pop gdpPercap
    #>   <chr>       <chr>     <int>   <int>    <dbl>     <dbl>
    #> 1 Afghanistan Asia       1952      28  8425333      779.
    #> 2 Afghanistan Asia       1957      30  9240934      821.
    #> 3 Afghanistan Asia       1962      31 10267083      853.
    #> 4 Afghanistan Asia       1967      34 11537966      836.
    #> # … with 1,700 more rows
    #> # ℹ Use `print(n = ...)` to see more rows

## I can already convert between data types. Why do I need `convert`?

You can change alot of data types with little code. Consider using `mutate` from `dplyr` to do the same operation:

    gapminder %>%
      mutate(country = as.character(country),
             continent = as.character(continent),
             lifeExp = as.integer(lifeExp),
             pop = as.double(pop),
             gdpPercap = as.numeric(gdpPercap))

    #> # A tibble: 1,704 × 6
    #>   country     continent  year lifeExp      pop gdpPercap
    #>   <chr>       <chr>     <int>   <int>    <dbl>     <dbl>
    #> 1 Afghanistan Asia       1952      28  8425333      779.
    #> 2 Afghanistan Asia       1957      30  9240934      821.
    #> 3 Afghanistan Asia       1962      31 10267083      853.
    #> 4 Afghanistan Asia       1967      34 11537966      836.
    #> # … with 1,700 more rows
    #> # ℹ Use `print(n = ...)` to see more rows

Which gives the same result. However, you need to refer to the column name twice and the data type conversion function for each column. Imagine the code to convert 20 columns.

However, `dplyr` have another way of applying the same function to multiple columns which could help, `mutate_at`. The same example would then look like:

    gapminder %>%
      mutate_at(vars(country, continent), funs(as.character)) %>%
      mutate_at(vars(lifeExp), funs(as.integer)) %>%
      mutate_at(vars(pop), funs(as.double)) %>%
      mutate_at(vars(gdpPercap), funs(as.numeric))

    #> Warning: `funs()` was deprecated in dplyr 0.8.0.
    #> ℹ Please use a list of either functions or lambdas:
    #>
    #> # Simple named list: list(mean = mean, median = median)
    #>
    #> # Auto named with `tibble::lst()`: tibble::lst(mean, median)
    #>
    #> # Using lambdas list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))

    #> # A tibble: 1,704 × 6
    #>   country     continent  year lifeExp      pop gdpPercap
    #>   <chr>       <chr>     <int>   <int>    <dbl>     <dbl>
    #> 1 Afghanistan Asia       1952      28  8425333      779.
    #> 2 Afghanistan Asia       1957      30  9240934      821.
    #> 3 Afghanistan Asia       1962      31 10267083      853.
    #> 4 Afghanistan Asia       1967      34 11537966      836.
    #> # … with 1,700 more rows
    #> # ℹ Use `print(n = ...)` to see more rows

Which is more easily scaled to deal with data type conversion of large numbers of variables. However, `convert` does the same job with much less code. In fact, `convert` uses `mutate_at` internally. The difference is syntax and code readability. Compare again with `convert`:

    gapminder %>%
      convert(chr(country,
                   continent),
               int(lifeExp),
               dbl(pop),
               num(gdpPercap))

    #> # A tibble: 1,704 × 6
    #>   country     continent  year lifeExp      pop gdpPercap
    #>   <chr>       <chr>     <int>   <int>    <dbl>     <dbl>
    #> 1 Afghanistan Asia       1952      28  8425333      779.
    #> 2 Afghanistan Asia       1957      30  9240934      821.
    #> 3 Afghanistan Asia       1962      31 10267083      853.
    #> 4 Afghanistan Asia       1967      34 11537966      836.
    #> # … with 1,700 more rows
    #> # ℹ Use `print(n = ...)` to see more rows

## Adding additional arguments

`convert` also supports functions of `convert` support additional arguments to be passed. For example, if you want to convert a number to a date and want to include an `origin` argument you can write:

    tibble(dates = c(12818, 13891),
            sunny = c("yes", "no")) %>%
      convert(dte(dates, .args = list(origin = "1900-01-01")))

    #> # A tibble: 2 × 2
    #>   dates      sunny
    #>   <date>     <chr>
    #> 1 1935-02-05 yes
    #> 2 1938-01-13 no

## Final note

`convert` is built upon `dplyr` and it will share some amazing features of `dplyr`. For example, `tidyselect` works with `convert` which helps you to select multiple columns at the same time. A simple example, if you want to change all columns with names that includes the letter “e” to factors, you can write:

    gapminder %>%
      convert(fct(contains("e")))

    #> # A tibble: 1,704 × 6
    #>   country     continent year  lifeExp      pop gdpPercap
    #>   <fct>       <fct>     <fct> <fct>      <int> <fct>
    #> 1 Afghanistan Asia      1952  28.801   8425333 779.4453145
    #> 2 Afghanistan Asia      1957  30.332   9240934 820.8530296
    #> 3 Afghanistan Asia      1962  31.997  10267083 853.10071
    #> 4 Afghanistan Asia      1967  34.02   11537966 836.1971382
    #> # … with 1,700 more rows
    #> # ℹ Use `print(n = ...)` to see more rows