Nothing
# Demo for presentation at the 16th Berlin R meetup
# {dm} facilitates working with multiple tables
options(tibble.print_min = 6)
options(tibble.print_max = 6)
##
##
##
## Why?
## --------------------------------------------------------------------
##
##
##
library(nycflights13)
# Example dataset: tables linked with each other
?flights
?airports
?airlines
?planes
?weather
library(tidyverse)
flights_base <-
flights %>%
select(year, month, day, carrier, tailnum, origin, dest)
flights_base
# carrier column also present in `airlines`, this table contains
# additional information
airlines
flights_base %>%
left_join(airlines)
# single source of truth: updating in one single location
airlines[airlines$carrier == "UA", "name"] <- "United broke my guitar"
# ...propagates to related records
flights_base %>%
left_join(airlines)
# Same for airplanes?
planes
flights_base %>%
left_join(planes)
flights_base %>%
left_join(planes) %>%
count(is.na(type))
# Take a closer look at the join
flights_base %>%
left_join(planes, by = "tailnum")
# Same for airports?
airports
try(
flights_base %>%
left_join(airports)
)
# Need to specify join variables!
flights_base %>%
left_join(airports, by = c("origin" = "faa"))
# cleanup
rm(airlines)
##
##
##
## Keys
## --------------------------------------------------------------------
##
##
##
# Row identifiers
t1 <- tibble(a = 1, b = letters[1:3])
t1
t2 <- tibble(a = 1, c = 1:2)
t2
# What happens here?
left_join(t1, t2)
# When joining, the column(s) must be unique in at least one
# participating table!
# Ensure uniqueness:
airlines %>%
count(carrier)
airlines %>%
count(carrier) %>%
count(n)
planes %>%
count(tailnum) %>%
count(n)
# dm shortcut:
planes %>%
dm::check_key(tailnum)
try(
planes %>%
dm::check_key(engines)
)
airports %>%
dm::check_key(faa)
# FIXME: add dm function that explains why not key candidate
# Why is name not a key candidate for airports?
try(
airports %>%
dm::check_key(name)
)
airports %>%
add_count(name) %>%
filter(n > 1) %>%
arrange(name)
# Cleanup
rm(t1, t2)
##
##
##
## Data model
## --------------------------------------------------------------------
##
##
##
library(dm)
# Compound object: tables, relationships, data
dm_nycflights13(cycle = TRUE)
dm_nycflights13(cycle = TRUE) %>%
dm_draw()
# Selection of tables
dm_nycflights13(cycle = TRUE) %>%
dm_select_tbl(flights, airlines) %>%
dm_draw()
dm_nycflights13(cycle = TRUE) %>%
dm_select_tbl(airports, airlines) %>%
dm_draw()
try(
dm_nycflights13() %>%
dm_select_tbl(bogus)
)
# Accessing tables
dm_nycflights13() %>%
tbl("airlines")
# Table names
dm_nycflights13() %>%
src_tbls()
# NB: [, $, [[ and names() also work
# Analogy: parallel vectors in the global environment
x <- 1:5
y <- x + 1
z <- x * y
w <- map(z, ~ runif(.))
tibble(x, y, z, w)
tibble(x = 1:5) %>%
mutate(y = x + 1) %>%
mutate(z = x * y) %>%
mutate(w = map(z, ~ runif(.)))
##
##
##
## Filtering for data models
## --------------------------------------------------------------------
##
##
##
dm_nycflights13()
# Filtering on a table returns a dm object with the filter condition(s) stored
(dm_nyc_filtered <-
dm_nycflights13() %>%
dm_filter(airlines, carrier == "AA"))
# Apply all filters and retrieve an "updated" `dm`
dm_nyc_filtered %>%
dm_apply_filters()
# If a filter condition is phrased wrongly it will only fail, once the filter is being applied
(dm_nyc_fail <-
dm_nycflights13() %>%
dm_filter(airports, origin == "EWR"))
try(
tbl(dm_nyc_fail, "flights")
)
# Mind: when accessing table from a `dm` (using one of: `tbl()`, `[[.dm()`, `$.dm()`),
# only the necessary filter conditions are applied:
tbl(dm_nyc_fail, "weather")
dm_nycflights13() %>%
dm_filter(flights, origin == "EWR") %>%
dm_apply_filters()
# ... which then can be filtered on another table
dm_nycflights13() %>%
dm_filter(airlines, name == "American Airlines Inc.") %>%
dm_filter(airports, name != "John F Kennedy Intl") %>%
dm_apply_filters()
aa_non_jfk_january <-
dm_nycflights13() %>%
dm_filter(airlines, name == "American Airlines Inc.") %>%
dm_filter(airports, name != "John F Kennedy Intl") %>%
dm_filter(flights, month == 1) %>%
dm_apply_filters()
aa_non_jfk_january
# ... and processed further
aa_non_jfk_january %>%
tbl("planes")
##
##
##
## Copy to database
## --------------------------------------------------------------------
##
##
##
# FIXME: SQLite with many rows
# FIXME: Make work with planes table
# FIXME: remove all_connected = TRUE for now, redo example above
# All operations are designed to work locally and on the database
nycflights13_sqlite <-
dm_nycflights13() %>%
dm_select_tbl(-planes) %>%
dm_filter(flights, month == 1) %>%
dm_apply_filters() %>%
copy_dm_to(dbplyr::src_memdb(), ., unique_table_names = TRUE)
nycflights13_sqlite
nycflights13_sqlite %>%
dm_draw()
nycflights13_sqlite %>%
dm_get_tables() %>%
map(dbplyr::sql_render)
# Filtering on the database
nycflights13_sqlite %>%
dm_filter(airlines, name == "American Airlines Inc.") %>%
dm_filter(airports, name != "John F Kennedy Intl") %>%
dm_filter(flights, day == 1) %>%
tbl("flights")
# ... and the corresponding SQL statement
nycflights13_sqlite %>%
dm_filter(airlines, name == "American Airlines Inc.") %>%
dm_filter(airports, name != "John F Kennedy Intl") %>%
dm_filter(flights, day == 1) %>%
tbl("flights") %>%
dbplyr::sql_render()
##
##
##
## Joining tables
## --------------------------------------------------------------------
##
##
##
dm_nycflights13() %>%
dm_join_to_tbl(airlines, flights, join = left_join)
dm_nycflights13() %>%
dm_join_to_tbl(flights, airlines, join = left_join)
nycflights13_sqlite %>%
dm_join_to_tbl(airlines, flights, join = left_join)
aa_non_jfk_january %>%
dm_join_to_tbl(flights, airlines, join = left_join)
# FIXME: Multi-joins
try(
dm_nycflights13() %>%
dm_join_to_tbl(airports, airlines, join = left_join)
)
try(
dm_nycflights13() %>%
dm_join_to_tbl(flights, airports, airlines, join = left_join)
)
##
##
##
## Build up data model from scratch
## --------------------------------------------------------------------
##
##
##
# Linking the weather table
# Determine key candidates
weather %>%
enum_pk_candidates()
weather %>%
enum_pk_candidates() %>%
count(candidate)
# It's tricky:
weather %>%
unite("slot_id", origin, year, month, day, hour, remove = FALSE) %>%
count(slot_id) %>%
filter(n > 1)
weather %>%
count(origin, time_hour) %>%
filter(n > 1)
weather %>%
count(origin, format(time_hour)) %>%
filter(n > 1)
# This looks like a good candidate:
weather %>%
count(origin, format(time_hour, tz = "UTC")) %>%
filter(n > 1)
# FIXME: Support compound keys (#3)
# Currently, we need to create surrogate keys:
weather_link <-
weather %>%
mutate(time_hour_fmt = format(time_hour, tz = "UTC")) %>%
unite("origin_slot_id", origin, time_hour_fmt, remove = FALSE)
flights_link <-
flights %>%
mutate(time_hour_fmt = format(time_hour, tz = "UTC")) %>%
unite("origin_slot_id", origin, time_hour_fmt, remove = FALSE)
# one option to create a `dm` is to use `as_dm()`:
nycflights13_dm <- as_dm(list(airlines = airlines, airports = airports, flights = flights_link, planes = planes, weather = weather_link))
# Copy to this environment
airlines_global <- airlines
airports_global <- airports
planes_global <- planes
global <-
dm_from_src(src_df(env = .GlobalEnv))
global
global %>%
dm_rename_tbl(
airlines = airlines_global,
airports = airports_global,
planes = planes_global,
flights = flights_link,
weather = weather_link
) %>%
dm_select_tbl(airlines, airports, planes, flights, weather)
# or better:
nycflights13_tbl <-
global %>%
dm_select_tbl(
airlines = airlines_global,
airports = airports_global,
planes = planes_global,
flights = flights_link,
weather = weather_link
)
nycflights13_tbl
nycflights13_tbl %>%
dm_draw()
# Adding primary keys
nycflights13_pk <-
nycflights13_tbl %>%
dm_add_pk(weather, origin_slot_id) %>%
dm_add_pk(planes, tailnum) %>%
dm_add_pk(airports, faa) %>%
dm_add_pk(airlines, carrier)
nycflights13_pk %>%
dm_draw()
# FIXME: Model weak constraints, show differently in diagram (#4)
# Adding foreign keys
nycflights13_fk <-
nycflights13_pk %>%
dm_add_fk(flights, origin_slot_id, weather, check = FALSE) %>%
dm_add_fk(flights, tailnum, planes, check = FALSE) %>%
dm_add_fk(flights, origin, airports) %>%
dm_add_fk(flights, dest, airports, check = FALSE) %>%
dm_add_fk(flights, carrier, airlines)
nycflights13_fk %>%
dm_draw()
# Color it!
dm_get_available_colors()
nycflights13_fk %>%
dm_set_colors(airlines = , planes = , weather = , airports = "blue") %>%
dm_draw()
##
##
##
## Import a dm from a database, including key constraints
## --------------------------------------------------------------------
##
##
##
try({
# Import
dm_pq <-
dm_nycflights13() %>%
dm_select_tbl(-planes) %>%
dm_filter(flights, month == 1) %>%
copy_dm_to(src_postgres(), ., temporary = FALSE)
dm_from_pq <-
dm_learn_from_db(src_postgres())
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.