Data Visualization Notes

These are my notes for GRD 610A: Data Visualization II in Winter 2021 at the College for Creative Studies. These notes are for my work in the book Data Visualization by Kieran Healy (Princeton University Press, 2019).

Get Started

Everything has a name

Objects in R are created and referred to by their names. Certain names are not allowed because they are reserved words such as TRUE, if, mean(), and NA. Names also cannot start with a number or contain spaces. There are different naming conventions.

Snake Case
my_data
this_is_snake_case

Camel Case
myData
thisIsCamelCase

Pascal Case
MyData
ThisIsPascalCase

Pick one naming convention and stick with it. Be consistent; donโ€™t switch between conventions. I recommend snake case.

# This is a comment (it starts with #)

my_data <- c(1, 2, 3, 4) # Assign using <- ; use ALT + - or OPTION + -

My_Data  
## Error in eval(expr, envir, enclos): object 'My_Data' not found
# Cannot be found because we called it my_data (lowercase)

# Now we can see it
my_data 
## [1] 1 2 3 4

Everything is an object; using functions

Think of functions like a recipe. The arguments of the function are the ingredients and what happens within the function is the sequence of cooking steps.

c(1, 2, 3, 1, 3, 5, 25) # c() is the combine function, it puts things together into a vector/list
## [1]  1  2  3  1  3  5 25
my_numbers <- c(1, 2, 3, 1, 3, 5, 25)
your_numbers <- c(5, 31, 71, 1, 3, 21, 6)

my_numbers
## [1]  1  2  3  1  3  5 25
mean(x = my_numbers)
## [1] 5.714286
mean(my_numbers) # you don't have to specify the argument names, but order matters if you do not specify
## [1] 5.714286
mean(x = your_numbers)
## [1] 19.71429
my_summary <- summary(my_numbers)

my_summary
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   1.500   3.000   5.714   4.000  25.000
table(my_numbers)
## my_numbers
##  1  2  3  5 25 
##  2  1  2  1  1
sd(my_numbers)
## [1] 8.616153
my_numbers * 5
## [1]   5  10  15   5  15  25 125
my_numbers + 1
## [1]  2  3  4  2  4  6 26
my_numbers + my_numbers # How is this different than the last line?
## [1]  2  4  6  2  6 10 50
# If you're not sure what an object is, ask for its class or type

class(my_numbers)
## [1] "numeric"
class(my_summary)
## [1] "summaryDefault" "table"
class(summary)
## [1] "function"
my_new_vector <- c(my_numbers, "Apple") # What happens if we combine a word with numbers?

my_new_vector
## [1] "1"     "2"     "3"     "1"     "3"     "5"     "25"    "Apple"
class(my_new_vector)
## [1] "character"
# Let's look at a new dataset

titanic
##       fate    sex    n percent
## 1 perished   male 1364    62.0
## 2 perished female  126     5.7
## 3 survived   male  367    16.7
## 4 survived female  344    15.6
class(titanic) 
## [1] "data.frame"
# Titanic is a data frame, which is like a table
# The $ operator can be used to access a column of a data frame by name

titanic$percent
## [1] 62.0  5.7 16.7 15.6
# Tibbles are slightly different than data frames. They are also data tables, but they provide more information.

titanic_tb <- as_tibble(titanic)

titanic_tb # How is does this compare to titanic above?
## # A tibble: 4 x 4
##   fate     sex        n percent
##   <fct>    <fct>  <dbl>   <dbl>
## 1 perished male    1364    62  
## 2 perished female   126     5.7
## 3 survived male     367    16.7
## 4 survived female   344    15.6
# To see inside an object, ask for its structure

str(my_numbers)
##  num [1:7] 1 2 3 1 3 5 25
str(my_summary)
##  'summaryDefault' Named num [1:6] 1 1.5 3 5.71 4 ...
##  - attr(*, "names")= chr [1:6] "Min." "1st Qu." "Median" "Mean" ...

Programming in R can be challenging and it takes time to get used to. Be patient and take a break if you get stuck. Make sure parentheses are opened and closed. Complete your commands (look out for the + in the console). Take your time and lookout for typos.

Get Data into R

In this section, we will get data from a URL and make a quick figure.

# Data source
url <- "https://cdn.rawgit.com/kjhealy/viz-organdata/master/organdonation.csv"

# Read the CSV from the URL
organs <- read_csv(file = url)
## Parsed with column specification:
## cols(
##   .default = col_double(),
##   country = col_character(),
##   world = col_character(),
##   opt = col_character(),
##   consent.law = col_character(),
##   consent.practice = col_character(),
##   consistent = col_character(),
##   ccode = col_character()
## )
## See spec(...) for full column specifications.
# Take a quick look at the data
glimpse(organs)
## Rows: 238
## Columns: 21
## $ country          <chr> "Australia", "Australia", "Australia", "Australia"...
## $ year             <dbl> NA, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998...
## $ donors           <dbl> NA, 12.09, 12.35, 12.51, 10.25, 10.18, 10.59, 10.2...
## $ pop              <dbl> 17065, 17284, 17495, 17667, 17855, 18072, 18311, 1...
## $ pop.dens         <dbl> 0.2204433, 0.2232723, 0.2259980, 0.2282198, 0.2306...
## $ gdp              <dbl> 16774, 17171, 17914, 18883, 19849, 21079, 21923, 2...
## $ gdp.lag          <dbl> 16591, 16774, 17171, 17914, 18883, 19849, 21079, 2...
## $ health           <dbl> 1300, 1379, 1455, 1540, 1626, 1737, 1846, 1948, 20...
## $ health.lag       <dbl> 1224, 1300, 1379, 1455, 1540, 1626, 1737, 1846, 19...
## $ pubhealth        <dbl> 4.8, 5.4, 5.4, 5.4, 5.4, 5.5, 5.6, 5.7, 5.9, 6.1, ...
## $ roads            <dbl> 136.59537, 122.25179, 112.83224, 110.54508, 107.98...
## $ cerebvas         <dbl> 682, 647, 630, 611, 631, 592, 576, 525, 516, 493, ...
## $ assault          <dbl> 21, 19, 17, 18, 17, 16, 17, 17, 16, 15, 16, 15, 14...
## $ external         <dbl> 444, 425, 406, 376, 387, 371, 395, 385, 410, 409, ...
## $ txp.pop          <dbl> 0.9375916, 0.9257116, 0.9145470, 0.9056433, 0.8961...
## $ world            <chr> "Liberal", "Liberal", "Liberal", "Liberal", "Liber...
## $ opt              <chr> "In", "In", "In", "In", "In", "In", "In", "In", "I...
## $ consent.law      <chr> "Informed", "Informed", "Informed", "Informed", "I...
## $ consent.practice <chr> "Informed", "Informed", "Informed", "Informed", "I...
## $ consistent       <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "...
## $ ccode            <chr> "Oz", "Oz", "Oz", "Oz", "Oz", "Oz", "Oz", "Oz", "O...
# View(organs) # Run in RStudio
# Another way to view data 
gapminder
## # A tibble: 1,704 x 6
##    country     continent  year lifeExp      pop gdpPercap
##    <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
##  1 Afghanistan Asia       1952    28.8  8425333      779.
##  2 Afghanistan Asia       1957    30.3  9240934      821.
##  3 Afghanistan Asia       1962    32.0 10267083      853.
##  4 Afghanistan Asia       1967    34.0 11537966      836.
##  5 Afghanistan Asia       1972    36.1 13079460      740.
##  6 Afghanistan Asia       1977    38.4 14880372      786.
##  7 Afghanistan Asia       1982    39.9 12881816      978.
##  8 Afghanistan Asia       1987    40.8 13867957      852.
##  9 Afghanistan Asia       1992    41.7 16317921      649.
## 10 Afghanistan Asia       1997    41.8 22227415      635.
## # ... with 1,694 more rows
# Make a plot object
p <- ggplot(data = gapminder,
            mapping = aes(x = gdpPercap, 
                          y = lifeExp))

# Create a scatterplot
p + geom_point()

Make a Plot

Show the Right Numbers

Graph Tables, Make Labels, Add Notes

Work with Models

Draw Maps

Refine your Plots

LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXphdGlvbiBOb3RlcyINCmF1dGhvcjogIkplbm4gU2NoaWxsaW5nIg0KZGF0ZTogIjIwMjEtMDEtMjciDQpvdXRwdXQ6DQogaHRtbF9kb2N1bWVudDoNCiAgdGhlbWU6ICJmbGF0bHkiICMgVGhlbWUgR2FsbGVyeTogaHR0cHM6Ly93d3cuZGF0YWRyZWFtaW5nLm9yZy9wb3N0L3ItbWFya2Rvd24tdGhlbWUtZ2FsbGVyeS8NCiAgdG9jOiBUUlVFDQogIHRvY19mbG9hdDogVFJVRQ0KICBjb2RlX2Rvd25sb2FkOiBUUlVFDQotLS0NCiAgIA0KIyMgRGF0YSBWaXN1YWxpemF0aW9uIE5vdGVzDQoNClRoZXNlIGFyZSBteSBub3RlcyBmb3IgKipHUkQgNjEwQTogRGF0YSBWaXN1YWxpemF0aW9uIElJKiogaW4gV2ludGVyIDIwMjEgYXQgdGhlIENvbGxlZ2UgZm9yIENyZWF0aXZlIFN0dWRpZXMuIFRoZXNlIG5vdGVzIGFyZSBmb3IgbXkgd29yayBpbiB0aGUgYm9vayAqRGF0YSBWaXN1YWxpemF0aW9uKiBieSBLaWVyYW4gSGVhbHkgKFByaW5jZXRvbiBVbml2ZXJzaXR5IFByZXNzLCAyMDE5KS4gDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KDQojIyBCeSBkZWZ1bHQsIHNob3cgY29kZSBmb3IgYWxsIGNodW5rcyBpbiB0aGUga25pdHRlZCBkb2N1bWVudCwNCiMjIGFzIHdlbGwgYXMgdGhlIG91dHB1dC4gVG8gb3ZlcnJpZGUgZm9yIGEgcGFydGljdWxhciBjaHVuaw0KIyMgdXNlIGVjaG8gPSBGQUxTRSBpbiBpdHMgb3B0aW9ucy4NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkgDQoNCiMjIFNldCB0aGUgZGVmYXVsdCBzaXplIG9mIGZpZ3VyZXMNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGg9OCwgZmlnLmhlaWdodD01KSAgDQoNCiMjIExvYWQgdGhlIGxpYnJhcmllcyB3ZSB3aWxsIGJlIHVzaW5nDQpsaWJyYXJ5KGdhcG1pbmRlcikNCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkoc29jdml6KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCiMjIExpYnJhcmllczogSW5zdGFsbCBvbmNlIHBlciBtYWNoaW5lLCBsb2FkIG9uY2UgcGVyIFIgc2Vzc2lvbg0KDQojIyBXZWVrIDIgLyBDaGFwdGVyIDIgTm90ZXMgcHVibGlzaGVkIGhlcmU6IGh0dHBzOi8vcnB1YnMuY29tL2plbm5zY2hpbGxpbmcvZGdyNjEwYV93MjFfdzJfbm90ZXMNCg0KYGBgDQoNCg0KIyMgR2V0IFN0YXJ0ZWQNCg0KIyMjIEV2ZXJ5dGhpbmcgaGFzIGEgbmFtZQ0KT2JqZWN0cyBpbiBSIGFyZSBjcmVhdGVkIGFuZCByZWZlcnJlZCB0byBieSB0aGVpciBuYW1lcy4gQ2VydGFpbiBuYW1lcyBhcmUgbm90IGFsbG93ZWQgYmVjYXVzZSB0aGV5IGFyZSByZXNlcnZlZCB3b3JkcyBzdWNoIGFzIGBgVFJVRWBgLCBgYGlmYGAsIGBgbWVhbigpYGAsIGFuZCBgYE5BYGAuIE5hbWVzIGFsc28gY2Fubm90IHN0YXJ0IHdpdGggYSBudW1iZXIgb3IgY29udGFpbiBzcGFjZXMuIFRoZXJlIGFyZSBkaWZmZXJlbnQgbmFtaW5nIGNvbnZlbnRpb25zLiAgDQoNCioqU25ha2UgQ2FzZSoqICANCmBgbXlfZGF0YWBgICANCmBgdGhpc19pc19zbmFrZV9jYXNlYGAgIA0KDQoqKkNhbWVsIENhc2UqKiAgDQpgYG15RGF0YWBgICANCmBgdGhpc0lzQ2FtZWxDYXNlYGANCg0KKipQYXNjYWwgQ2FzZSoqICANCmBgTXlEYXRhYGAgIA0KYGBUaGlzSXNQYXNjYWxDYXNlYGANCg0KUGljayBvbmUgbmFtaW5nIGNvbnZlbnRpb24gYW5kIHN0aWNrIHdpdGggaXQuIEJlIGNvbnNpc3RlbnQ7IGRvbid0IHN3aXRjaCBiZXR3ZWVuIGNvbnZlbnRpb25zLiBJIHJlY29tbWVuZCBzbmFrZSBjYXNlLg0KDQpgYGB7ciBuYW1pbmcsIGVycm9yPVRSVUV9DQoNCiMgVGhpcyBpcyBhIGNvbW1lbnQgKGl0IHN0YXJ0cyB3aXRoICMpDQoNCm15X2RhdGEgPC0gYygxLCAyLCAzLCA0KSAjIEFzc2lnbiB1c2luZyA8LSA7IHVzZSBBTFQgKyAtIG9yIE9QVElPTiArIC0NCg0KTXlfRGF0YSAgDQojIENhbm5vdCBiZSBmb3VuZCBiZWNhdXNlIHdlIGNhbGxlZCBpdCBteV9kYXRhIChsb3dlcmNhc2UpDQoNCiMgTm93IHdlIGNhbiBzZWUgaXQNCm15X2RhdGEgDQoNCmBgYA0KDQojIyMgRXZlcnl0aGluZyBpcyBhbiBvYmplY3Q7IHVzaW5nIGZ1bmN0aW9ucw0KVGhpbmsgb2YgZnVuY3Rpb25zIGxpa2UgYSByZWNpcGUuIFRoZSBhcmd1bWVudHMgb2YgdGhlIGZ1bmN0aW9uIGFyZSB0aGUgaW5ncmVkaWVudHMgYW5kIHdoYXQgaGFwcGVucyB3aXRoaW4gdGhlIGZ1bmN0aW9uIGlzIHRoZSBzZXF1ZW5jZSBvZiBjb29raW5nIHN0ZXBzLiANCmBgYHtyIG9iamVjdHMtZnVuY3Rpb25zfQ0KDQpjKDEsIDIsIDMsIDEsIDMsIDUsIDI1KSAjIGMoKSBpcyB0aGUgY29tYmluZSBmdW5jdGlvbiwgaXQgcHV0cyB0aGluZ3MgdG9nZXRoZXIgaW50byBhIHZlY3Rvci9saXN0DQoNCm15X251bWJlcnMgPC0gYygxLCAyLCAzLCAxLCAzLCA1LCAyNSkNCnlvdXJfbnVtYmVycyA8LSBjKDUsIDMxLCA3MSwgMSwgMywgMjEsIDYpDQoNCm15X251bWJlcnMNCg0KbWVhbih4ID0gbXlfbnVtYmVycykNCm1lYW4obXlfbnVtYmVycykgIyB5b3UgZG9uJ3QgaGF2ZSB0byBzcGVjaWZ5IHRoZSBhcmd1bWVudCBuYW1lcywgYnV0IG9yZGVyIG1hdHRlcnMgaWYgeW91IGRvIG5vdCBzcGVjaWZ5DQoNCm1lYW4oeCA9IHlvdXJfbnVtYmVycykNCg0KbXlfc3VtbWFyeSA8LSBzdW1tYXJ5KG15X251bWJlcnMpDQoNCm15X3N1bW1hcnkNCg0KdGFibGUobXlfbnVtYmVycykNCg0Kc2QobXlfbnVtYmVycykNCg0KbXlfbnVtYmVycyAqIDUNCg0KbXlfbnVtYmVycyArIDENCg0KbXlfbnVtYmVycyArIG15X251bWJlcnMgIyBIb3cgaXMgdGhpcyBkaWZmZXJlbnQgdGhhbiB0aGUgbGFzdCBsaW5lPw0KDQojIElmIHlvdSdyZSBub3Qgc3VyZSB3aGF0IGFuIG9iamVjdCBpcywgYXNrIGZvciBpdHMgY2xhc3Mgb3IgdHlwZQ0KDQpjbGFzcyhteV9udW1iZXJzKQ0KDQpjbGFzcyhteV9zdW1tYXJ5KQ0KDQpjbGFzcyhzdW1tYXJ5KQ0KDQpteV9uZXdfdmVjdG9yIDwtIGMobXlfbnVtYmVycywgIkFwcGxlIikgIyBXaGF0IGhhcHBlbnMgaWYgd2UgY29tYmluZSBhIHdvcmQgd2l0aCBudW1iZXJzPw0KDQpteV9uZXdfdmVjdG9yDQoNCmNsYXNzKG15X25ld192ZWN0b3IpDQoNCiMgTGV0J3MgbG9vayBhdCBhIG5ldyBkYXRhc2V0DQoNCnRpdGFuaWMNCg0KY2xhc3ModGl0YW5pYykgDQoNCiMgVGl0YW5pYyBpcyBhIGRhdGEgZnJhbWUsIHdoaWNoIGlzIGxpa2UgYSB0YWJsZQ0KIyBUaGUgJCBvcGVyYXRvciBjYW4gYmUgdXNlZCB0byBhY2Nlc3MgYSBjb2x1bW4gb2YgYSBkYXRhIGZyYW1lIGJ5IG5hbWUNCg0KdGl0YW5pYyRwZXJjZW50DQoNCiMgVGliYmxlcyBhcmUgc2xpZ2h0bHkgZGlmZmVyZW50IHRoYW4gZGF0YSBmcmFtZXMuIFRoZXkgYXJlIGFsc28gZGF0YSB0YWJsZXMsIGJ1dCB0aGV5IHByb3ZpZGUgbW9yZSBpbmZvcm1hdGlvbi4NCg0KdGl0YW5pY190YiA8LSBhc190aWJibGUodGl0YW5pYykNCg0KdGl0YW5pY190YiAjIEhvdyBpcyBkb2VzIHRoaXMgY29tcGFyZSB0byB0aXRhbmljIGFib3ZlPw0KDQojIFRvIHNlZSBpbnNpZGUgYW4gb2JqZWN0LCBhc2sgZm9yIGl0cyBzdHJ1Y3R1cmUNCg0Kc3RyKG15X251bWJlcnMpDQoNCnN0cihteV9zdW1tYXJ5KQ0KDQpgYGANClByb2dyYW1taW5nIGluIFIgY2FuIGJlIGNoYWxsZW5naW5nIGFuZCBpdCB0YWtlcyB0aW1lIHRvIGdldCB1c2VkIHRvLiBCZSBwYXRpZW50IGFuZCB0YWtlIGEgYnJlYWsgaWYgeW91IGdldCBzdHVjay4gTWFrZSBzdXJlIHBhcmVudGhlc2VzIGFyZSBvcGVuZWQgYW5kIGNsb3NlZC4gQ29tcGxldGUgeW91ciBjb21tYW5kcyAobG9vayBvdXQgZm9yIHRoZSArIGluIHRoZSBjb25zb2xlKS4gVGFrZSB5b3VyIHRpbWUgYW5kIGxvb2tvdXQgZm9yIHR5cG9zLiANCg0KIyMjIEdldCBEYXRhIGludG8gUg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIGdldCBkYXRhIGZyb20gYSBVUkwgYW5kIG1ha2UgYSBxdWljayBmaWd1cmUuDQoNCmBgYHtyIGdldC1kYXRhfQ0KDQojIERhdGEgc291cmNlDQp1cmwgPC0gImh0dHBzOi8vY2RuLnJhd2dpdC5jb20va2poZWFseS92aXotb3JnYW5kYXRhL21hc3Rlci9vcmdhbmRvbmF0aW9uLmNzdiINCg0KIyBSZWFkIHRoZSBDU1YgZnJvbSB0aGUgVVJMDQpvcmdhbnMgPC0gcmVhZF9jc3YoZmlsZSA9IHVybCkNCg0KIyBUYWtlIGEgcXVpY2sgbG9vayBhdCB0aGUgZGF0YQ0KZ2xpbXBzZShvcmdhbnMpDQoNCiMgVmlldyhvcmdhbnMpICMgUnVuIGluIFJTdHVkaW8NCg0KYGBgDQoNCmBgYHtyIG1ha2UtZmlndXJlfQ0KDQojIEFub3RoZXIgd2F5IHRvIHZpZXcgZGF0YSANCmdhcG1pbmRlcg0KDQojIE1ha2UgYSBwbG90IG9iamVjdA0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlciwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGdkcFBlcmNhcCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsaWZlRXhwKSkNCg0KIyBDcmVhdGUgYSBzY2F0dGVycGxvdA0KcCArIGdlb21fcG9pbnQoKQ0KDQpgYGANCg0KDQojIyBNYWtlIGEgUGxvdA0KDQpgYGB7cn0NCg0KYGBgDQoNCg0KIyMgU2hvdyB0aGUgUmlnaHQgTnVtYmVycw0KDQpgYGB7cn0NCg0KYGBgDQoNCg0KIyMgR3JhcGggVGFibGVzLCBNYWtlIExhYmVscywgQWRkIE5vdGVzDQoNCmBgYHtyfQ0KDQpgYGANCg0KIyMgV29yayB3aXRoIE1vZGVscw0KDQpgYGB7cn0NCg0KYGBgDQoNCiMjIERyYXcgTWFwcw0KDQpgYGB7cn0NCg0KYGBgDQoNCg0KIyMgUmVmaW5lIHlvdXIgUGxvdHMNCg0KYGBge3J9DQoNCmBgYA0KDQoNCg==