Creating summary tables

In spreadsheet, pivot tables or dynamic tables are powerful tools for summarizing data in different ways. We can create these tables using the group_by and summarize functions from the tidyverse package.

Let’s take a look at our report, right after we read the data, we create a summary table with the number of cases and the average body mass for each specie in our dataset.

We can use group_by() to group our data by species and summarize() to calculate the number of cases and the average body_mass_g for each grouping.

penguins %>% 
  group_by(species) %>% 
  summarise(count = n(),
            mean_body_mass = mean(body_mass_g, na.rm = TRUE))
## # A tibble: 3 × 3
##   species   count mean_body_mass
##   <chr>     <int>          <dbl>
## 1 Adelie      152          3701.
## 2 Chinstrap    68          3733.
## 3 Gentoo      124          5076.

The function n() returns the number of cases and the function mean computes the average. The parameter na.rm = TRUE tells the function to remove non available values (NA) before performing the calculation.

Question: What if you don’t use group_by() before summarise()? Let’s try it and discuss what’s going on.

penguins %>% 
  summarise(count = n(),
            mean_body_mass = mean(body_mass_g, na.rm = TRUE))
## # A tibble: 1 × 2
##   count mean_body_mass
##   <int>          <dbl>
## 1   344          4202.

So if we don’t group_by first, we will get a single summary statistic (n and mean in this case) for the whole dataset.

Now, we want the average of the body mass in kilograms and with two decimals. We can change our summarise() call to get that calculation.

penguins %>% 
  group_by(species) %>% 
  summarise(count = n(),
            mean_body_mass = round(mean(body_mass_g/1000, na.rm = TRUE), 2))
## # A tibble: 3 × 3
##   species   count mean_body_mass
##   <chr>     <int>          <dbl>
## 1 Adelie      152           3.7 
## 2 Chinstrap    68           3.73
## 3 Gentoo      124           5.08

First we divide body mass by 1000 to transform from grams to kilograms and then we use the function round() to get only two decimals.

Now we are at the point where we actually want to save this summary information as a variable so we can use it in further analyses and formatting.

So let’s add a variable assignment to that first line:

summary <- penguins %>% 
  group_by(species) %>% 
  summarise(count = n(),
            mean_body_mass = round(mean(body_mass_g/1000, na.rm = TRUE), 2))

Formatig the table

To make our table look better in a report, we have to change some of its elements. For example, the name of the variables should be informative for a person (at the moment they are useful names for a computer), and we can add a title to it and change the alignment of the numbers.

We can do that with the kable function.

summary %>% 
  knitr::kable(col.names = c("Specie", "Count", "Mean Body Mass"),
               align = "lcc",
               caption = "Summary of penguins on the data set by specie.")
Summary of penguins on the data set by specie.
Specie Count Mean Body Mass
Adelie 152 3.70
Chinstrap 68 3.73
Gentoo 124 5.08

We can do a lot more with other functions (or “verbs”) of the tidyverse. Let’s see a couple more

Creating new variables

There are a lot of times in which we want to operate beyond the original data. This is often done by adding a column. We do this with the mutate() function.

We can add a new variable (called prop) representing the proportion of penguins in each species with by using mutate().

penguins %>% 
  group_by(species) %>% 
  summarise(count = n(),
            mean_body_mass = round(mean(body_mass_g/1000, na.rm = TRUE), 2)) %>%
  mutate(prop = count/nrow(penguins))
## # A tibble: 3 × 4
##   species   count mean_body_mass  prop
##   <chr>     <int>          <dbl> <dbl>
## 1 Adelie      152           3.7  0.442
## 2 Chinstrap    68           3.73 0.198
## 3 Gentoo      124           5.08 0.360

Filtering data

In our report we also make plot by species. To do this we need to keep only the data of the species we want to analyse. We use the filter() function to filter data frames, this function returns a new data frame with only the rows that satisfy some conditions.

We can use logical operators (>, <, >=, <=, ==) to create conditions from numeric variables These operators are also useful for dates and text strings. Look at the code to see a condition for text type of data.

penguins %>% 
  filter(species == "Gentoo")
## # A tibble: 124 × 8
##    species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
##    <chr>   <chr>           <dbl>         <dbl>             <dbl>       <dbl>
##  1 Gentoo  Biscoe           46.1          13.2               211        4500
##  2 Gentoo  Biscoe           50            16.3               230        5700
##  3 Gentoo  Biscoe           48.7          14.1               210        4450
##  4 Gentoo  Biscoe           50            15.2               218        5700
##  5 Gentoo  Biscoe           47.6          14.5               215        5400
##  6 Gentoo  Biscoe           46.5          13.5               210        4550
##  7 Gentoo  Biscoe           45.4          14.6               211        4800
##  8 Gentoo  Biscoe           46.7          15.3               219        5200
##  9 Gentoo  Biscoe           43.3          13.4               209        4400
## 10 Gentoo  Biscoe           46.8          15.4               215        5150
## # ℹ 114 more rows
## # ℹ 2 more variables: sex <chr>, year <dbl>

Activity: Write code to filter all the penguins in the Torgersen island.

Activity: Now write the code to filter all the penguins with flipper lenght greater than 200

LS0tCnRpdGxlOiAiRGF0YSBtYW5pcHVsYXRpb24iCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiBmYWxzZQogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCgllY2hvID0gVFJVRSwKCW1lc3NhZ2UgPSBGQUxTRSwKCXdhcm5pbmcgPSBGQUxTRQopCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKcGVuZ3VpbnMgPC0gcmVhZF9jc3YoImRhdGEvcGVuZ3VpbnMuY3N2IikKYGBgCgojIyBDcmVhdGluZyBzdW1tYXJ5IHRhYmxlcwoKSW4gc3ByZWFkc2hlZXQsIHBpdm90IHRhYmxlcyBvciBkeW5hbWljIHRhYmxlcyBhcmUgcG93ZXJmdWwgdG9vbHMgZm9yIHN1bW1hcml6aW5nIGRhdGEgaW4gZGlmZmVyZW50IHdheXMuIFdlIGNhbiBjcmVhdGUgdGhlc2UgdGFibGVzIHVzaW5nIHRoZSBgZ3JvdXBfYnlgIGFuZCBgc3VtbWFyaXplYCBmdW5jdGlvbnMgZnJvbSB0aGUgdGlkeXZlcnNlIHBhY2thZ2UuIAoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgb3VyIHJlcG9ydCwgcmlnaHQgYWZ0ZXIgd2UgcmVhZCB0aGUgZGF0YSwgd2UgY3JlYXRlIGEgc3VtbWFyeSB0YWJsZSB3aXRoIHRoZSBudW1iZXIgb2YgY2FzZXMgYW5kIHRoZSBhdmVyYWdlIGJvZHkgbWFzcyBmb3IgZWFjaCBzcGVjaWUgaW4gb3VyIGRhdGFzZXQuCgpXZSBjYW4gdXNlIGBncm91cF9ieSgpYCB0byBncm91cCBvdXIgZGF0YSBieSAqKnNwZWNpZXMqKiBhbmQgYHN1bW1hcml6ZSgpYCB0byBjYWxjdWxhdGUgdGhlIG51bWJlciBvZiBjYXNlcyBhbmQgdGhlIGF2ZXJhZ2UgKipib2R5X21hc3NfZyoqIGZvciBlYWNoIGdyb3VwaW5nLgoKYGBge3J9CgpwZW5ndWlucyAlPiUgCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lIAogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwKICAgICAgICAgICAgbWVhbl9ib2R5X21hc3MgPSBtZWFuKGJvZHlfbWFzc19nLCBuYS5ybSA9IFRSVUUpKQoKYGBgCgpUaGUgZnVuY3Rpb24gYG4oKWAgcmV0dXJucyB0aGUgbnVtYmVyIG9mIGNhc2VzIGFuZCB0aGUgZnVuY3Rpb24gYG1lYW5gIGNvbXB1dGVzIHRoZSBhdmVyYWdlLiBUaGUgcGFyYW1ldGVyIGBuYS5ybSA9IFRSVUVgIHRlbGxzIHRoZSBmdW5jdGlvbiB0byByZW1vdmUgbm9uIGF2YWlsYWJsZSB2YWx1ZXMgKGBOQWApIGJlZm9yZSBwZXJmb3JtaW5nIHRoZSBjYWxjdWxhdGlvbi4KCj4gUXVlc3Rpb246IFdoYXQgaWYgeW91IGRvbuKAmXQgdXNlIGBncm91cF9ieSgpYCBiZWZvcmUgYHN1bW1hcmlzZSgpYD8gTGV04oCZcyB0cnkgaXQgYW5kIGRpc2N1c3Mgd2hhdOKAmXMgZ29pbmcgb24uCgpgYGB7cn0KcGVuZ3VpbnMgJT4lIAogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwKICAgICAgICAgICAgbWVhbl9ib2R5X21hc3MgPSBtZWFuKGJvZHlfbWFzc19nLCBuYS5ybSA9IFRSVUUpKQpgYGAKClNvIGlmIHdlIGRvbid0IGdyb3VwX2J5IGZpcnN0LCB3ZSB3aWxsIGdldCBhIHNpbmdsZSBzdW1tYXJ5IHN0YXRpc3RpYyAobiBhbmQgbWVhbiBpbiB0aGlzIGNhc2UpIGZvciB0aGUgd2hvbGUgZGF0YXNldC4KCk5vdywgd2Ugd2FudCB0aGUgYXZlcmFnZSBvZiB0aGUgYm9keSBtYXNzIGluIGtpbG9ncmFtcyBhbmQgd2l0aCB0d28gZGVjaW1hbHMuIFdlIGNhbiBjaGFuZ2Ugb3VyIGBzdW1tYXJpc2UoKWAgY2FsbCB0byBnZXQgdGhhdCBjYWxjdWxhdGlvbi4gIAoKYGBge3J9CnBlbmd1aW5zICU+JSAKICBncm91cF9ieShzcGVjaWVzKSAlPiUgCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpLAogICAgICAgICAgICBtZWFuX2JvZHlfbWFzcyA9IHJvdW5kKG1lYW4oYm9keV9tYXNzX2cvMTAwMCwgbmEucm0gPSBUUlVFKSwgMikpCmBgYAoKRmlyc3Qgd2UgZGl2aWRlIGJvZHkgbWFzcyBieSAxMDAwIHRvIHRyYW5zZm9ybSBmcm9tIGdyYW1zIHRvIGtpbG9ncmFtcyBhbmQgdGhlbiB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGByb3VuZCgpYCB0byBnZXQgb25seSB0d28gZGVjaW1hbHMuCgpOb3cgd2UgYXJlIGF0IHRoZSBwb2ludCB3aGVyZSB3ZSBhY3R1YWxseSB3YW50IHRvIHNhdmUgdGhpcyBzdW1tYXJ5IGluZm9ybWF0aW9uIGFzIGEgdmFyaWFibGUgc28gd2UgY2FuIHVzZSBpdCBpbiBmdXJ0aGVyIGFuYWx5c2VzIGFuZCBmb3JtYXR0aW5nLgoKU28gbGV0J3MgYWRkIGEgdmFyaWFibGUgYXNzaWdubWVudCB0byB0aGF0IGZpcnN0IGxpbmU6CgpgYGB7cn0Kc3VtbWFyeSA8LSBwZW5ndWlucyAlPiUgCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lIAogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwKICAgICAgICAgICAgbWVhbl9ib2R5X21hc3MgPSByb3VuZChtZWFuKGJvZHlfbWFzc19nLzEwMDAsIG5hLnJtID0gVFJVRSksIDIpKQpgYGAKCiMjIEZvcm1hdGlnIHRoZSB0YWJsZQoKVG8gbWFrZSBvdXIgdGFibGUgbG9vayBiZXR0ZXIgaW4gYSByZXBvcnQsIHdlIGhhdmUgdG8gY2hhbmdlIHNvbWUgb2YgaXRzIGVsZW1lbnRzLiBGb3IgZXhhbXBsZSwgdGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlcyBzaG91bGQgYmUgaW5mb3JtYXRpdmUgZm9yIGEgcGVyc29uIChhdCB0aGUgbW9tZW50IHRoZXkgYXJlIHVzZWZ1bCBuYW1lcyBmb3IgYSBjb21wdXRlciksIGFuZCB3ZSBjYW4gYWRkIGEgdGl0bGUgdG8gaXQgYW5kIGNoYW5nZSB0aGUgYWxpZ25tZW50IG9mIHRoZSBudW1iZXJzLgoKV2UgY2FuIGRvIHRoYXQgd2l0aCB0aGUgYGthYmxlYCBmdW5jdGlvbi4KCmBgYHtyfQpzdW1tYXJ5ICU+JSAKICBrbml0cjo6a2FibGUoY29sLm5hbWVzID0gYygiU3BlY2llIiwgIkNvdW50IiwgIk1lYW4gQm9keSBNYXNzIiksCiAgICAgICAgICAgICAgIGFsaWduID0gImxjYyIsCiAgICAgICAgICAgICAgIGNhcHRpb24gPSAiU3VtbWFyeSBvZiBwZW5ndWlucyBvbiB0aGUgZGF0YSBzZXQgYnkgc3BlY2llLiIpCmBgYAoKV2UgY2FuIGRvIGEgbG90IG1vcmUgd2l0aCBvdGhlciBmdW5jdGlvbnMgKG9yICJ2ZXJicyIpIG9mIHRoZSB0aWR5dmVyc2UuIExldCdzIHNlZSBhIGNvdXBsZSBtb3JlCgojIyBDcmVhdGluZyBuZXcgdmFyaWFibGVzCgpUaGVyZSBhcmUgYSBsb3Qgb2YgdGltZXMgaW4gd2hpY2ggd2Ugd2FudCB0byBvcGVyYXRlIGJleW9uZCB0aGUgb3JpZ2luYWwgZGF0YS4gVGhpcyBpcyBvZnRlbiBkb25lIGJ5IGFkZGluZyBhIGNvbHVtbi4gV2UgZG8gdGhpcyB3aXRoIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uLgoKV2UgY2FuIGFkZCBhIG5ldyB2YXJpYWJsZSAoY2FsbGVkIGBwcm9wYCkgcmVwcmVzZW50aW5nIHRoZSBwcm9wb3J0aW9uIG9mIHBlbmd1aW5zIGluIGVhY2ggKipzcGVjaWVzKiogd2l0aCBieSB1c2luZyBgbXV0YXRlKClgLiAKCmBgYHtyfQpwZW5ndWlucyAlPiUgCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lIAogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwKICAgICAgICAgICAgbWVhbl9ib2R5X21hc3MgPSByb3VuZChtZWFuKGJvZHlfbWFzc19nLzEwMDAsIG5hLnJtID0gVFJVRSksIDIpKSAlPiUKICBtdXRhdGUocHJvcCA9IGNvdW50L25yb3cocGVuZ3VpbnMpKQoKYGBgCgojIyBGaWx0ZXJpbmcgZGF0YQoKSW4gb3VyIHJlcG9ydCB3ZSBhbHNvIG1ha2UgcGxvdCBieSBzcGVjaWVzLiBUbyBkbyB0aGlzIHdlIG5lZWQgdG8ga2VlcCBvbmx5IHRoZSBkYXRhIG9mIHRoZSBzcGVjaWVzIHdlIHdhbnQgdG8gYW5hbHlzZS4gIFdlIHVzZSB0aGUgYGZpbHRlcigpYCBmdW5jdGlvbiB0byBmaWx0ZXIgZGF0YSBmcmFtZXMsIHRoaXMgZnVuY3Rpb24gcmV0dXJucyBhIG5ldyBkYXRhIGZyYW1lIHdpdGggb25seSB0aGUgcm93cyB0aGF0IHNhdGlzZnkgc29tZSBjb25kaXRpb25zLgoKV2UgY2FuIHVzZSBsb2dpY2FsIG9wZXJhdG9ycyAoYD5gLCBgPGAsIGA+PWAsIGA8PWAsIGA9PWApIHRvIGNyZWF0ZSBjb25kaXRpb25zIGZyb20gbnVtZXJpYyB2YXJpYWJsZXMgIFRoZXNlIG9wZXJhdG9ycyBhcmUgYWxzbyB1c2VmdWwgZm9yIGRhdGVzIGFuZCB0ZXh0IHN0cmluZ3MuIExvb2sgYXQgdGhlIGNvZGUgdG8gc2VlIGEgY29uZGl0aW9uIGZvciB0ZXh0IHR5cGUgb2YgZGF0YS4KCmBgYHtyfQpwZW5ndWlucyAlPiUgCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkdlbnRvbyIpCmBgYAoKPiBBY3Rpdml0eTogV3JpdGUgY29kZSB0byBmaWx0ZXIgYWxsIHRoZSBwZW5ndWlucyBpbiB0aGUgVG9yZ2Vyc2VuIGlzbGFuZC4KCj4gQWN0aXZpdHk6IE5vdyB3cml0ZSB0aGUgY29kZSB0byBmaWx0ZXIgYWxsIHRoZSBwZW5ndWlucyB3aXRoIGZsaXBwZXIgbGVuZ2h0IGdyZWF0ZXIgdGhhbiAyMDAKCg==