class: center, middle, inverse, title-slide .title[ # Maps & Nets ] .author[ ### Visualizing and communicating data with R
The R Bootcamp @ Unibas
] .date[ ### November 2023 ] --- layout: true <div class="my-footer"> <span style="text-align:center"> <span> <img src="https://raw.githubusercontent.com/therbootcamp/therbootcamp.github.io/master/_sessions/_image/by-sa.png" height=14 style="vertical-align: middle"/> </span> <a href="https://therbootcamp.github.io/"> <span style="padding-left:82px"> <font color="#7E7E7E"> https://therbootcamp.github.io </font> </span> </a> <a href="https://therbootcamp.github.io/"> <font color="#7E7E7E"> The R Bootcamp | November 2023 </font> </a> </span> </div> --- .pull-left3[ # Maps with <mono>ggplot</mono>/<mono>sf</mono> <ul> <li class="m1"><span>Maps require geometric shapes stored in <high>shapefiles</high>.</span></li><br> <li class="m2"><span>The <high>simple features</high> (<mono>sf</mono>) framework makes processing and visualizing maps with the <mono>tidyverse<mono> easy.</span></li> </ul> ] .pull-right6[ <br><br> <img src="maps-nets_files/figure-html/unnamed-chunk-2-1.png" style="display: block; margin: auto;" /> ] --- # Shapefiles .pull-left45[ <ul> <li class="m1"><span>Geospatial vector data format for <high>geographic information system</high> (GIS) software.</span></li><br> <li class="m2"><span>Necessary files:</span></li> <ul class="level"> <li><span><mono>.shp</mono> | actual shapefile</span></li> <li><span><mono>.shx</mono> | shape index format</span></li> <li><span><mono>.dbf</mono> | attribute format</span></li> </ul><br> <li class="m3"><span>Optional files:</span></li> <ul class="level"> <li><span><mono>.prj</mono> | projection description</span></li> <li><span><mono>.cpg</mono> | code page specification</span></li> </ul> </ul> ] .pull-right45[ <p align="center"> <img src="image/files.png" height="420px"> </p> ] --- # <mono>sf</mono> ```r read_sf('1_Data/quarters') ``` ``` Simple feature collection with 21 features and 5 fields Geometry type: POLYGON Dimension: XY Bounding box: xmin: 2609000 ymin: 1263000 xmax: 2619000 ymax: 1272000 Projected CRS: CH1903+ / LV95 # A tibble: 21 × 6 OBJID OBJECTID TXT ZTXT TYPE geometry <chr> <dbl> <chr> <chr> <chr> <POLYGON [m]> 1 17136 1 7 07 Bruderholz ((2612556 1264548, 2612561 1264514, 2612565 12644… 2 17139 2 6 06 Gundeldingen ((2610887 1266551, 2610896 1266546, 2610918 12665… 3 17142 3 5 05 St. Alban ((2612942 1267023, 2613000 1267019, 2613027 12670… 4 17145 4 4 04 Breite ((2613684 1266891, 2613686 1266889, 2613689 12668… 5 17148 5 8 08 Bachletten ((2610561 1266791, 2610571 1266781, 2610595 12667… 6 17151 6 2 02 Vorstädte ((2610928 1268323, 2610934 1268293, 2610947 12682… 7 17154 7 1 01 Altstadt Grossbasel ((2611366 1267578, 2611375 1267568, 2611376 12675… 8 17157 8 3 03 Am Ring ((2610705 1267923, 2610680 1267844, 2610647 12677… 9 17160 9 9 09 Gotthelf ((2609245 1266802, 2609236 1266801, 2609366 12671… 10 17163 10 10 10 Iselin ((2610234 1267656, 2610284 1267389, 2610234 12674… # ℹ 11 more rows ``` --- # <mono>geom_sf</mono> .pull-left45[ <ul> <li class="m1"><span>Since <mono>read_sf</mono> creates a <mono>tibble</tibble>, it can be plugged <high>straight into <mono>ggplot</mono></high>.</span></li><br> <li class="m2"><span>The dedicated geom <mono>geom_sf</mono> plots the geometric polygons.</span></li> </ul> <br> ```r # read shapefiles quarters_map <- read_sf('1_Data/quarters') # plot quarters quarters_map %>% ggplot() + geom_sf() ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-6-1.png" style="display: block; margin: auto;" /> ] --- # <mono>geom_sf</mono> .pull-left45[ <ul> <li class="m1"><span>Since <mono>read_sf</mono> creates a <mono>tibble</tibble>, it can be plugged <high>straight into <mono>ggplot</mono></high>.</span></li><br> <li class="m2"><span>The dedicated geom <mono>geom_sf</mono> plots the geometric polygons.</span></li> </ul> <br> ```r # read shapefiles quarters_map <- read_sf('1_Data/quarters') # plot quarters quarters_map %>% ggplot() + geom_sf() + # remove background theme_void() ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-8-1.png" style="display: block; margin: auto;" /> ] --- # Wrangling .pull-left45[ <ul> <li class="m1"><span>Since <mono>read_sf</mono> creates a <mono>tibble</tibble>, one can easily <high>join additional data</high>.</span></li> </ul> <br> ```r # join basel tax data quarters_map <- quarters_map %>% left_join(filter(basel, year == 2017), by = c("TYPE" = "quarter")) # plot quarters quarters_map %>% ggplot() + geom_sf() + theme_void() ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-10-1.png" style="display: block; margin: auto;" /> ] --- # Add color .pull-left45[ <ul> <li class="m1"><span>Colors are set using <mono>aes()</mono> just like in a regular <mono>ggplot</mono>.</span></li> </ul> ```r # join basel tax data quarters_map <- quarters_map %>% left_join(filter(basel, year == 2017), by = c("TYPE" = "quarter")) # plot quarters quarters_map %>% ggplot() + # fill color by income geom_sf(aes(fill = income_mean)) + theme_void() ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-12-1.png" style="display: block; margin: auto;" /> ] --- # Styling .pull-left45[ <ul> <li class="m1"><span>Styling can be adjusted just like in any other <mono>ggplot<mono>.</span></li> </ul> ```r # join basel tax data quarters_map <- quarters_map %>% left_join(filter(basel, year == 2017), by = c("TYPE" = "quarter")) # plot quarters quarters_map %>% ggplot() + # add white outlines geom_sf(aes(fill = income_mean), col = "white") + theme_void() ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-14-1.png" style="display: block; margin: auto;" /> ] --- # Styling .pull-left45[ <ul> <li class="m1"><span>Styling can be adjusted just like in any other <mono>ggplot<mono>.</span></li> </ul> ```r # join basel tax data quarters_map <- quarters_map %>% left_join(filter(basel, year == 2017), by = c("TYPE" = "quarter")) # plot quarters quarters_map %>% ggplot() + geom_sf(aes(fill = income_mean), col = "white") + theme_void() + # change legend title scale_fill_continuous(name = 'Income') ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-16-1.png" style="display: block; margin: auto;" /> ] --- # Styling .pull-left45[ <ul> <li class="m1"><span>Styling can be adjusted just like in any other <mono>ggplot<mono>.</span></li> </ul> ```r # join basel tax data quarters_map <- quarters_map %>% left_join(filter(basel, year == 2017), by = c("TYPE" = "quarter")) # plot quarters quarters_map %>% ggplot() + geom_sf(aes(fill = income_mean), col = "white") + theme_void() + scale_fill_continuous(name = 'Income') + # add annotion labs(title = "Inequality in Basel", subtitle = "Average income in Basel...", caption = "ource: Open Data Basel...") ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-18-1.png" style="display: block; margin: auto;" /> ] --- # <mono>geom_label()</mono> .pull-left45[ <ul> <li class="m1"><span>Annotation with labels or images requires specification of a location such as the <high>polygon's centroid</high>.</span></li> </ul> ```r # start with joint object quarters_map %>% # determine centroids mutate(ctr = st_centroid(geometry), lon = sapply(ctr, function(x) x[1]), lat = sapply(ctr, function(x) x[2])) %>% # plot quarters ggplot(...) + # add labels geom_label_repel(aes(x = lon, y = lat, label = TYPE), size = 2.5) ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-20-1.png" style="display: block; margin: auto;" /> ] --- # <mono>geom_image()</mono> .pull-left45[ <ul> <li class="m1"><span>Styling can be adjusted just like in any other <mono>ggplot<mono>.</span></li> </ul> ```r # join basel tax data quarters_map %>% # determine centroids mutate(ctr = st_centroid(geometry), lon = sapply(ctr, function(x) x[1]), lat = sapply(ctr, function(x) x[2])) %>% # plot quarters ggplot(...) + # add images geom_image(aes(x = lon, y = lat, image = image), size=.05) ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-22-1.png" style="display: block; margin: auto;" /> ] --- .pull-left3[ # Networks with <mono>tidygraph</mono> & <mono>ggraph</mono> <ul> <li class="m1"><span>Networks (graphs) show relationships (edges) between units (nodes).</span></li><br> <li class="m2"><span><highm>tidygraph</highm> helps with creating tidy network objects, <highm>ggraph</highm> helps with visualizing networks within the <mono>ggplot</mono> framework.</span></li> </ul> ] .pull-right6[ <img src="maps-nets_files/figure-html/unnamed-chunk-23-1.png" style="display: block; margin: auto;" /> ] --- # Edge list .pull-left45[ <ul> <li class="m1"><span>A network is typically created from either an <high>edge list</high> or an adjacency matrix.</span></li><br> </ul> ```r # determine correlations basel %>% select(year, quarter, income_median) %>% pivot_wider(names_from = quarter, values_from = income_median) %>% select(-1) %>% cor() %>% as.table() %>% as_tibble(.name_repair = "unique") %>% rename(from = `...1`, to = `...2`, weight = n) %>% # limit to strong edges filter(weight < 1, weight > .5) ``` ] .pull-right45[ ``` # A tibble: 184 × 3 from to weight <chr> <chr> <dbl> 1 Am Ring Altstadt Grossbasel 0.523 2 Bachletten Altstadt Grossbasel 0.558 3 Am Ring Vorstädte 0.881 4 St. Alban Vorstädte 0.806 5 Gundeldingen Vorstädte 0.721 6 Bruderholz Vorstädte 0.605 7 Bachletten Vorstädte 0.770 8 Gotthelf Vorstädte 0.872 9 Altstadt Kleinbasel Vorstädte 0.560 10 Wettstein Vorstädte 0.917 # ℹ 174 more rows ``` ] --- # <mono>tidygraph</mono> .pull-left45[ <ul> <li class="m1"><span><mono>tidygraph</mono> generates a <high>network object</high> the edge list.</span></li><br> </ul> ```r # determine correlations network <- basel %>% select(year, quarter, income_median) %>% pivot_wider(names_from = quarter, values_from = income_median) %>% select(-1) %>% cor() %>% as.table() %>% as_tibble(.name_repair = "unique") %>% rename(from = `...1`, to = `...2`, weight = n) %>% filter(weight < 1, weight > .5) %>% # format to graph as_tbl_graph(edges, directed = FALSE) ``` ] .pull-right45[ ``` # A tbl_graph: 21 nodes and 184 edges # # An undirected multigraph with 1 component # # A tibble: 21 × 1 name <chr> 1 Am Ring 2 Bachletten 3 St. Alban 4 Gundeldingen 5 Bruderholz 6 Gotthelf # ℹ 15 more rows # # A tibble: 184 × 3 from to weight <int> <int> <dbl> 1 1 13 0.523 2 2 13 0.558 3 1 14 0.881 # ℹ 181 more rows ``` ] --- # <mono>ggraph</mono> .pull-left45[ <ul> <li class="m1"><span><mono>ggraph</mono> provides syntax to plot networks within the <highm>ggplot framwork</high>.</span></li><br> </ul> ```r # start network plot network %>% ggraph() ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-29-1.png" style="display: block; margin: auto;" /> ] --- # Adding nodes & edges .pull-left45[ <ul> <li class="m1"><span><mono>geom_node_point()</mono> adds points to represent the nodes of the network.</span></li><br> <li class="m2"><mono>geom_edge_link()</mono> adds (straight) lines to represent the edges of the network.</span></li><br> </ul> ```r # start network plot network %>% ggraph() + # add nodes and edges geom_edge_link() + geom_node_point() ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-31-1.png" style="display: block; margin: auto;" /> ] --- # Adding labels .pull-left45[ <ul> <li class="m1"><span><mono>geom_node_label()</mono> adds node labels.</span></li><br> </ul> ```r # start network plot network %>% ggraph() + geom_edge_link() + geom_node_point() + # add labels geom_node_label(aes(label = name), show.legend = FALSE) ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-33-1.png" style="display: block; margin: auto;" /> ] --- # Styling networks .pull-left5[ <ul> <li class="m1"><span><mono>theme_graph()</mono> removes unnecessary background details.</span></li><br> <li class="m2"><span><mono>scale_edge_width()</mono> and similar functions can be used to adapt edges or nodes.</span></li> </ul> ```r # start network plot network %>% ggraph() + geom_edge_hive() + # style change geom_node_point() + geom_node_label(aes(label = name), show.legend = FALSE) + # add styling theme_graph() + scale_edge_width(range=c(.2, 1)) + coord_cartesian(clip = "off") ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-35-1.png" style="display: block; margin: auto;" /> ] --- # Adding clusters .pull-left5[ <ul> <li class="m1"><span><mono>group_louvain()</mono> and similar functions can be used to determine node groups (clusters).</span></li> </ul> ```r # add clusters network %>% mutate(community = group_louvain(weight) %>% as.factor()) %>% # network plot ggraph() + geom_edge_hive() + geom_node_point() + geom_node_label(aes(label = name, col = community), show.legend = FALSE) + theme_graph() + scale_edge_width(range=c(.2, 1)) + coord_cartesian(clip = "off") scale_color_manual(values=viridis(3)[1:2]) ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-37-1.png" style="display: block; margin: auto;" /> ] --- # Adding ellipses .pull-left5[ <ul> <li class="m1"><span><mono>stat_ellipse()</mono> can be used to add ellipses covering points in the data (similar <mono>stat_*</mono> functions exist for other visual elements.</span></li> </ul> ```r # add clusters network %>% ... # network plot ggraph() + # ellipses stat_ellipse(aes(x = x , y = y, group = community, fill = community), level = .8, geom = "polygon", alpha = .1, show.legend = FALSE) + # rest of plot scale_fill_manual(values=viridis(3)[1:2]) + ... ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-39-1.png" style="display: block; margin: auto;" /> ] --- # Adding annotation .pull-left5[ <ul> <li class="m1"><span><mono>annotate()</mono> can be used to <high>add any type of plot elements</high> (layers) using vector specification.</span></li><br> </ul> ```r # add clusters network %>% ... # network plot ggraph() + ... # annotation annotate("text", x = c(-2.5, 2), y = c(.8,1), label = c("Group 1","Group 2"), size=7) ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-41-1.png" style="display: block; margin: auto;" /> ] --- # Adding labels .pull-left5[ <ul> <li class="m1"><span>Labels can be added as usual using <mono>labs()</mono>.</span></li><br> </ul> ```r # add clusters network %>% ... # network plot ggraph() + ... # labels labs(title = "Network...", subtitle = "reveals...") ``` ] .pull-right45[ <img src="maps-nets_files/figure-html/unnamed-chunk-43-1.png" style="display: block; margin: auto;" /> ] --- class: middle, center <h1><font style="font-size:12px">click to download</font><br><a href="https://www.dropbox.com/s/mx2x5bak5gie7sb/Maps%26Nets.Rmd?dl=1">Practical</a></h1>