Re-render book for O'Reilly
This commit is contained in:
		@@ -1,15 +1,15 @@
 | 
			
		||||
<section data-type="chapter" id="chp-rectangling">
 | 
			
		||||
<h1><span id="sec-rectangling" class="quarto-section-identifier d-none d-lg-block"><span class="chapter-title">Data rectangling</span></span></h1>
 | 
			
		||||
<h1><span id="sec-rectangling" class="quarto-section-identifier d-none d-lg-block"><span class="chapter-title">Hierarchical data</span></span></h1>
 | 
			
		||||
<section id="introduction" data-type="sect1">
 | 
			
		||||
<h1>
 | 
			
		||||
Introduction</h1>
 | 
			
		||||
<p>In this chapter, you’ll learn the art of data <strong>rectangling</strong>, taking data that is fundamentally tree-like and converting it into a rectangular data frames made up of rows and columns. This is important because hierarchical data is surprisingly common, especially when working with data that comes from the web.</p>
 | 
			
		||||
<p>In this chapter, you’ll learn the art of data <strong>rectangling</strong>, taking data that is fundamentally hierarchical, or tree-like, and converting it into a rectangular data frame made up of rows and columns. This is important because hierarchical data is surprisingly common, especially when working with data that comes from the web.</p>
 | 
			
		||||
<p>To learn about rectangling, you’ll need to first learn about lists, the data structure that makes hierarchical data possible. Then you’ll learn about two crucial tidyr functions: <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">tidyr::unnest_longer()</a></code> and <code><a href="https://tidyr.tidyverse.org/reference/unnest_wider.html">tidyr::unnest_wider()</a></code>. We’ll then show you a few case studies, applying these simple functions again and again to solve real problems. We’ll finish off by talking about JSON, the most frequent source of hierarchical datasets and a common format for data exchange on the web.</p>
 | 
			
		||||
 | 
			
		||||
<section id="prerequisites" data-type="sect2">
 | 
			
		||||
<h2>
 | 
			
		||||
Prerequisites</h2>
 | 
			
		||||
<p>In this chapter we’ll use many functions from tidyr, a core member of the tidyverse. We’ll also use repurrrsive to provide some interesting datasets for rectangling practice, and we’ll finish by using jsonlite to read JSON files into R lists.</p>
 | 
			
		||||
<p>In this chapter, we’ll use many functions from tidyr, a core member of the tidyverse. We’ll also use repurrrsive to provide some interesting datasets for rectangling practice, and we’ll finish by using jsonlite to read JSON files into R lists.</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">library(tidyverse)
 | 
			
		||||
library(repurrrsive)
 | 
			
		||||
@@ -21,7 +21,7 @@ library(jsonlite)</pre>
 | 
			
		||||
<section id="lists" data-type="sect1">
 | 
			
		||||
<h1>
 | 
			
		||||
Lists</h1>
 | 
			
		||||
<p>So far you’ve worked with data frames that contain simple vectors like integers, numbers, characters, date-times, and factors. These vectors are simple because they’re homogeneous: every element is the same type. If you want to store element of different types in the same vector, you’ll need a <strong>list</strong>, which you create with <code><a href="https://rdrr.io/r/base/list.html">list()</a></code>:</p>
 | 
			
		||||
<p>So far you’ve worked with data frames that contain simple vectors like integers, numbers, characters, date-times, and factors. These vectors are simple because they’re homogeneous: every element is of the same data type. If you want to store elements of different types in the same vector, you’ll need a <strong>list</strong>, which you create with <code><a href="https://rdrr.io/r/base/list.html">list()</a></code>:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">x1 <- list(1:4, "a", TRUE)
 | 
			
		||||
x1
 | 
			
		||||
@@ -135,7 +135,7 @@ str(x5)
 | 
			
		||||
<section id="list-columns" data-type="sect2">
 | 
			
		||||
<h2>
 | 
			
		||||
List-columns</h2>
 | 
			
		||||
<p>Lists can also live inside a tibble, where we call them list-columns. List-columns are useful because they allow you to shoehorn in objects that wouldn’t usually belong in a tibble. In particular, list-columns are are used a lot in the <a href="https://www.tidymodels.org">tidymodels</a> ecosystem, because they allow you to store things like models or resamples in a data frame.</p>
 | 
			
		||||
<p>Lists can also live inside a tibble, where we call them list-columns. List-columns are useful because they allow you to place objects in a tibble that wouldn’t usually belong in there. In particular, list-columns are used a lot in the <a href="https://www.tidymodels.org">tidymodels</a> ecosystem, because they allow you to store things like model outputs or resamples in a data frame.</p>
 | 
			
		||||
<p>Here’s a simple example of a list-column:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">df <- tibble(
 | 
			
		||||
@@ -160,7 +160,7 @@ df
 | 
			
		||||
#> 1     1 a     <list [2]></pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Computing with list-columns is harder, but that’s because computing with lists is harder in general; we’ll come back to that in <a href="#chp-iteration" data-type="xref">#chp-iteration</a>. In this chapter, we’ll focus on unnesting list-columns out into regular variables so you can use your existing tools on them.</p>
 | 
			
		||||
<p>The default print method just displays a rough summary of the contents. The list column could be arbitrarily complex, so there’s no good way to print it. If you want to see it, you’ll need to pull the list-column out and apply one of the techniques that you learned above:</p>
 | 
			
		||||
<p>The default print method just displays a rough summary of the contents. The list column could be arbitrarily complex, so there’s no good way to print it. If you want to see it, you’ll need to pull the list-column out and apply one of the techniques that you’ve learned above:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">df |> 
 | 
			
		||||
  filter(x == 1) |> 
 | 
			
		||||
@@ -188,7 +188,7 @@ Base R
 | 
			
		||||
#>         x       y
 | 
			
		||||
#> 1    1, 2    1, 2
 | 
			
		||||
#> 2 3, 4, 5 3, 4, 5</pre>
 | 
			
		||||
</div><p>It’s easier to use list-columns with tibbles because <code><a href="https://tibble.tidyverse.org/reference/tibble.html">tibble()</a></code> treats lists like either vectors and the print method has been designed with lists in mind.</p></div>
 | 
			
		||||
</div><p>It’s easier to use list-columns with tibbles because <code><a href="https://tibble.tidyverse.org/reference/tibble.html">tibble()</a></code> treats lists like vectors and the print method has been designed with lists in mind.</p></div>
 | 
			
		||||
 | 
			
		||||
</section>
 | 
			
		||||
</section>
 | 
			
		||||
@@ -307,7 +307,7 @@ df6 |> unnest_longer(y)
 | 
			
		||||
#> 5     3    31 a    
 | 
			
		||||
#> 6     3    32 b</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>If you don’t want these <code>ids</code>, you can suppress them with <code>indices_include = FALSE</code>. On the other hand, it’s sometimes useful to retain the position of unnamed elements in unnamed list-columns. You can do this with <code>indices_include = TRUE</code>:</p>
 | 
			
		||||
<p>If you don’t want these <code>ids</code>, you can suppress them with <code>indices_include = FALSE</code>. On the other hand, sometimes the positions of the elements is meaningful, and even if the elements are unnamed, you might still want to track their indices. You can do this with <code>indices_include = TRUE</code>:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">df2 |> 
 | 
			
		||||
  unnest_longer(y, indices_include = TRUE)
 | 
			
		||||
@@ -326,7 +326,7 @@ df6 |> unnest_longer(y)
 | 
			
		||||
<section id="inconsistent-types" data-type="sect2">
 | 
			
		||||
<h2>
 | 
			
		||||
Inconsistent types</h2>
 | 
			
		||||
<p>What happens if you unnest a list-column contains different types of vector? For example, take the following dataset where the list-column <code>y</code> contains two numbers, a factor, and a logical, which can’t normally be mixed in a single column.</p>
 | 
			
		||||
<p>What happens if you unnest a list-column that contains different types of vector? For example, take the following dataset where the list-column <code>y</code> contains two numbers, a factor, and a logical, which can’t normally be mixed in a single column.</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">df4 <- tribble(
 | 
			
		||||
  ~x, ~y,
 | 
			
		||||
@@ -334,7 +334,7 @@ Inconsistent types</h2>
 | 
			
		||||
  "b", list(TRUE, factor("a"), 5)
 | 
			
		||||
)</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p><code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> always keeps the set of columns change, while changing the number of rows. So what happens? How does <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> produce five rows while keeping everything in <code>y</code>?</p>
 | 
			
		||||
<p><code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> always keeps the set of columns unchanged, while changing the number of rows. So what happens? How does <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> produce five rows while keeping everything in <code>y</code>?</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">df4 |> 
 | 
			
		||||
  unnest_longer(y)
 | 
			
		||||
@@ -348,7 +348,7 @@ Inconsistent types</h2>
 | 
			
		||||
#> 5 b     <dbl [1]></pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>As you can see, the output contains a list-column, but every element of the list-column contains a single element. Because <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> can’t find a common type of vector, it keeps the original types in a list-column. You might wonder if this breaks the commandment that every element of a column must be the same type — not quite: every element is a still a list, even though the contents of each element is a different type.</p>
 | 
			
		||||
<p>What happens if you find this problem in a dataset you’re trying to rectangle? There are two basic options. You could use the <code>transform</code> argument to coerce all inputs to a common type. It’s not particularly useful here because there’s only really one class that these five class can be converted to character.</p>
 | 
			
		||||
<p>What happens if you find this problem in a dataset you’re trying to rectangle? There are two basic options. You could use the <code>transform</code> argument to coerce all inputs to a common type. However, it’s not particularly useful here because there’s only really one class that these five class can be converted to character.</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">df4 |> 
 | 
			
		||||
  unnest_longer(y, transform = as.character)
 | 
			
		||||
@@ -372,7 +372,7 @@ Inconsistent types</h2>
 | 
			
		||||
#> 1 a     <dbl [1]>
 | 
			
		||||
#> 2 b     <dbl [1]></pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Then you can call <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> once more:</p>
 | 
			
		||||
<p>Then you can call <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> once more. This gives us a rectangular dataset of just the numeric values.</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">df4 |> 
 | 
			
		||||
  unnest_longer(y) |> 
 | 
			
		||||
@@ -392,12 +392,12 @@ Inconsistent types</h2>
 | 
			
		||||
Other functions</h2>
 | 
			
		||||
<p>tidyr has a few other useful rectangling functions that we’re not going to cover in this book:</p>
 | 
			
		||||
<ul><li>
 | 
			
		||||
<code><a href="https://tidyr.tidyverse.org/reference/unnest_auto.html">unnest_auto()</a></code> automatically picks between <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> and <code><a href="https://tidyr.tidyverse.org/reference/unnest_wider.html">unnest_wider()</a></code> based on the structure of the list-column. It’s a great for rapid exploration, but ultimately its a bad idea because it doesn’t force you to understand how your data is structured, and makes your code harder to understand.</li>
 | 
			
		||||
<code><a href="https://tidyr.tidyverse.org/reference/unnest_auto.html">unnest_auto()</a></code> automatically picks between <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> and <code><a href="https://tidyr.tidyverse.org/reference/unnest_wider.html">unnest_wider()</a></code> based on the structure of the list-column. It’s great for rapid exploration, but ultimately it’s a bad idea because it doesn’t force you to understand how your data is structured, and makes your code harder to understand.</li>
 | 
			
		||||
<li>
 | 
			
		||||
<code><a href="https://tidyr.tidyverse.org/reference/unnest.html">unnest()</a></code> expands both rows and columns. It’s useful when you have a list-column that contains a 2d structure like a data frame, which you don’t see in this book.</li>
 | 
			
		||||
<code><a href="https://tidyr.tidyverse.org/reference/unnest.html">unnest()</a></code> expands both rows and columns. It’s useful when you have a list-column that contains a 2d structure like a data frame, which you don’t see in this book, but you might encounter if you use the <a href="https://www.tmwr.org/base-r.html#combining-base-r-models-and-the-tidyverse">tidymodels</a> ecosystem.</li>
 | 
			
		||||
<li>
 | 
			
		||||
<code><a href="https://tidyr.tidyverse.org/reference/hoist.html">hoist()</a></code> allows you to reach into a deeply nested list and extract just the components that you need. It’s mostly equivalent to repeated invocations of <code><a href="https://tidyr.tidyverse.org/reference/unnest_wider.html">unnest_wider()</a></code> + <code><a href="https://dplyr.tidyverse.org/reference/select.html">select()</a></code> so read up on it if you’re trying to extract just a couple of important variables embedded in a bunch of data that you don’t care about.</li>
 | 
			
		||||
</ul><p>These are good to know about when you’re reading other people’s code or tackling rarer rectangling challenges.</p>
 | 
			
		||||
</ul><p>These functions are good to know about as you might encounter them when reading other people’s code or tackling rarer rectangling challenges yourself.</p>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
<section id="exercises" data-type="sect2">
 | 
			
		||||
@@ -424,7 +424,7 @@ Case studies</h1>
 | 
			
		||||
<section id="very-wide-data" data-type="sect2">
 | 
			
		||||
<h2>
 | 
			
		||||
Very wide data</h2>
 | 
			
		||||
<p>We’ll with <code>gh_repos</code>. This is a list that contains data about a collection of GitHub repositories retrieved using the GitHub API. It’s a very deeply nested list so it’s difficult to show the structure in this book; you might want to explore a little on your own with <code>View(gh_repos)</code> before we continue.</p>
 | 
			
		||||
<p>We’ll start with <code>gh_repos</code>. This is a list that contains data about a collection of GitHub repositories retrieved using the GitHub API. It’s a very deeply nested list so it’s difficult to show the structure in this book; we recommend exploring a little on your own with <code>View(gh_repos)</code> before we continue.</p>
 | 
			
		||||
<p><code>gh_repos</code> is a list, but our tools work with list-columns, so we’ll begin by putting it into a tibble. We call the column <code>json</code> for reasons we’ll get to later.</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">repos <- tibble(json = gh_repos)
 | 
			
		||||
@@ -460,21 +460,21 @@ repos
 | 
			
		||||
  unnest_longer(json) |> 
 | 
			
		||||
  unnest_wider(json) 
 | 
			
		||||
#> # A tibble: 176 × 68
 | 
			
		||||
#>         id name      full_…¹ owner        private html_…² descr…³ fork  url  
 | 
			
		||||
#>      <int> <chr>     <chr>   <list>       <lgl>   <chr>   <chr>   <lgl> <chr>
 | 
			
		||||
#> 1 61160198 after     gaborc… <named list> FALSE   https:… Run Co… FALSE http…
 | 
			
		||||
#> 2 40500181 argufy    gaborc… <named list> FALSE   https:… Declar… FALSE http…
 | 
			
		||||
#> 3 36442442 ask       gaborc… <named list> FALSE   https:… Friend… FALSE http…
 | 
			
		||||
#> 4 34924886 baseimpo… gaborc… <named list> FALSE   https:… Do we … FALSE http…
 | 
			
		||||
#> 5 61620661 citest    gaborc… <named list> FALSE   https:… Test R… TRUE  http…
 | 
			
		||||
#> 6 33907457 clisymbo… gaborc… <named list> FALSE   https:… Unicod… FALSE http…
 | 
			
		||||
#> # … with 170 more rows, 59 more variables: forks_url <chr>, keys_url <chr>,
 | 
			
		||||
#> #   collaborators_url <chr>, teams_url <chr>, hooks_url <chr>,
 | 
			
		||||
#> #   issue_events_url <chr>, events_url <chr>, assignees_url <chr>,
 | 
			
		||||
#> #   branches_url <chr>, tags_url <chr>, blobs_url <chr>, git_tags_url <chr>,
 | 
			
		||||
#> #   git_refs_url <chr>, trees_url <chr>, statuses_url <chr>,
 | 
			
		||||
#> #   languages_url <chr>, stargazers_url <chr>, contributors_url <chr>,
 | 
			
		||||
#> #   subscribers_url <chr>, subscription_url <chr>, commits_url <chr>, …</pre>
 | 
			
		||||
#>         id name     full_name owner        private html_url description fork 
 | 
			
		||||
#>      <int> <chr>    <chr>     <list>       <lgl>   <chr>    <chr>       <lgl>
 | 
			
		||||
#> 1 61160198 after    gaborcsa… <named list> FALSE   https:/… Run Code i… FALSE
 | 
			
		||||
#> 2 40500181 argufy   gaborcsa… <named list> FALSE   https:/… Declarativ… FALSE
 | 
			
		||||
#> 3 36442442 ask      gaborcsa… <named list> FALSE   https:/… Friendly C… FALSE
 | 
			
		||||
#> 4 34924886 baseimp… gaborcsa… <named list> FALSE   https:/… Do we get … FALSE
 | 
			
		||||
#> 5 61620661 citest   gaborcsa… <named list> FALSE   https:/… Test R pac… TRUE 
 | 
			
		||||
#> 6 33907457 clisymb… gaborcsa… <named list> FALSE   https:/… Unicode sy… FALSE
 | 
			
		||||
#> # … with 170 more rows, and 60 more variables: url <chr>, forks_url <chr>,
 | 
			
		||||
#> #   keys_url <chr>, collaborators_url <chr>, teams_url <chr>,
 | 
			
		||||
#> #   hooks_url <chr>, issue_events_url <chr>, events_url <chr>,
 | 
			
		||||
#> #   assignees_url <chr>, branches_url <chr>, tags_url <chr>,
 | 
			
		||||
#> #   blobs_url <chr>, git_tags_url <chr>, git_refs_url <chr>,
 | 
			
		||||
#> #   trees_url <chr>, statuses_url <chr>, languages_url <chr>,
 | 
			
		||||
#> #   stargazers_url <chr>, contributors_url <chr>, subscribers_url <chr>, …</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>This has worked but the result is a little overwhelming: there are so many columns that tibble doesn’t even print all of them! We can see them all with <code><a href="https://rdrr.io/r/base/names.html">names()</a></code>:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
@@ -531,7 +531,7 @@ repos
 | 
			
		||||
  unnest_wider(json) |> 
 | 
			
		||||
  select(id, full_name, owner, description) |> 
 | 
			
		||||
  unnest_wider(owner)
 | 
			
		||||
#> Error in `unpack()`:
 | 
			
		||||
#> Error in `unpack()` at ]8;line = 121:col = 2;file:///Users/hadleywickham/Documents/tidy-data/tidyr/R/unnest-wider.Rtidyr/R/unnest-wider.R:121:2]8;;:
 | 
			
		||||
#> ! Names must be unique.
 | 
			
		||||
#> ✖ These names are duplicated:
 | 
			
		||||
#>   * "id" at locations 1 and 4.
 | 
			
		||||
@@ -546,21 +546,21 @@ repos
 | 
			
		||||
  select(id, full_name, owner, description) |> 
 | 
			
		||||
  unnest_wider(owner, names_sep = "_")
 | 
			
		||||
#> # A tibble: 176 × 20
 | 
			
		||||
#>         id full_name  owner…¹ owner…² owner…³ owner…⁴ owner…⁵ owner…⁶ owner…⁷
 | 
			
		||||
#>      <int> <chr>      <chr>     <int> <chr>   <chr>   <chr>   <chr>   <chr>  
 | 
			
		||||
#> 1 61160198 gaborcsar… gaborc…  660288 https:… ""      https:… https:… https:…
 | 
			
		||||
#> 2 40500181 gaborcsar… gaborc…  660288 https:… ""      https:… https:… https:…
 | 
			
		||||
#> 3 36442442 gaborcsar… gaborc…  660288 https:… ""      https:… https:… https:…
 | 
			
		||||
#> 4 34924886 gaborcsar… gaborc…  660288 https:… ""      https:… https:… https:…
 | 
			
		||||
#> 5 61620661 gaborcsar… gaborc…  660288 https:… ""      https:… https:… https:…
 | 
			
		||||
#> 6 33907457 gaborcsar… gaborc…  660288 https:… ""      https:… https:… https:…
 | 
			
		||||
#> # … with 170 more rows, 11 more variables: owner_following_url <chr>,
 | 
			
		||||
#> #   owner_gists_url <chr>, owner_starred_url <chr>,
 | 
			
		||||
#> #   owner_subscriptions_url <chr>, owner_organizations_url <chr>,
 | 
			
		||||
#> #   owner_repos_url <chr>, owner_events_url <chr>,
 | 
			
		||||
#> #   owner_received_events_url <chr>, owner_type <chr>,
 | 
			
		||||
#> #   owner_site_admin <lgl>, description <chr>, and abbreviated variable
 | 
			
		||||
#> #   names ¹owner_login, ²owner_id, ³owner_avatar_url, ⁴owner_gravatar_id, …</pre>
 | 
			
		||||
#>         id full_name  owner_login owner_id owner_avatar_url owner_gravatar_id
 | 
			
		||||
#>      <int> <chr>      <chr>          <int> <chr>            <chr>            
 | 
			
		||||
#> 1 61160198 gaborcsar… gaborcsardi   660288 https://avatars… ""               
 | 
			
		||||
#> 2 40500181 gaborcsar… gaborcsardi   660288 https://avatars… ""               
 | 
			
		||||
#> 3 36442442 gaborcsar… gaborcsardi   660288 https://avatars… ""               
 | 
			
		||||
#> 4 34924886 gaborcsar… gaborcsardi   660288 https://avatars… ""               
 | 
			
		||||
#> 5 61620661 gaborcsar… gaborcsardi   660288 https://avatars… ""               
 | 
			
		||||
#> 6 33907457 gaborcsar… gaborcsardi   660288 https://avatars… ""               
 | 
			
		||||
#> # … with 170 more rows, and 14 more variables: owner_url <chr>,
 | 
			
		||||
#> #   owner_html_url <chr>, owner_followers_url <chr>,
 | 
			
		||||
#> #   owner_following_url <chr>, owner_gists_url <chr>,
 | 
			
		||||
#> #   owner_starred_url <chr>, owner_subscriptions_url <chr>,
 | 
			
		||||
#> #   owner_organizations_url <chr>, owner_repos_url <chr>,
 | 
			
		||||
#> #   owner_events_url <chr>, owner_received_events_url <chr>,
 | 
			
		||||
#> #   owner_type <chr>, owner_site_admin <lgl>, description <chr></pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>This gives another wide dataset, but you can see that <code>owner</code> appears to contain a lot of additional data about the person who “owns” the repository.</p>
 | 
			
		||||
</section>
 | 
			
		||||
@@ -568,7 +568,7 @@ repos
 | 
			
		||||
<section id="relational-data" data-type="sect2">
 | 
			
		||||
<h2>
 | 
			
		||||
Relational data</h2>
 | 
			
		||||
<p>Nested data is sometimes used to represent data that we’d usually spread out into multiple data frames. For example, take <code>got_chars</code>. Like <code>gh_repos</code> it’s a list, so we start by turning it into a list-column of a tibble:</p>
 | 
			
		||||
<p>Nested data is sometimes used to represent data that we’d usually spread out into multiple data frames. For example, take <code>got_chars</code> which contains data about characters that appear in Game of Thrones. Like <code>gh_repos</code> it’s a list, so we start by turning it into a list-column of a tibble:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">chars <- tibble(json = got_chars)
 | 
			
		||||
chars
 | 
			
		||||
@@ -623,15 +623,15 @@ characters
 | 
			
		||||
  unnest_wider(json) |> 
 | 
			
		||||
  select(id, where(is.list))
 | 
			
		||||
#> # A tibble: 30 × 8
 | 
			
		||||
#>      id titles    aliases    allegiances books     povBooks  tvSeries playe…¹
 | 
			
		||||
#>   <int> <list>    <list>     <list>      <list>    <list>    <list>   <list> 
 | 
			
		||||
#> 1  1022 <chr [3]> <chr [4]>  <chr [1]>   <chr [3]> <chr [2]> <chr>    <chr>  
 | 
			
		||||
#> 2  1052 <chr [2]> <chr [11]> <chr [1]>   <chr [2]> <chr [4]> <chr>    <chr>  
 | 
			
		||||
#> 3  1074 <chr [2]> <chr [1]>  <chr [1]>   <chr [3]> <chr [2]> <chr>    <chr>  
 | 
			
		||||
#> 4  1109 <chr [1]> <chr [1]>  <NULL>      <chr [1]> <chr [1]> <chr>    <chr>  
 | 
			
		||||
#> 5  1166 <chr [1]> <chr [1]>  <chr [1]>   <chr [3]> <chr [2]> <chr>    <chr>  
 | 
			
		||||
#> 6  1267 <chr [1]> <chr [1]>  <NULL>      <chr [2]> <chr [1]> <chr>    <chr>  
 | 
			
		||||
#> # … with 24 more rows, and abbreviated variable name ¹playedBy</pre>
 | 
			
		||||
#>      id titles    aliases    allegiances books     povBooks tvSeries playedBy
 | 
			
		||||
#>   <int> <list>    <list>     <list>      <list>    <list>   <list>   <list>  
 | 
			
		||||
#> 1  1022 <chr [2]> <chr [4]>  <chr [1]>   <chr [3]> <chr>    <chr>    <chr>   
 | 
			
		||||
#> 2  1052 <chr [2]> <chr [11]> <chr [1]>   <chr [2]> <chr>    <chr>    <chr>   
 | 
			
		||||
#> 3  1074 <chr [2]> <chr [1]>  <chr [1]>   <chr [3]> <chr>    <chr>    <chr>   
 | 
			
		||||
#> 4  1109 <chr [1]> <chr [1]>  <NULL>      <chr [1]> <chr>    <chr>    <chr>   
 | 
			
		||||
#> 5  1166 <chr [1]> <chr [1]>  <chr [1]>   <chr [3]> <chr>    <chr>    <chr>   
 | 
			
		||||
#> 6  1267 <chr [1]> <chr [1]>  <NULL>      <chr [2]> <chr>    <chr>    <chr>   
 | 
			
		||||
#> # … with 24 more rows</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Lets explore the <code>titles</code> column. It’s an unnamed list-column, so we’ll unnest it into rows:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
@@ -639,16 +639,16 @@ characters
 | 
			
		||||
  unnest_wider(json) |> 
 | 
			
		||||
  select(id, titles) |> 
 | 
			
		||||
  unnest_longer(titles)
 | 
			
		||||
#> # A tibble: 60 × 2
 | 
			
		||||
#> # A tibble: 59 × 2
 | 
			
		||||
#>      id titles                                              
 | 
			
		||||
#>   <int> <chr>                                               
 | 
			
		||||
#> 1  1022 Prince of Winterfell                                
 | 
			
		||||
#> 2  1022 Captain of Sea Bitch                                
 | 
			
		||||
#> 3  1022 Lord of the Iron Islands (by law of the green lands)
 | 
			
		||||
#> 4  1052 Acting Hand of the King (former)                    
 | 
			
		||||
#> 5  1052 Master of Coin (former)                             
 | 
			
		||||
#> 6  1074 Lord Captain of the Iron Fleet                      
 | 
			
		||||
#> # … with 54 more rows</pre>
 | 
			
		||||
#> 2  1022 Lord of the Iron Islands (by law of the green lands)
 | 
			
		||||
#> 3  1052 Acting Hand of the King (former)                    
 | 
			
		||||
#> 4  1052 Master of Coin (former)                             
 | 
			
		||||
#> 5  1074 Lord Captain of the Iron Fleet                      
 | 
			
		||||
#> 6  1074 Master of the Iron Victory                          
 | 
			
		||||
#> # … with 53 more rows</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>You might expect to see this data in its own table because it would be easy to join to the characters data as needed. To do so, we’ll do a little cleaning: removing the rows containing empty strings and renaming <code>titles</code> to <code>title</code> since each row now only contains a single title.</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
@@ -659,43 +659,42 @@ characters
 | 
			
		||||
  filter(titles != "") |> 
 | 
			
		||||
  rename(title = titles)
 | 
			
		||||
titles
 | 
			
		||||
#> # A tibble: 53 × 2
 | 
			
		||||
#> # A tibble: 52 × 2
 | 
			
		||||
#>      id title                                               
 | 
			
		||||
#>   <int> <chr>                                               
 | 
			
		||||
#> 1  1022 Prince of Winterfell                                
 | 
			
		||||
#> 2  1022 Captain of Sea Bitch                                
 | 
			
		||||
#> 3  1022 Lord of the Iron Islands (by law of the green lands)
 | 
			
		||||
#> 4  1052 Acting Hand of the King (former)                    
 | 
			
		||||
#> 5  1052 Master of Coin (former)                             
 | 
			
		||||
#> 6  1074 Lord Captain of the Iron Fleet                      
 | 
			
		||||
#> # … with 47 more rows</pre>
 | 
			
		||||
#> 2  1022 Lord of the Iron Islands (by law of the green lands)
 | 
			
		||||
#> 3  1052 Acting Hand of the King (former)                    
 | 
			
		||||
#> 4  1052 Master of Coin (former)                             
 | 
			
		||||
#> 5  1074 Lord Captain of the Iron Fleet                      
 | 
			
		||||
#> 6  1074 Master of the Iron Victory                          
 | 
			
		||||
#> # … with 46 more rows</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Now, for example, we could use this table tofind all the characters that are captains and see all their titles:</p>
 | 
			
		||||
<p>Now, for example, we could use this table to find all the characters that are captains and see all their titles:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">captains <- titles |> filter(str_detect(title, "Captain"))
 | 
			
		||||
captains
 | 
			
		||||
#> # A tibble: 5 × 2
 | 
			
		||||
#> # A tibble: 4 × 2
 | 
			
		||||
#>      id title                                 
 | 
			
		||||
#>   <int> <chr>                                 
 | 
			
		||||
#> 1  1022 Captain of Sea Bitch                  
 | 
			
		||||
#> 2  1074 Lord Captain of the Iron Fleet        
 | 
			
		||||
#> 3  1166 Captain of the Guard at Sunspear      
 | 
			
		||||
#> 4   150 Captain of the Black Wind             
 | 
			
		||||
#> 5    60 Captain of the Golden Storm (formerly)
 | 
			
		||||
#> 1  1074 Lord Captain of the Iron Fleet        
 | 
			
		||||
#> 2  1166 Captain of the Guard at Sunspear      
 | 
			
		||||
#> 3   150 Captain of the Black Wind             
 | 
			
		||||
#> 4    60 Captain of the Golden Storm (formerly)
 | 
			
		||||
 | 
			
		||||
characters |> 
 | 
			
		||||
  select(id, name) |> 
 | 
			
		||||
  inner_join(titles, by = "id", multiple = "all")
 | 
			
		||||
#> # A tibble: 53 × 3
 | 
			
		||||
#> # A tibble: 52 × 3
 | 
			
		||||
#>      id name              title                                              
 | 
			
		||||
#>   <int> <chr>             <chr>                                              
 | 
			
		||||
#> 1  1022 Theon Greyjoy     Prince of Winterfell                               
 | 
			
		||||
#> 2  1022 Theon Greyjoy     Captain of Sea Bitch                               
 | 
			
		||||
#> 3  1022 Theon Greyjoy     Lord of the Iron Islands (by law of the green land…
 | 
			
		||||
#> 4  1052 Tyrion Lannister  Acting Hand of the King (former)                   
 | 
			
		||||
#> 5  1052 Tyrion Lannister  Master of Coin (former)                            
 | 
			
		||||
#> 6  1074 Victarion Greyjoy Lord Captain of the Iron Fleet                     
 | 
			
		||||
#> # … with 47 more rows</pre>
 | 
			
		||||
#> 2  1022 Theon Greyjoy     Lord of the Iron Islands (by law of the green land…
 | 
			
		||||
#> 3  1052 Tyrion Lannister  Acting Hand of the King (former)                   
 | 
			
		||||
#> 4  1052 Tyrion Lannister  Master of Coin (former)                            
 | 
			
		||||
#> 5  1074 Victarion Greyjoy Lord Captain of the Iron Fleet                     
 | 
			
		||||
#> 6  1074 Victarion Greyjoy Master of the Iron Victory                         
 | 
			
		||||
#> # … with 46 more rows</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>You could imagine creating a table like this for each of the list-columns, then using joins to combine them with the character data as you need it.</p>
 | 
			
		||||
</section>
 | 
			
		||||
@@ -703,36 +702,36 @@ characters |>
 | 
			
		||||
<section id="a-dash-of-text-analysis" data-type="sect2">
 | 
			
		||||
<h2>
 | 
			
		||||
A dash of text analysis</h2>
 | 
			
		||||
<p>What if we wanted to find the most common words in the title? One simple approach starts by using <code><a href="https://stringr.tidyverse.org/reference/str_split.html">str_split()</a></code> to break each element of <code>title</code> up into words by spitting on <code>" "</code>:</p>
 | 
			
		||||
<p>Sticking with the same data, what if we wanted to find the most common words in the title? One simple approach starts by using <code><a href="https://stringr.tidyverse.org/reference/str_split.html">str_split()</a></code> to break each element of <code>title</code> up into words by splitting on <code>" "</code>:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">titles |> 
 | 
			
		||||
  mutate(word = str_split(title, " "), .keep = "unused")
 | 
			
		||||
#> # A tibble: 53 × 2
 | 
			
		||||
#> # A tibble: 52 × 2
 | 
			
		||||
#>      id word      
 | 
			
		||||
#>   <int> <list>    
 | 
			
		||||
#> 1  1022 <chr [3]> 
 | 
			
		||||
#> 2  1022 <chr [4]> 
 | 
			
		||||
#> 3  1022 <chr [11]>
 | 
			
		||||
#> 4  1052 <chr [6]> 
 | 
			
		||||
#> 5  1052 <chr [4]> 
 | 
			
		||||
#> 6  1074 <chr [6]> 
 | 
			
		||||
#> # … with 47 more rows</pre>
 | 
			
		||||
#> 2  1022 <chr [11]>
 | 
			
		||||
#> 3  1052 <chr [6]> 
 | 
			
		||||
#> 4  1052 <chr [4]> 
 | 
			
		||||
#> 5  1074 <chr [6]> 
 | 
			
		||||
#> 6  1074 <chr [5]> 
 | 
			
		||||
#> # … with 46 more rows</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>This creates a unnamed variable length list-column, so we can use <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code>:</p>
 | 
			
		||||
<p>This creates an unnamed variable length list-column, so we can use <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code>:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">titles |> 
 | 
			
		||||
  mutate(word = str_split(title, " "), .keep = "unused") |> 
 | 
			
		||||
  unnest_longer(word)
 | 
			
		||||
#> # A tibble: 202 × 2
 | 
			
		||||
#> # A tibble: 198 × 2
 | 
			
		||||
#>      id word      
 | 
			
		||||
#>   <int> <chr>     
 | 
			
		||||
#> 1  1022 Prince    
 | 
			
		||||
#> 2  1022 of        
 | 
			
		||||
#> 3  1022 Winterfell
 | 
			
		||||
#> 4  1022 Captain   
 | 
			
		||||
#> 4  1022 Lord      
 | 
			
		||||
#> 5  1022 of        
 | 
			
		||||
#> 6  1022 Sea       
 | 
			
		||||
#> # … with 196 more rows</pre>
 | 
			
		||||
#> 6  1022 the       
 | 
			
		||||
#> # … with 192 more rows</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>And then we can count that column to find the most common words:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
@@ -740,18 +739,18 @@ A dash of text analysis</h2>
 | 
			
		||||
  mutate(word = str_split(title, " "), .keep = "unused") |> 
 | 
			
		||||
  unnest_longer(word) |> 
 | 
			
		||||
  count(word, sort = TRUE)
 | 
			
		||||
#> # A tibble: 78 × 2
 | 
			
		||||
#>   word        n
 | 
			
		||||
#>   <chr>   <int>
 | 
			
		||||
#> 1 of         41
 | 
			
		||||
#> 2 the        29
 | 
			
		||||
#> 3 Lord        9
 | 
			
		||||
#> 4 Hand        6
 | 
			
		||||
#> 5 Captain     5
 | 
			
		||||
#> 6 King        5
 | 
			
		||||
#> # … with 72 more rows</pre>
 | 
			
		||||
#> # A tibble: 77 × 2
 | 
			
		||||
#>   word         n
 | 
			
		||||
#>   <chr>    <int>
 | 
			
		||||
#> 1 of          40
 | 
			
		||||
#> 2 the         29
 | 
			
		||||
#> 3 Lord         9
 | 
			
		||||
#> 4 Hand         6
 | 
			
		||||
#> 5 King         5
 | 
			
		||||
#> 6 Princess     5
 | 
			
		||||
#> # … with 71 more rows</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Some of those words are not very interesting so we could create a list of common words to drop. In text analysis these is commonly called stop words.</p>
 | 
			
		||||
<p>Some of those words are not very interesting so we could create a list of common words to drop. In text analysis these are commonly called stop words.</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">stop_words <- tibble(word = c("of", "the"))
 | 
			
		||||
 | 
			
		||||
@@ -761,16 +760,16 @@ titles |>
 | 
			
		||||
  anti_join(stop_words) |> 
 | 
			
		||||
  count(word, sort = TRUE)
 | 
			
		||||
#> Joining with `by = join_by(word)`
 | 
			
		||||
#> # A tibble: 76 × 2
 | 
			
		||||
#> # A tibble: 75 × 2
 | 
			
		||||
#>   word         n
 | 
			
		||||
#>   <chr>    <int>
 | 
			
		||||
#> 1 Lord         9
 | 
			
		||||
#> 2 Hand         6
 | 
			
		||||
#> 3 Captain      5
 | 
			
		||||
#> 4 King         5
 | 
			
		||||
#> 5 Princess     5
 | 
			
		||||
#> 6 Queen        5
 | 
			
		||||
#> # … with 70 more rows</pre>
 | 
			
		||||
#> 3 King         5
 | 
			
		||||
#> 4 Princess     5
 | 
			
		||||
#> 5 Queen        5
 | 
			
		||||
#> 6 Ser          5
 | 
			
		||||
#> # … with 69 more rows</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Breaking up text into individual fragments is a powerful idea that underlies much of text analysis. If this sounds interesting, a good place to learn more is <a href="https://www.tidytextmining.com">Text Mining with R</a> by Julia Silge and David Robinson.</p>
 | 
			
		||||
</section>
 | 
			
		||||
@@ -803,7 +802,7 @@ Deeply nested</h2>
 | 
			
		||||
#> 4 Chicago    <list [1]> OK    
 | 
			
		||||
#> 5 Arlington  <list [2]> OK</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>This gives us the <code>status</code> and the <code>results</code>. We’ll drop the status column since they’re all <code>OK</code>; in a real analysis, you’d also want capture all the rows where <code>status != "OK"</code> and figure out what went wrong. <code>results</code> is an unnamed list, with either one or two elements (we’ll see why shortly) so we’ll unnest it into rows:</p>
 | 
			
		||||
<p>This gives us the <code>status</code> and the <code>results</code>. We’ll drop the status column since they’re all <code>OK</code>; in a real analysis, you’d also want to capture all the rows where <code>status != "OK"</code> and figure out what went wrong. <code>results</code> is an unnamed list, with either one or two elements (we’ll see why shortly) so we’ll unnest it into rows:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">gmaps_cities |> 
 | 
			
		||||
  unnest_wider(json) |> 
 | 
			
		||||
@@ -829,15 +828,15 @@ Deeply nested</h2>
 | 
			
		||||
  unnest_wider(results)
 | 
			
		||||
locations
 | 
			
		||||
#> # A tibble: 7 × 6
 | 
			
		||||
#>   city       address_components formatted_address geometry     place…¹ types 
 | 
			
		||||
#>   <chr>      <list>             <chr>             <list>       <chr>   <list>
 | 
			
		||||
#> 1 Houston    <list [4]>         Houston, TX, USA  <named list> ChIJAY… <list>
 | 
			
		||||
#> 2 Washington <list [2]>         Washington, USA   <named list> ChIJ-b… <list>
 | 
			
		||||
#> 3 Washington <list [4]>         Washington, DC, … <named list> ChIJW-… <list>
 | 
			
		||||
#> 4 New York   <list [3]>         New York, NY, USA <named list> ChIJOw… <list>
 | 
			
		||||
#> 5 Chicago    <list [4]>         Chicago, IL, USA  <named list> ChIJ7c… <list>
 | 
			
		||||
#> 6 Arlington  <list [4]>         Arlington, TX, U… <named list> ChIJ05… <list>
 | 
			
		||||
#> # … with 1 more row, and abbreviated variable name ¹place_id</pre>
 | 
			
		||||
#>   city       address_compone…¹ formatted_address geometry     place_id types 
 | 
			
		||||
#>   <chr>      <list>            <chr>             <list>       <chr>    <list>
 | 
			
		||||
#> 1 Houston    <list [4]>        Houston, TX, USA  <named list> ChIJAYW… <list>
 | 
			
		||||
#> 2 Washington <list [2]>        Washington, USA   <named list> ChIJ-bD… <list>
 | 
			
		||||
#> 3 Washington <list [4]>        Washington, DC, … <named list> ChIJW-T… <list>
 | 
			
		||||
#> 4 New York   <list [3]>        New York, NY, USA <named list> ChIJOwg… <list>
 | 
			
		||||
#> 5 Chicago    <list [4]>        Chicago, IL, USA  <named list> ChIJ7cv… <list>
 | 
			
		||||
#> 6 Arlington  <list [4]>        Arlington, TX, U… <named list> ChIJ05g… <list>
 | 
			
		||||
#> # … with 1 more row, and abbreviated variable name ¹address_components</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Now we can see why two cities got two results: Washington matched both Washington state and Washington, DC, and Arlington matched Arlington, Virginia and Arlington, Texas.</p>
 | 
			
		||||
<p>There are few different places we could go from here. We might want to determine the exact location of the match, which is stored in the <code>geometry</code> list-column:</p>
 | 
			
		||||
@@ -846,15 +845,15 @@ locations
 | 
			
		||||
  select(city, formatted_address, geometry) |> 
 | 
			
		||||
  unnest_wider(geometry)
 | 
			
		||||
#> # A tibble: 7 × 6
 | 
			
		||||
#>   city       formatted_address bounds       location     locat…¹ viewport    
 | 
			
		||||
#>   <chr>      <chr>             <list>       <list>       <chr>   <list>      
 | 
			
		||||
#> 1 Houston    Houston, TX, USA  <named list> <named list> APPROX… <named list>
 | 
			
		||||
#> 2 Washington Washington, USA   <named list> <named list> APPROX… <named list>
 | 
			
		||||
#> 3 Washington Washington, DC, … <named list> <named list> APPROX… <named list>
 | 
			
		||||
#> 4 New York   New York, NY, USA <named list> <named list> APPROX… <named list>
 | 
			
		||||
#> 5 Chicago    Chicago, IL, USA  <named list> <named list> APPROX… <named list>
 | 
			
		||||
#> 6 Arlington  Arlington, TX, U… <named list> <named list> APPROX… <named list>
 | 
			
		||||
#> # … with 1 more row, and abbreviated variable name ¹location_type</pre>
 | 
			
		||||
#>   city       formatted_address   bounds           location     location_type
 | 
			
		||||
#>   <chr>      <chr>               <list>           <list>       <chr>        
 | 
			
		||||
#> 1 Houston    Houston, TX, USA    <named list [2]> <named list> APPROXIMATE  
 | 
			
		||||
#> 2 Washington Washington, USA     <named list [2]> <named list> APPROXIMATE  
 | 
			
		||||
#> 3 Washington Washington, DC, USA <named list [2]> <named list> APPROXIMATE  
 | 
			
		||||
#> 4 New York   New York, NY, USA   <named list [2]> <named list> APPROXIMATE  
 | 
			
		||||
#> 5 Chicago    Chicago, IL, USA    <named list [2]> <named list> APPROXIMATE  
 | 
			
		||||
#> 6 Arlington  Arlington, TX, USA  <named list [2]> <named list> APPROXIMATE  
 | 
			
		||||
#> # … with 1 more row, and 1 more variable: viewport <list></pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>That gives us new <code>bounds</code> (a rectangular region) and <code>location</code> (a point). We can unnest <code>location</code> to see the latitude (<code>lat</code>) and longitude (<code>lng</code>):</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
@@ -863,15 +862,15 @@ locations
 | 
			
		||||
  unnest_wider(geometry) |> 
 | 
			
		||||
  unnest_wider(location)
 | 
			
		||||
#> # A tibble: 7 × 7
 | 
			
		||||
#>   city       formatted_address bounds         lat    lng locat…¹ viewport    
 | 
			
		||||
#>   <chr>      <chr>             <list>       <dbl>  <dbl> <chr>   <list>      
 | 
			
		||||
#> 1 Houston    Houston, TX, USA  <named list>  29.8  -95.4 APPROX… <named list>
 | 
			
		||||
#> 2 Washington Washington, USA   <named list>  47.8 -121.  APPROX… <named list>
 | 
			
		||||
#> 3 Washington Washington, DC, … <named list>  38.9  -77.0 APPROX… <named list>
 | 
			
		||||
#> 4 New York   New York, NY, USA <named list>  40.7  -74.0 APPROX… <named list>
 | 
			
		||||
#> 5 Chicago    Chicago, IL, USA  <named list>  41.9  -87.6 APPROX… <named list>
 | 
			
		||||
#> 6 Arlington  Arlington, TX, U… <named list>  32.7  -97.1 APPROX… <named list>
 | 
			
		||||
#> # … with 1 more row, and abbreviated variable name ¹location_type</pre>
 | 
			
		||||
#>   city       formatted_address   bounds             lat    lng location_type
 | 
			
		||||
#>   <chr>      <chr>               <list>           <dbl>  <dbl> <chr>        
 | 
			
		||||
#> 1 Houston    Houston, TX, USA    <named list [2]>  29.8  -95.4 APPROXIMATE  
 | 
			
		||||
#> 2 Washington Washington, USA     <named list [2]>  47.8 -121.  APPROXIMATE  
 | 
			
		||||
#> 3 Washington Washington, DC, USA <named list [2]>  38.9  -77.0 APPROXIMATE  
 | 
			
		||||
#> 4 New York   New York, NY, USA   <named list [2]>  40.7  -74.0 APPROXIMATE  
 | 
			
		||||
#> 5 Chicago    Chicago, IL, USA    <named list [2]>  41.9  -87.6 APPROXIMATE  
 | 
			
		||||
#> 6 Arlington  Arlington, TX, USA  <named list [2]>  32.7  -97.1 APPROXIMATE  
 | 
			
		||||
#> # … with 1 more row, and 1 more variable: viewport <list></pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Extracting the bounds requires a few more steps:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
@@ -913,7 +912,7 @@ locations
 | 
			
		||||
#> # … with 1 more row</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Note how we unnest two columns simultaneously by supplying a vector of variable names to <code><a href="https://tidyr.tidyverse.org/reference/unnest_wider.html">unnest_wider()</a></code>.</p>
 | 
			
		||||
<p>This is somewhere that <code><a href="https://tidyr.tidyverse.org/reference/hoist.html">hoist()</a></code>, mentioned briefly above, can be useful. Once you’ve discovered the path to get to the components you’re interested in, you can extract them directly using <code><a href="https://tidyr.tidyverse.org/reference/hoist.html">hoist()</a></code>:</p>
 | 
			
		||||
<p>This is where <code><a href="https://tidyr.tidyverse.org/reference/hoist.html">hoist()</a></code>, mentioned earlier in the chapter, can be useful. Once you’ve discovered the path to get to the components you’re interested in, you can extract them directly using <code><a href="https://tidyr.tidyverse.org/reference/hoist.html">hoist()</a></code>:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">locations |> 
 | 
			
		||||
  select(city, formatted_address, geometry) |> 
 | 
			
		||||
@@ -972,7 +971,7 @@ Data types</h2>
 | 
			
		||||
<p>JSON is a simple format designed to be easily read and written by machines, not humans. It has six key data types. Four of them are scalars:</p>
 | 
			
		||||
<ul><li>The simplest type is a null (<code>null</code>) which plays the same role as both <code>NULL</code> and <code>NA</code> in R. It represents the absence of data.</li>
 | 
			
		||||
<li>A <strong>string</strong> is much like a string in R, but must always use double quotes.</li>
 | 
			
		||||
<li>A <strong>number</strong> is similar to R’s numbers: they can use integer (e.g. 123), decimal (e.g. 123.45), or scientific (e.g. 1.23e3) notation. JSON doesn’t support Inf, -Inf, or NaN.</li>
 | 
			
		||||
<li>A <strong>number</strong> is similar to R’s numbers: they can use integer (e.g. 123), decimal (e.g. 123.45), or scientific (e.g. 1.23e3) notation. JSON doesn’t support <code>Inf</code>, <code>-Inf</code>, or <code>NaN</code>.</li>
 | 
			
		||||
<li>A <strong>boolean</strong> is similar to R’s <code>TRUE</code> and <code>FALSE</code>, but uses lowercase <code>true</code> and <code>false</code>.</li>
 | 
			
		||||
</ul><p>JSON’s strings, numbers, and booleans are pretty similar to R’s character, numeric, and logical vectors. The main difference is that JSON’s scalars can only represent a single value. To represent multiple values you need to use one of the two remaining types: arrays and objects.</p>
 | 
			
		||||
<p>Both arrays and objects are similar to lists in R; the difference is whether or not they’re named. An <strong>array</strong> is like an unnamed list, and is written with <code>[]</code>. For example <code>[1, 2, 3]</code> is an array containing 3 numbers, and <code>[null, 1, "string", false]</code> is an array that contains a null, a number, a string, and a boolean. An <strong>object</strong> is like a named list, and is written with <code><a href="https://rdrr.io/r/base/Paren.html">{}</a></code>. The names (keys in JSON terminology) are strings, so must be surrounded by quotes. For example, <code>{"x": 1, "y": 2}</code> is an object that maps <code>x</code> to 1 and <code>y</code> to 2.</p>
 | 
			
		||||
@@ -994,7 +993,7 @@ gh_users2 <- read_json(gh_users_json())
 | 
			
		||||
identical(gh_users, gh_users2)
 | 
			
		||||
#> [1] TRUE</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>In this book, I’ll also use <code><a href="https://rdrr.io/pkg/jsonlite/man/read_json.html">parse_json()</a></code>, since it takes a string containing JSON, which makes it good for generating simple examples. To get started, here’s three simple JSON datasets, starting with a number, then putting a few number in an array, then putting that array in an object:</p>
 | 
			
		||||
<p>In this book, we’ll also use <code><a href="https://rdrr.io/pkg/jsonlite/man/read_json.html">parse_json()</a></code>, since it takes a string containing JSON, which makes it good for generating simple examples. To get started, here are three simple JSON datasets, starting with a number, then putting a few numbers in an array, then putting that array in an object:</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">str(parse_json('1'))
 | 
			
		||||
#>  int 1
 | 
			
		||||
@@ -1038,7 +1037,7 @@ df |>
 | 
			
		||||
#> 1 John     34
 | 
			
		||||
#> 2 Susan    27</pre>
 | 
			
		||||
</div>
 | 
			
		||||
<p>In rarer cases, the JSON consists of a single top-level JSON object, representing one “thing”. In this case, you’ll need to kick off the rectangling process by wrapping it a list, before you put it in a tibble.</p>
 | 
			
		||||
<p>In rarer cases, the JSON file consists of a single top-level JSON object, representing one “thing”. In this case, you’ll need to kick off the rectangling process by wrapping it in a list, before you put it in a tibble.</p>
 | 
			
		||||
<div class="cell">
 | 
			
		||||
<pre data-type="programlisting" data-code-language="r">json <- '{
 | 
			
		||||
  "status": "OK", 
 | 
			
		||||
@@ -1114,7 +1113,7 @@ df_row <- tibble(json = json_row)</pre>
 | 
			
		||||
<section id="summary" data-type="sect1">
 | 
			
		||||
<h1>
 | 
			
		||||
Summary</h1>
 | 
			
		||||
<p>In this chapter, you learned what lists are, how you can generate the from JSON files, and how turn them into rectangular data frames. Surprisingly we only need two new functions: <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> to put list elements into rows and <code><a href="https://tidyr.tidyverse.org/reference/unnest_wider.html">unnest_wider()</a></code> to put list elements into columns. It doesn’t matter how deeply nested the list-column is, all you need to do is repeatedly call these two functions.</p>
 | 
			
		||||
<p>In this chapter, you learned what lists are, how you can generate them from JSON files, and how turn them into rectangular data frames. Surprisingly we only need two new functions: <code><a href="https://tidyr.tidyverse.org/reference/unnest_longer.html">unnest_longer()</a></code> to put list elements into rows and <code><a href="https://tidyr.tidyverse.org/reference/unnest_wider.html">unnest_wider()</a></code> to put list elements into columns. It doesn’t matter how deeply nested the list-column is, all you need to do is repeatedly call these two functions.</p>
 | 
			
		||||
<p>JSON is the most common data format returned by web APIs. What happens if the website doesn’t have an API, but you can see data you want on the website? That’s the topic of the next chapter: web scraping, extracting data from HTML webpages.</p>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user