{"id":13665870,"url":"https://github.com/gadenbuie/tidyexplain","last_synced_at":"2025-04-04T08:04:51.678Z","repository":{"id":41403734,"uuid":"144889262","full_name":"gadenbuie/tidyexplain","owner":"gadenbuie","description":"🤹‍♀ Animations of tidyverse verbs using R, the tidyverse, and gganimate","archived":false,"fork":false,"pushed_at":"2022-01-11T02:50:28.000Z","size":86929,"stargazers_count":762,"open_issues_count":14,"forks_count":181,"subscribers_count":31,"default_branch":"main","last_synced_at":"2024-10-14T22:29:57.585Z","etag":null,"topics":["dplyr","gganimate","ggplot2","joins","rstats","sql"],"latest_commit_sha":null,"homepage":"https://garrickadenbuie.com/project/tidyexplain","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gadenbuie.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null}},"created_at":"2018-08-15T18:29:46.000Z","updated_at":"2024-09-29T18:09:31.000Z","dependencies_parsed_at":"2022-08-10T02:07:24.105Z","dependency_job_id":null,"html_url":"https://github.com/gadenbuie/tidyexplain","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadenbuie%2Ftidyexplain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadenbuie%2Ftidyexplain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadenbuie%2Ftidyexplain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadenbuie%2Ftidyexplain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gadenbuie","download_url":"https://codeload.github.com/gadenbuie/tidyexplain/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247137532,"owners_count":20889893,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dplyr","gganimate","ggplot2","joins","rstats","sql"],"created_at":"2024-08-02T06:00:52.596Z","updated_at":"2025-04-04T08:04:51.657Z","avatar_url":"https://github.com/gadenbuie.png","language":"R","funding_links":[],"categories":["R"],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r setup, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  echo = FALSE,\n  warning = FALSE,\n  message = FALSE,\n  cache = TRUE\n)\n```\n\n[gganimate]: https://github.com/thomasp85/gganimate#README\n[dplyr-two-table]: https://dplyr.tidyverse.org/articles/two-table.html\n[r4ds]: http://r4ds.had.co.nz/\n[r4ds-relational]: http://r4ds.had.co.nz/relational-data.html\n[r4ds-set-ops]: http://r4ds.had.co.nz/relational-data.html#set-operations\n[r4ds-tidy-data]: http://r4ds.had.co.nz/tidy-data.html#tidy-data-1\n[tidyverse]: https://tidyverse.org\n[tidyr]: https://tidyr.tidyverse.org\n\n# Tidy Animated Verbs\n\n[![CC0](https://img.shields.io/badge/license_(images)_-CC0-green.svg)](https://creativecommons.org/publicdomain/zero/1.0/)\n[![MIT](https://img.shields.io/badge/license_(code)_-MIT-green.svg)](https://opensource.org/licenses/MIT)\n\nGarrick Aden-Buie -- [\u0026commat;grrrck](https://twitter.com/grrrck) -- [garrickadenbuie.com](https://www.garrickadenbuie.com)\n\n**Thanks to contributions from ...**\n\n- [Tyler Grant Smith](https://github.com/TylerGrantSmith) contributed set operations animations.\n- [Lukas Wallrich](https://github.com/LukasWallrich) and [Kelsey Gonzalez](https://github.com/kelseygonzalez) helped create animations of tidyr's pivoting functions.\n\n**Animations:**\n\n\u003c!-- README START --\u003e\n\n- [**Mutating Joins**](#mutating-joins) — [`inner_join()`](#inner-join), [`left_join()`](#left-join),\n  [`right_join()`](#right-join), [`full_join()`](#full-join)\n  \n- [**Filtering Joins**](#filtering-joins) — [`semi_join()`](#semi-join), [`anti_join()`](#anti-join)\n\n- [**Set Operations**](#set-operations) — [`union()`](#union), [`union_all()`](#union-all), [`intersect()`](#intersection), [`setdiff()`](#set-difference)\n\n- [**Tidy Data**](#tidy-data) — [`pivot_wider()` and `pivot_longer()`](#pivot-wider-and-longer), [`spread()` and `gather()`](#spread-and-gather)\n\n- Learn more about\n    - [Using the animations and images](#usage)\n    - [Relational Data](#relational-data)\n    - [gganimate](#gganimate)\n    \n## Background\n\n### Usage\n\nPlease feel free to use these images for teaching or learning about action verbs from the [tidyverse](https://tidyverse.org).\nYou can directly download the [original animations](images/) or static images in [svg](images/static/svg/) or [png](images/static/png/) formats, or you can use the [scripts](R/) to recreate the images locally.\n\nCurrently, the animations cover the [dplyr two-table verbs][dplyr-two-table] and I'd like to expand the animations to include more verbs from the tidyverse.\n[Suggestions are welcome!](https://github.com/gadenbuie/tidy-animated-verbs/issues)\n\n### Relational Data\n\nThe [Relational Data][r4ds-relational] chapter of the\n[R for Data Science][r4ds] book by Garrett Grolemund and Hadley Wickham\nis an excellent resource for learning more about relational data.\n\nThe [dplyr two-table verbs vignette][dplyr-two-table]\nand Jenny Bryan's [Cheatsheet for dplyr join functions](http://stat545.com/bit001_dplyr-cheatsheet.html)\nare also great resources.\n\n### gganimate\n\nThe animations were made possible by the newly re-written [gganimate] package by \n[Thomas Lin Pedersen](https://github.com/thomasp85)\n(original by [Dave Robinson](https://github.com/dgrtwo)).\nThe [package readme][gganimate] provides an excellent (and quick) introduction to gganimate.\n\n### Dynamic Animations\n\nThanks to an initial push by [David Zimmermann](https://github.com/DavZim), we have begun work towards functions that generate dynamic animations from users' actual data.\nPlease visit the [pkg branch](https://github.com/gadenbuie/tidyexplain/tree/pkg) of the tidyexplain repository for more information (or to contribute!).\n\n## Mutating Joins\n\n\u003e A mutating join allows you to combine variables from two tables. It first matches observations by their keys, then copies across variables from one table to the other.  \n\u003e [R for Data Science: Mutating joins](http://r4ds.had.co.nz/relational-data.html#mutating-joins)\n\n```{r intial-dfs}\nsource(\"R/00_base_join.R\")\ndf_names \u003c- tibble(\n  .x = c(1.5, 4.5), .y = 0.25,\n  value = c(\"x\", \"y\"),\n  size = 12,\n  color = \"black\"\n)\n\ng \u003c- plot_data(initial_join_dfs) +\n  geom_text(data = df_names, family = \"Fira Mono\", size = 24)\n\nsave_static_plot(g, \"original-dfs\")\n```\n\n\u003cimg src=\"images/static/png/original-dfs.png\" width=\"480px\" /\u003e\n\n```{r echo=TRUE}\nx\ny\n```\n\n### Inner Join\n\n\u003e All rows from `x` where there are matching values in `y`, and all columns from `x` and `y`.\n\n```{r inner-join}\nsource(\"R/inner_join.R\")\n```\n\n![](images/inner-join.gif)\n\n```{r echo=TRUE}\ninner_join(x, y, by = \"id\")\n```\n\n### Left Join\n\n\u003e All rows from `x`, and all columns from `x` and `y`. Rows in `x` with no match in `y` will have `NA` values in the new columns.\n\n```{r left-join}\nsource(\"R/left_join.R\")\n```\n\n![](images/left-join.gif)\n\n```{r echo=TRUE}\nleft_join(x, y, by = \"id\")\n```\n\n### Left Join (Extra Rows in y)\n\n\u003e ... If there are multiple matches between `x` and `y`, all combinations of the matches are returned.\n\n```{r left-join-extra}\nsource(\"R/left_join_extra.R\")\n```\n\n![](images/left-join-extra.gif)\n\n```{r echo=TRUE}\ny_extra # has multiple rows with the key from `x`\nleft_join(x, y_extra, by = \"id\")\n```\n\n### Right Join\n\n\u003e All rows from y, and all columns from `x` and `y`. Rows in `y` with no match in `x` will have `NA` values in the new columns.\n\n```{r right-join}\nsource(\"R/right_join.R\")\n```\n\n![](images/right-join.gif)\n\n```{r echo=TRUE}\nright_join(x, y, by = \"id\")\n```\n\n### Full Join\n\n\u003e All rows and all columns from both `x` and `y`. Where there are not matching values, returns `NA` for the one missing.\n\n```{r full-join}\nsource(\"R/full_join.R\")\n```\n\n![](images/full-join.gif)\n\n```{r echo=TRUE}\nfull_join(x, y, by = \"id\")\n```\n\n## Filtering Joins\n\n\u003e Filtering joins match observations in the same way as mutating joins, but affect the observations, not the variables.\n\u003e ... Semi-joins are useful for matching filtered summary tables back to the original rows.\n\u003e ... Anti-joins are useful for diagnosing join mismatches.  \n\u003e [R for Data Science: Filtering Joins](http://r4ds.had.co.nz/relational-data.html#filtering-joins)\n\n### Semi Join\n\n\u003e All rows from `x` where there are matching values in `y`, keeping just columns from `x`.\n\n```{r semi-join}\nsource(\"R/semi_join.R\")\n```\n\n![](images/semi-join.gif)\n\n```{r echo=TRUE}\nsemi_join(x, y, by = \"id\")\n```\n\n### Anti Join\n\n\u003e All rows from `x` where there are not matching values in `y`, keeping just columns from `x`.\n\n```{r anti-join}\nsource(\"R/anti_join.R\")\n```\n\n![](images/anti-join.gif)\n\n```{r echo=TRUE}\nanti_join(x, y, by = \"id\")\n```\n\n## Set Operations\n\n\u003e Set operations are occasionally useful when you want to break a single complex filter into simpler pieces. \n\u003e All these operations work with a complete row, comparing the values of every variable. \n\u003e These expect the x and y inputs to have the same variables, and treat the observations like sets.  \n\u003e [R for Data Science: Set operations](http://r4ds.had.co.nz/relational-data.html#set-operations)\n\n```{r intial-dfs-so}\nsource(\"R/00_base_set.R\")\ndf_names \u003c- tibble(\n  .x = c(2.5, 5.5), .y = 0.25,\n  value = c(\"x\", \"y\"),\n  size = 12,\n  color = \"black\"\n)\n\ng \u003c- plot_data_set(initial_set_dfs, \"\", NULL, NULL) +\n  geom_text(data = df_names, family = \"Fira Mono\", size = 24)\n\nsave_static_plot(g, \"original-dfs-set-ops\")\n```\n\n```{r remove-set-ops-ids}\nx \u003c- x %\u003e% select(-id)\ny \u003c- y %\u003e% select(-id)\n```\n\n\u003cimg src=\"images/static/png/original-dfs-set-ops.png\" width=\"480px\" /\u003e\n\n```{r echo=TRUE}\nx\ny \n```\n\n### Union\n\n\u003e All unique rows from `x` and `y`.\n\n```{r union}\nsource(\"R/union.R\")\n\u003c\u003cremove-set-ops-ids\u003e\u003e\n```\n\n![](images/union.gif)\n\n```{r echo=TRUE}\nunion(x, y)\n```\n\n![](images/union-rev.gif)\n\n```{r echo=TRUE}\nunion(y, x)\n```\n\n### Union All\n\n\u003e All rows from `x` and `y`, keeping duplicates.\n\n```{r union-all}\nsource(\"R/union_all.R\")\n\u003c\u003cremove-set-ops-ids\u003e\u003e\n```\n\n![](images/union-all.gif)\n\n\n```{r echo=TRUE}\nunion_all(x, y)\n```\n\n\n### Intersection\n\n\u003e Common rows in both `x` and `y`, keeping just unique rows.\n\n```{r intersect}\nsource(\"R/intersect.R\")\n\u003c\u003cremove-set-ops-ids\u003e\u003e\n```\n\n![](images/intersect.gif)\n\n```{r echo=TRUE}\nintersect(x, y)\n```\n\n### Set Difference\n\n\u003e All rows from `x` which are not also rows in `y`, keeping just unique rows.\n\n```{r setdiff}\nsource(\"R/setdiff.R\")\n\u003c\u003cremove-set-ops-ids\u003e\u003e\n```\n\n![](images/setdiff.gif)\n\n```{r echo=TRUE}\nsetdiff(x, y)\n```\n\n![](images/setdiff-rev.gif)\n\n```{r echo=TRUE}\nsetdiff(y, x)\n```\n\n## Tidy Data\n\n[Tidy data][r4ds-tidy-data] follows the following three rules:\n\n1. Each variable has its own column.\n1. Each observation has its own row.\n1. Each value has its own cell.\n\nMany of the tools in the [tidyverse] expect data to be formatted as a tidy dataset and the [tidyr] package provides functions to help you organize your data into tidy data.\n\n```{r tidyr-wide-long, fig.width = 6, fig.height = 10}\nsource(\"R/tidyr_pivoting.R\")\nsource(\"R/tidyr_spread_gather.R\")\n\ntidy_plots \u003c- list()\ntidy_plots$wide \u003c- bind_rows(sg_wide, sg_wide_labels)\ntidy_plots$long \u003c- bind_rows(sg_long, sg_long_labels)\n\ntidy_plots \u003c- map(tidy_plots, ~ mutate(., \n  .text_color = ifelse(grepl(\"id|key|val\", value), \"black\", \"white\"),\n  .text_size  = ifelse(grepl(\"id|key|val\", value), 6, 10)\n)) %\u003e% \n  imap(~ plot_data(.x, .y))\n\ntidy_plots$wide \u003c- tidy_plots$wide + ylim(-6.5, 0.5)\n\nsave_static_plot(\n  patchwork::wrap_plots(tidy_plots, widths = 4, heights = 8),\n  width = 8,\n  height = 8,\n  \"original-dfs-tidy\"\n)\n```\n\n![](images/static/png/original-dfs-tidy.png)\n\n```{r echo=TRUE}\nwide\nlong\n```\n\n### Pivot Wider and Longer\n\n`pivot_wider()` and `pivot_longer()` were introduced in [tidyr version 1.0](https://www.tidyverse.org/blog/2019/09/tidyr-1-0-0/#pivoting) (released in September 2019).\nThey provide a more consistent and more powerful approach to changing the fundamental shape of the data and are \"modern alternatives to `spread()` and `gather()`. \n\nHere we show the very basic mechanics of pivoting, but there's much more that the pivot functions can do.\nYou can learn more about them in the [Pivoting vignette in tidyr](https://tidyr.tidyverse.org/articles/pivot.html).\n\n```r\npivot_wider(data, names_from = key, values_from = val)\n```\n\n\u003e `pivot_wider()` \"widens\" data, increasing the number of columns and decreasing the number of rows.\n\n```r\npivot_longer(data, cols = x:y, names_to = \"key\", values_to = \"val\")\n```\n\n\u003e `pivot_longer()` \"lengthens\" data, increasing the number of rows and decreasing the number of columns.\n\n![](images/tidyr-pivoting.gif)\n\n### Spread and Gather\n\n```r\nspread(data, key, value)\n```\n\n\u003e Spread a key-value pair across multiple columns. \n\u003e Use it when an a column contains observations from multiple variables.\n\n```r\ngather(data, key = \"key\", value = \"value\", ...)\n```\n\n\u003e Gather takes multiple columns and collapses into key-value pairs, duplicating all other columns as needed. \n\u003e You use `gather()` when you notice that your column names are not names of variables, but *values* of a variable.\n\n![](images/tidyr-spread-gather.gif)\n\n```{r echo=TRUE}\ngather(wide, key, val, x:z)\nspread(long, key, val)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgadenbuie%2Ftidyexplain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgadenbuie%2Ftidyexplain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgadenbuie%2Ftidyexplain/lists"}