Animating Data Transformations III – separate()

We recently have published two blogs on animating data transformations. The first, Animating Data Transformations, illustrated the spread() and gather() functions for going between wide and tall representations of data. The second, Animating Data Transformations II, covered the unnest() function for transforming a list column into a one value per row format. Today, we’re going to introduce and animate the separate() function, which converts data with a single column containing multiple variables into a tidy, one-column-per-variable format.

First, let’s create and view some sample data, where one column is actually the concatenation of three variables: two treatments, and one value.

library(tidyverse)
library(gganimate)

sample_data <- tibble(
ID = 1:10,
`TRT1_TRT2_Value` = paste(sample(LETTERS[1:3], 10, replace = TRUE),
sample(LETTERS[1:3], 10, replace = TRUE),
round(rnorm(10)), sep = "_")
)

sample_data

# A tibble: 10 x 2
ID TRT1_TRT2_Value
<int> <chr>
1     1 B_A_1
2     2 B_A_-4
3     3 B_B_2
4     4 C_B_0
5     5 A_C_2
6     6 A_B_0
7     7 A_C_1
8     8 B_A_-1
9     9 B_B_0
10    10 A_A_0

Next, we will use the separate() function and specify the into parameter, as well as the sep character (an underscore, in this case):

sample_data_separated <- sample_data %>%
separate("TRT1_TRT2_Value", into = c("Treatment 1", "Treatment 2", "Value"), sep = "_")

sample_data_separated
# A tibble: 10 x 4
ID `Treatment 1` `Treatment 2` Value
<int> <chr>         <chr>         <chr>
1     1 B             A             1
2     2 B             A             -4
3     3 B             B             2
4     4 C             B             0
5     5 A             C             2
6     6 A             B             0
7     7 A             C             1
8     8 B             A             -1
9     9 B             B             0
10    10 A             A             0

Next, we perform a similar routine to the previous blogs and combine the two datasets into one dataset which will be used to build the animation:

longDat <- function(x) {
names(x) %>%
rbind(x) %>%
setNames(seq_len(ncol(x))) %>%
mutate(row = row_number()) %>%
tidyr::gather(column, value, -row) %>%
mutate(column = as.integer(column)) %>%
ungroup() %>%
arrange(column, row)
}

long_tables <- map(list(sample_data, sample_data_separated), longDat)

combined_table <- long_tables[] %>%
mutate(tstep = "a")

separated_table <- long_tables[] %>%
mutate(tstep = "b")

both_tables <- bind_rows(combined_table, separated_table)
both_tables\$celltype[both_tables\$column == 1] <- c("header", rep("id", 10), "header2", rep("id", 10))
both_tables\$celltype[both_tables\$column == 2] <- c("header", rep("value_treatment", 10), "header2", rep("treatment", 10))
both_tables\$celltype[both_tables\$column == 3] <- c("header2", rep("treatment", 10))
both_tables\$celltype[both_tables\$column == 4] <- c("header2", rep("value", 10))

both_tables
# A tibble: 66 x 5
row column value tstep celltype
<int>  <int> <chr> <chr> <chr>
1     1      1 ID    a     header
2     2      1 1     a     id
3     3      1 2     a     id
4     4      1 3     a     id
5     5      1 4     a     id
6     6      1 5     a     id
7     7      1 6     a     id
8     8      1 7     a     id
9     9      1 8     a     id
10    10      1 9     a     id
# … with 56 more rows

From this, we can produce static versions of the two images which will form the basis for the animation:

base_plot <- ggplot(both_tables, aes(column, -row, fill = celltype)) +
geom_tile(color = "black") +
geom_text(aes(label = value), size = 6, fontface = "bold") +
theme_void() +
scale_fill_manual(values = c("grey85", "grey85", "#ffebcc", "#d6e5ff", "#ffd6d7", "#f2d6ff"),
name = "",
labels = c("Header", "", "ID", "Treatment", "Value", "Value_Treatment"),
breaks = c("header", "", "id", "treatment", "value", "value_treatment")) +
theme(
plot.margin = unit(c(1, 1, 1, 1), "cm")
)
p0 <- base_plot +
facet_wrap(~tstep)
p0

Finally, we use gganimate to build the final animation!

p1 <- base_plot +
transition_states(
states            = tstep,
transition_length = 1,
state_length      = 1
) +
{"ft_lang_label":"__label__en","ft_lang_prob":0.50270975,"math_prob":0.9908032,"size":3855,"snap":"2019-43-2019-47","text_gpt3_token_len":1254,"char_repetition_ratio":0.12386393,"word_repetition_ratio":0.009836066,"special_character_ratio":0.3618677,"punctuation_ratio":0.1442441,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.98072314,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2019-10-22T04:14:03Z\",\"WARC-Record-ID\":\"<urn:uuid:c068a7ad-30f7-4f9f-98f9-0f56c07c974e>\",\"Content-Length\":\"59005\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:b0284557-2b15-47c2-9d95-a85f3db83417>\",\"WARC-Concurrent-To\":\"<urn:uuid:6117dc1b-53e0-4994-92ac-952809ac160e>\",\"WARC-IP-Address\":\"104.236.78.19\",\"WARC-Target-URI\":\"https://omnianalytics.io/2019/01/23/animating-data-transformations-iii-separate/\",\"WARC-Payload-Digest\":\"sha1:UU6D7PMKTO56R7GZNFWLZV4UKYRYFWWP\",\"WARC-Block-Digest\":\"sha1:5LTFMLXBBONRLRTRFUBTKRJVIMDCRVIY\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2019/CC-MAIN-2019-43/CC-MAIN-2019-43_segments_1570987798619.84_warc_CC-MAIN-20191022030805-20191022054305-00450.warc.gz\"}"}