# Introduction to headliner

The goal of `headliner` is to help analysts to translate facts to insights. In the comparison below (source), both dashboards have the same underlying data but how they present the information to the user is very different.

Right now, there isn’t anything out of the box to help users dynamically create phrasing like used in the “insights” version without a bit of coding gymnastics. The many ways you could approach it combined with the steps required to say “if positive, show it like this, if negative show it like that” increase the technical debt this kind of code could add to a project. For this reason, `headliner` is designed to deliver the building blocks required to create these phrases for plot titles, value boxes in `shiny` or section headers in a report.

## Installation

You can install the dev version of `headliner` from github with:

    devtools::install_github("rjake/headliner")

### Installation

For these examples, I will use a function called `demo_data()` to build a data set based on the current date (this was last run on 12/19/22).

    library(headliner)
    library(dplyr)
    demo_data()
    #> # A tibble: 10 × 5
    #>    group sales count on_sale date
    #>    <chr> <dbl> <dbl>   <dbl> <date>
    #>  1 a       101    35       1 2022-12-19
    #>  2 a       102    34       0 2022-10-19
    #>  3 b       103    33       1 2022-08-19
    #>  4 b       104    32       0 2022-06-19
    #>  5 c       105    31       1 2022-04-19
    #>  6 c       106    30       0 2022-02-19
    #>  7 d       107    29       1 2021-12-19
    #>  8 d       108    28       0 2021-10-19
    #>  9 e       109    27       1 2021-08-19
    #> 10 e       110    26       0 2021-06-19

### A simple headline

What we want is to say something like this:

    #> We have seen a 5.6% decrease compared to the same time last year (101 vs. 107).

We can look at the data an see that about 12 months ago, `sales` was 107 where as today it is 101. We can give these values to `headline()` and get a simple phrase

    headline(
      x = 101,
      y = 107
    )
    #> decrease of 6 (101 vs. 107)

To see how the sentence was constructed, we can look at the components used under the hood. This `return_data = TRUE` returns a named list. I will condense with `view_list()`

    headline(101, 107, return_data = TRUE) |>
      view_list()
    #>                                           value
    #> headline            decrease of 6 (101 vs. 107)
    #> x                                           101
    #> y                                           107
    #> delta                                         6
    #> delta_p                                     5.6
    #> article_delta                               a 6
    #> article_delta_p                           a 5.6
    #> raw_delta                                    -6
    #> raw_delta_p                                -5.6
    #> article_raw_delta                          a -6
    #> article_raw_delta_p                      a -5.6
    #> sign                                         -1
    #> orig_values                         101 vs. 107
    #> trend                                  decrease

### A custom headline

We can compose it like this using `glue::glue()` syntax

    headline(
      x = 101,
      y = 107,
      headline = "We have seen {article_delta_p}% {trend} compared to the same time last year ({orig_values})."
    )
    #> We have seen a 5.6% decrease compared to the same time last year (101 vs. 107).

You might have noticed that there are multiple `article_*` components available. `article_delta` is for the difference between the two values (“a 6 person loss” vs “an 8 person loss”), `article_delta_p` is for the percentage difference for “a 5.6%” vs “an 8.6%”. You can also add articles to words using `add_article()`. For example `add_artice("increase")` gives us “an increase” vs `add_artice("decrease")` “a decrease”.

### Helpers for dynamic calculations

But let’s see if we can make the calculations more dynamic…

First, we can use a function called `add_date_columns()` to calculate distances from the current date (or the reference date specified) to the values in the `date` column . With these new fields we can see that 10/19/22 was 61 days ago (or 9 weeks or 2 months, …) from the current date.

    demo_data() |>
      add_date_columns(date_col = date)
    #> # A tibble: 10 × 11
    #>    group sales count on_sale date         day  week month quarter calendar_year
    #>    <chr> <dbl> <dbl>   <dbl> <date>     <dbl> <dbl> <dbl>   <dbl>         <dbl>
    #>  1 a       101    35       1 2022-12-19     0     0     0       0             0
    #>  2 a       102    34       0 2022-10-19   -61    -9    -2       0             0
    #>  3 b       103    33       1 2022-08-19  -122   -18    -4      -1             0
    #>  4 b       104    32       0 2022-06-19  -183   -27    -6      -2             0
    #>  5 c       105    31       1 2022-04-19  -244   -35    -8      -2             0
    #>  6 c       106    30       0 2022-02-19  -303   -44   -10      -3             0
    #>  7 d       107    29       1 2021-12-19  -365   -53   -12      -4            -1
    #>  8 d       108    28       0 2021-10-19  -426   -61   -14      -4            -1
    #>  9 e       109    27       1 2021-08-19  -487   -70   -16      -5            -1
    #> 10 e       110    26       0 2021-06-19  -548   -79   -18      -6            -1
    #> # … with 1 more variable: fiscal_year <dbl>

We can then identify some conditions for our group of interest (`x`) and our reference group (`y`). This step uses the kind of logic you would use in `dplyr::filter()` or `base::subset()` + the logic used by `dplyr::across()`

    yoy <- # year over year
      demo_data() |>
      add_date_columns(date) |>
      compare_conditions(
        x = (month == 0),   # this month
        y = (month == -12), # vs 12 months ago
        .cols = sales,   # the column(s) to aggregate
        .fns = lst(mean)    # the list of functions passed to summarise(across(...))
      )

    yoy
    #> # A tibble: 1 × 2
    #>   mean_sales_x mean_sales_y
    #>          <dbl>        <dbl>
    #> 1          101          107

The argument `lst(mean)` is equivalent to writing `list(mean = mean)`. The name (left side) is how it will name the column, the right side is the function to use. If I had used `.fns = list(avg = mean)` The names would have been `avg_sales_x` and `avg_sales_y` and used `mean()` as the calculation. Because `compare_conditions()` uses the mean as the default, I’ll omit it going forward.

You may want to do other steps with the data frame but if you want to go right into a headline, you can use `headline_list()`. Another good option for adding headlines to data frames is `add_headline_column()` (see next section)

    yoy |>
      headline_list(
        headline = "We have seen {article_delta_p}% {trend} compared to the same time last year ({orig_values})."
      )
    #> We have seen a 5.6% decrease compared to the same time last year (101 vs. 107).

If your result has more than 2 values, you can specify the values you need by calling their names

    car_stats <-
      mtcars |>
      compare_conditions(
        x = cyl == 4,
        y = cyl > 4,
        .cols = starts_with("d"),
        .fns = list(avg = mean, min = min)
      )

    view_list(car_stats)
    #>                 value
    #> avg_disp_x 105.136364
    #> avg_disp_y 296.504762
    #> avg_drat_x   4.070909
    #> avg_drat_y   3.348095
    #> min_disp_x  71.100000
    #> min_disp_y 145.000000
    #> min_drat_x   3.690000
    #> min_drat_y   2.760000

    car_stats |>
      headline_list(
        x = avg_disp_x,
        y = avg_disp_y,
        headline = "Difference in avg. displacement of {delta}cu.in. ({orig_values})"
      )
    #> Difference in avg. displacement of 191.4cu.in. (105.1 vs. 296.5)

    car_stats |>
      headline_list(
        x = avg_drat_x,
        y = avg_drat_y,
        headline = "Difference in avg. rear axle ratio of {delta} ({orig_values})"
      )
    #> Difference in avg. rear axle ratio of 0.7 (4.1 vs. 3.3)

`compare_conditions()` can also be used to compare categorical criteria.

    pixar_films |>
      compare_conditions(
        rating == "G",
        rating == "PG",
        .cols = rotten_tomatoes
      ) |>
      headline_list(
        headline =
          "Metacritic has an avg. rating of {x} for G-rated films and {y} for PG-rated films \\
        ({delta} points {trend})",
        trend_phrases = trend_terms(more = "higher",  less = "lower"),
        n_decimal = 0
      )
    #> Metacritic has an avg. rating of 87 for G-rated films and 91 for PG-rated films (4 points lower)

    demo_data() |>
      compare_conditions(
        x = group == "a",
        y = group == "c",
        .cols = c(sales),
        .fns = sum
      ) |>
      headline_list(
        headline = "Group A is ${delta} {trend} Group C (${x} vs ${y})",
        trend_phrases = trend_terms(more = "ahead",  less = "behind")
      )
    #> Group A is $8 behind Group C ($203 vs $211)

### Within a data frame

You can also use `add_headline_column()` to append a column to your data frame with headlines describing each row. You can reference existing columns in the headline and you can bring back specific talking points using `return_cols =`. You can use this to find the most interesting phrases.

    pixar_films |>
      select(film, rotten_tomatoes, metacritic) |>
      add_headline_column(
        x = rotten_tomatoes,
        y = metacritic,
        headline = "{film} had a difference of {delta} points",
        return_cols = c(delta)
      ) |>
      arrange(desc(delta))
    #> # A tibble: 22 × 5
    #>    film                rotten_tomatoes metacritic headline                 delta
    #>    <chr>                         <dbl>      <dbl> <chr>                    <dbl>
    #>  1 Onward                           88         61 Onward had a difference…    27
    #>  2 Monsters, Inc.                   96         79 Monsters, Inc. had a di…    17
    #>  3 Cars 2                           40         57 Cars 2 had a difference…    17
    #>  4 Finding Dory                     94         77 Finding Dory had a diff…    17
    #>  5 Coco                             97         81 Coco had a difference o…    16
    #>  6 A Bug's Life                     92         77 A Bug's Life had a diff…    15
    #>  7 Monsters University              80         65 Monsters University had…    15
    #>  8 Incredibles 2                    93         80 Incredibles 2 had a dif…    13
    #>  9 Toy Story 4                      97         84 Toy Story 4 had a diffe…    13
    #> 10 Toy Story 2                     100         88 Toy Story 2 had a diffe…    12
    #> # … with 12 more rows

### Adding plural phrasing & multiple trend terms

You can add phrases to customize your sentences. `plural_phrases()` allows you to add new variables to the list of components available. Here I am adding `{people}` for use in my headline.

    headline(
      x = 9,
      y = 10,
      headline = "{delta_p}% {trend} ({delta} {people})",
      plural_phrases = list(
        people = plural_phrasing(single = "person", multi = "people")
      )
    )
    #> 10% decrease (1 person)

You can actually pass multiple `trend_terms()` and `plural_phrasing()` options.

    # lists to use
    more_less <-
      list(
        an_increase = trend_terms("an increase", "a decrease"),
        more = trend_terms(more = "more", less = "less")
      )

    are_people <-
      list(
        are = plural_phrasing(single = "is", multi = "are"),
        people = plural_phrasing(single = "person", multi = "people")
      )

    # notice the difference in these two outputs
    headline(
      x = 25,
      y = 23,
      headline = "There {are} {delta} {more} {people} ({an_increase} of {delta_p}%)",
      trend_phrases = more_less,
      plural_phrases = are_people
    )
    #> There are 2 more people (an increase of 8.7%)

    headline(
      x = 25,
      y = 26,
      headline = "There {are} {delta} {more} {people} ({an_increase} of {delta_p}%)",
      trend_phrases = more_less,
      plural_phrases = are_people
    )
    #> There is 1 less person (a decrease of 3.8%)

### When there’s no change

You can also adjust the text if the numbers are the same

    headline(3, 3)
    #> There was no difference

    headline(3, 3, if_match = "There were no additional applicants ({x} total)")
    #> There were no additional applicants (3 total)

### An example with `valueBox()`

Here’s an example that uses `headliner` to create a `valueBox()` in `shiny` with dynamic colors and text.

    show <- compare_values(101, 107)
    box_color <- ifelse(show$sign == -1, "red", "blue")

    valueBox(
      value =
        headline(
          show$x,
          show$y,
          headline = "{delta_p}% {trend}"
        ),
      subtitle = "vs. the same time last year",
      color = box_color
    )