class: center, middle, inverse, title-slide .title[ # Introduction to Geospatial Techniques for Social Scientists in R ] .subtitle[ ## Investigating Spatial Autocorrelation ] .author[ ### Stefan Jünger & Anne-Kathrin Stroppe ] .institute[ ### GESIS Workshop ] .date[ ### April 24, 2024 ] --- layout: true --- ## Now <table class="table" style="color: black; margin-left: auto; margin-right: auto;"> <thead> <tr> <th style="text-align:left;"> Day </th> <th style="text-align:left;"> Time </th> <th style="text-align:left;"> Title </th> </tr> </thead> <tbody> <tr> <td style="text-align:left;color: gray !important;"> April 23 </td> <td style="text-align:left;color: gray !important;"> 10:00-11:30 </td> <td style="text-align:left;font-weight: bold;"> Introduction to GIS </td> </tr> <tr> <td style="text-align:left;color: gray !important;"> April 23 </td> <td style="text-align:left;color: gray !important;"> 11:45-13:00 </td> <td style="text-align:left;font-weight: bold;"> Vector Data </td> </tr> <tr> <td style="text-align:left;color: gray !important;color: gray !important;"> April 23 </td> <td style="text-align:left;color: gray !important;color: gray !important;"> 13:00-14:00 </td> <td style="text-align:left;font-weight: bold;color: gray !important;"> Lunch Break </td> </tr> <tr> <td style="text-align:left;color: gray !important;"> April 23 </td> <td style="text-align:left;color: gray !important;"> 14:00-15:30 </td> <td style="text-align:left;font-weight: bold;"> Mapping </td> </tr> <tr> <td style="text-align:left;color: gray !important;border-bottom: 1px solid"> April 23 </td> <td style="text-align:left;color: gray !important;border-bottom: 1px solid"> 15:45-17:00 </td> <td style="text-align:left;font-weight: bold;border-bottom: 1px solid"> Raster Data </td> </tr> <tr> <td style="text-align:left;color: gray !important;"> April 24 </td> <td style="text-align:left;color: gray !important;"> 09:00-10:30 </td> <td style="text-align:left;font-weight: bold;"> Advanced Data Import & Processing </td> </tr> <tr> <td style="text-align:left;color: gray !important;"> April 24 </td> <td style="text-align:left;color: gray !important;"> 10:45-12:00 </td> <td style="text-align:left;font-weight: bold;"> Applied Data Wrangling & Linking </td> </tr> <tr> <td style="text-align:left;color: gray !important;color: gray !important;"> April 24 </td> <td style="text-align:left;color: gray !important;color: gray !important;"> 12:00-13:00 </td> <td style="text-align:left;font-weight: bold;color: gray !important;"> Lunch Break </td> </tr> <tr> <td style="text-align:left;color: gray !important;background-color: yellow !important;"> April 24 </td> <td style="text-align:left;color: gray !important;background-color: yellow !important;"> 13:00-14:30 </td> <td style="text-align:left;font-weight: bold;background-color: yellow !important;"> Investigating Spatial Autocorrelation </td> </tr> <tr> <td style="text-align:left;color: gray !important;"> April 24 </td> <td style="text-align:left;color: gray !important;"> 14:45-16:00 </td> <td style="text-align:left;font-weight: bold;"> Spatial Econometrics & Outlook </td> </tr> </tbody> </table> --- ## Thus far We've done some - wrangling, - mapping, - and linking of geospatial data (with georeferenced survey data) We've seen that geospatial data are - relevant to provide context - as social scientists, we know that space is important! - nice to look at - we can tell a story **However, geospatial data can be interesting on their own for social science studies!** --- ## Tobler's first law of geography > [E]verything is related to everything else, but near things are more related than distant things (Tobler 1970, p. 236) This means nearby geographical regions, institutions, or people are more similar to each other or do have a stronger influence on each other. **What we get is an interdependent system.** .tinyisher[Tobler, W. R. (1970). A Computer Movie Simulating Urban Growth in the Detroit Region. Economic Geography, 46, 234–240. https://doi.org/10.2307/143141] --- ## Spatial Interdependence or Autocorrelation Tobler's law is the fundamental principle of doing spatial analysis. We want to know 1. if observations in our data are spatially interdependent 2. and how this interdependence can be explained (= data generation process) --- ## Developing a model of connectiveness: the chess board .pull-left[ .center[ <img src="data:image/png;base64,#../img/random.png" width="3464" style="display: block; margin: auto;" /> ] ] -- .pull-right[ .center[ <img src="data:image/png;base64,#../img/non_random.png" width="3464" style="display: block; margin: auto;" /> ] ] --- ## Rook and queen neighborhoods .pull-left[ .center[ <img src="data:image/png;base64,#../img/rook.png" width="3464" style="display: block; margin: auto;" /> ] ] -- .pull-right[ .center[ <img src="data:image/png;base64,#../img/queen.png" width="3464" style="display: block; margin: auto;" /> ] ] --- ## It's an interdependent system .pull-left[ .center[ <img src="data:image/png;base64,#../img/rook_interdependent.png" width="3464" style="display: block; margin: auto;" /> ] ] .pull-right[ .center[ <img src="data:image/png;base64,#../img/queen_interdependent.png" width="3464" style="display: block; margin: auto;" /> ] ] --- ## Let's do it hands-on: Our 'research' question Say, we are interested in AfD voting outcomes in relation to ethnic compositions of neighborhoods - combination of far-right voting research with Allport's classic contact theory - we are just doing it in the Urban context of Cologne (again) --- ## Voting districts ```r voting_districts <- sf::st_read("./data/Stimmbezirk.shp") |> sf::st_transform(3035) |> dplyr::transmute(Stimmbezirk = as.numeric(nummer)) ``` ``` ## Reading layer `Stimmbezirk' from data source ## `C:\Users\mueller2\a_talks_presentations\gesis-workshop-geospatial-techniques-R-2024\data\Stimmbezirk.shp' using driver `ESRI Shapefile' ## Simple feature collection with 543 features and 14 fields ## Geometry type: MULTIPOLYGON ## Dimension: XY ## Bounding box: xmin: 343914.7 ymin: 5632759 xmax: 370674.3 ymax: 5661475 ## Projected CRS: ETRS89 / UTM zone 32N ``` ```r head(voting_districts, 2) ``` ``` ## Simple feature collection with 2 features and 1 field ## Geometry type: MULTIPOLYGON ## Dimension: XY ## Bounding box: xmin: 4104887 ymin: 3094313 xmax: 4107089 ymax: 3096356 ## Projected CRS: ETRS89-extended / LAEA Europe ## Stimmbezirk geometry ## 1 10205 MULTIPOLYGON (((4105588 309... ## 2 10213 MULTIPOLYGON (((4106748 309... ``` --- ## AfD vote share ```r afd_votes <- glue::glue( "https://www.stadt-koeln.de/wahlen/bundestagswahl/09-2021/praesentation/\\ Open-Data-Bundestagswahl476.csv" ) |> readr::read_csv2() |> dplyr::transmute(Stimmbezirk = `gebiet-nr`, afd_share = (F1 / F) * 100) head(afd_votes, 2) ``` ``` ## # A tibble: 2 × 2 ## Stimmbezirk afd_share ## <dbl> <dbl> ## 1 10101 13.1 ## 2 10102 11.4 ``` --- ## Simple ID matching to link data ```r election_results <- dplyr::left_join( voting_districts, afd_votes, by = "Stimmbezirk" ) head(election_results, 2) ``` ``` ## Simple feature collection with 2 features and 2 fields ## Geometry type: MULTIPOLYGON ## Dimension: XY ## Bounding box: xmin: 4104887 ymin: 3094313 xmax: 4107089 ymax: 3096356 ## Projected CRS: ETRS89-extended / LAEA Europe ## Stimmbezirk afd_share geometry ## 1 10205 9.74026 MULTIPOLYGON (((4105588 309... ## 2 10213 11.76471 MULTIPOLYGON (((4106748 309... ``` --- ## Do vote shares spatially cluster? .pull-left[ ```r tm_shape(election_results) + tm_polygons( "afd_share", palette = "viridis" ) ``` ] .pull-right[ .center[ <img src="data:image/png;base64,#2_3_Investigating_Spatial_Autocorrelation_files/figure-html/does-it-cluster-show-1.png" style="display: block; margin: auto;" /> ] ] --- ## Pull in German Census data ```r immigrants_cologne <- z11::z11_get_100m_attribute(STAATSANGE_KURZ_2) |> terra::crop(election_results) |> terra::mask(election_results) inhabitants_cologne <- z11::z11_get_100m_attribute(Einwohner) |> terra::crop(election_results) |> terra::mask(election_results) immigrant_share_cologne <- (immigrants_cologne / inhabitants_cologne) * 100 ``` --- ## It's raster data .pull-left[ ```r tm_shape(immigrant_share_cologne) + tm_raster(palette = "-viridis") ``` ] .pull-right[ .center[ <img src="data:image/png;base64,#2_3_Investigating_Spatial_Autocorrelation_files/figure-html/raster-data-show-1.png" style="display: block; margin: auto;" /> ] ] --- ## Linking: Let's get geographical As the voting (vector) data is different to the Census raster data we cannot use simple ID matching like before - we have to rely on spatial linking techniques - we could use `terra::extract()` - but as a default it only captures raster cells as a whole and not their spatial fraction - which is honestly okay for most applications - but why not try something else? --- ## `exactextractr::exact_extract()`! ```r election_results <- election_results |> dplyr::mutate( immigrant_share = exactextractr::exact_extract( immigrant_share_cologne, election_results, 'mean', progress = FALSE ), inhabitants = exactextractr::exact_extract( inhabitants_cologne, election_results, 'mean', progress = FALSE ) ) head(election_results, 2) ``` ``` ## Simple feature collection with 2 features and 4 fields ## Geometry type: MULTIPOLYGON ## Dimension: XY ## Bounding box: xmin: 4104887 ymin: 3094313 xmax: 4107089 ymax: 3096356 ## Projected CRS: ETRS89-extended / LAEA Europe ## Stimmbezirk afd_share geometry immigrant_share inhabitants ## 1 10205 9.74026 MULTIPOLYGON (((4105588 309... 15.00740 140.1017 ## 2 10213 11.76471 MULTIPOLYGON (((4106748 309... 10.66915 107.6079 ``` --- ## Voilà .pull-left[ ```r tm_shape(election_results) + tm_polygons( "immigrant_share", palette = "-viridis" ) ``` ] .pull-right[ .center[ <img src="data:image/png;base64,#2_3_Investigating_Spatial_Autocorrelation_files/figure-html/extract-map-show-1.png" style="display: block; margin: auto;" /> ] ] --- ## How to test spatial autocorrelation .pull-left[ We now have to ask - do the spatial units relate to each other? - if yes, in which way? - only if they are bordering each other? (i.e., Queens or Rooks) - or also if they are in proximity but not necessarily contiguous? ] .pull-right[ .center[ <img src="data:image/png;base64,#2_3_Investigating_Spatial_Autocorrelation_files/figure-html/show-borders-1.png" style="display: block; margin: auto;" /> ] ] --- ## Let's try Queens neighborhoods ```r queens_neighborhoods <- spdep::poly2nb( election_results, queen = TRUE ) summary(queens_neighborhoods) ``` ``` ## Neighbour list object: ## Number of regions: 543 ## Number of nonzero links: 2978 ## Percentage nonzero weights: 1.010009 ## Average number of links: 5.484346 ## Link number distribution: ## ## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ## 2 12 53 107 136 102 58 30 21 13 4 2 2 1 ## 2 least connected regions: ## 69 196 with 1 link ## 1 most connected region: ## 249 with 14 links ``` --- ## Connected regions .pull-left[ ```r queens_neighborhoods |> spdep::nb2lines( coords = sf::st_as_sfc(election_results), as_sf = TRUE ) |> tm_shape() + tm_dots() + tm_lines() ``` ] .pull-right[ .center[ <img src="data:image/png;base64,#2_3_Investigating_Spatial_Autocorrelation_files/figure-html/connected-regions-show-1.png" style="display: block; margin: auto;" /> ] ] --- ## Can we now start? Unfortunately, we are yet done with creating the links between neighborhoods. What we receive is, in principle, a huge matrix with connected observations. ``` ## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] ## 1 0 0 0 0 0 0 0 0 0 0 ## 2 0 0 0 0 0 0 0 1 1 0 ## 3 0 0 0 1 1 0 0 0 1 0 ## 4 0 0 1 0 1 1 0 0 0 0 ## 5 0 0 1 1 0 1 0 0 0 0 ## 6 0 0 0 1 1 0 1 1 0 0 ## 7 0 0 0 0 0 1 0 1 1 0 ## 8 0 1 0 0 0 1 1 0 1 0 ## 9 0 1 1 0 0 0 1 1 0 0 ## 10 0 0 0 0 0 0 0 0 0 0 ``` That's nothing we could plug into a statistical model, such as a regression or the like (see next session). --- ## Normalization Normalization is the process of creating actual spatial weights. There is a huge dispute on how to do it (Neumayer & Plümper, 2016). But nobody questions whether it should be done in the first place since, among others, it restricts the parameter space of the weights. .pull-left[ non-normalized ``` ## [,1] [,2] [,3] [,4] [,5] ## 1 0 0 0 0 0 ## 2 0 0 0 0 0 ## 3 0 0 0 1 1 ## 4 0 0 1 0 1 ## 5 0 0 1 1 0 ``` ``` ## [1] 0 0 2 2 2 ``` ] .pull-right[ normalized ``` ## [,1] [,2] [,3] [,4] [,5] ## 1 0 0 0.0000000 0.000 0.0000000 ## 2 0 0 0.0000000 0.000 0.0000000 ## 3 0 0 0.0000000 0.125 0.1250000 ## 4 0 0 0.3333333 0.000 0.3333333 ## 5 0 0 0.2500000 0.250 0.0000000 ``` ``` ## [1] 0.0000000 0.0000000 0.2500000 0.6666667 0.5000000 ``` ] <br> <br> .footnote[ Neumayer, E., & Plümper, T. (2016). W. Political Science Research and Methods, 4(01), 175–193. https://doi.org/10.1017/psrm.2014.40 ] --- ## Row-normalization One of the disputed but at the same time standard procedures is row-normalization. It divides all individual weights (=connections between spatial units) `\(w_{ij}\)` by the row-wise sum of of all other weights: `$$W = \sum_j\frac{w_{ij}}{\sum_jw_{ij}}$$` An alternative would be minmax-normalization: `$$W = \sum_j\frac{w_{ij} - min(w_{ij})}{max(w_{ij})-min(w_{ij})}$$` --- ## Apply row-normalization ```r queens_W <- spdep::nb2listw(queens_neighborhoods, style = "W") summary(queens_W) ``` ``` ## Characteristics of weights list object: ## Neighbour list object: ## Number of regions: 543 ## Number of nonzero links: 2978 ## Percentage nonzero weights: 1.010009 ## Average number of links: 5.484346 ## Link number distribution: ## ## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ## 2 12 53 107 136 102 58 30 21 13 4 2 2 1 ## 2 least connected regions: ## 69 196 with 1 link ## 1 most connected region: ## 249 with 14 links ## ## Weights style: W ## Weights constants summary: ## n nn S0 S1 S2 ## W 543 294849 543 211.1469 2261.598 ``` --- ## Test of spatial autocorrelation: Moran's I `$$I=\frac{N}{\sum_{i=1}^N\sum_{j=1}^Nw_{ij}}\frac{\sum_{i=1}^{N}\sum_{j=1}^Nw_{ij}(x_i-\bar{x})(x_j-\bar{x})}{\sum_{i=1}^N(x_i-\bar{x})^2}$$` Most and foremost, Moran's I makes use of the previously created weights between all spatial units pairs `\(w_{ij}\)`. It weights deviations from an overall mean value of connected pairs according to the strength of the modeled spatial relations. Moran's I can be interpreted as some sort of a correlation coefficient (-1 = perfect negative spatial autocorrelation; +1 = perfect positive spatial autocorrelation). --- ## Moran's I in `spdep` ```r spdep::moran.test( election_results$immigrant_share, listw = queens_W ) ``` ``` ## ## Moran I test under randomisation ## ## data: election_results$immigrant_share ## weights: queens_W ## ## Moran I statistic standard deviate = 20.428, p-value < 2.2e-16 ## alternative hypothesis: greater ## sample estimates: ## Moran I statistic Expectation Variance ## 0.5408375504 -0.0018450185 0.0007057415 ``` --- ## Test of spatial autocorrelation: Geary's C Moran's I is a global statistic for spatial autocorrelation. It can produce issues when there are only local clusters of spatial interdependence in the data. An alternative is the use of `Geary's C`: `$$C=\frac{(N-1)\sum_i\sum_jw_{ij}(x_i-x_j)^2}{2\sum_{i=1}^N\sum_{j=1}^Nw_{ij}\sum_i(x_i-\bar{x})^2}$$` As you can see, in the numerator, the average value `\(\bar{x}\)` is not as prominent as in Moran's I. Geary's C only produces values between 0 and 2 (value near 0 = positive spatial autocorrelation; 1 = no spatial autocorrelation; values near 2 = negative spatial autocorrelation). --- ## Geary's C in `spdep` ```r spdep::geary.test( election_results$immigrant_share, listw = queens_W ) ``` ``` ## ## Geary C test under randomisation ## ## data: election_results$immigrant_share ## weights: queens_W ## ## Geary C statistic standard deviate = 17.355, p-value < 2.2e-16 ## alternative hypothesis: Expectation greater than statistic ## sample estimates: ## Geary C statistic Expectation Variance ## 0.442840220 1.000000000 0.001030604 ``` --- ## Modern inferface to neighbors: `sfdep` package The [`sfdep`](https://cran.r-project.org/web/packages/sfdep/index.html) package provides a more `tidyverse`-compliant syntax to spatial weights. See: ```r election_results <- election_results |> dplyr::mutate( neighbors = sfdep::st_contiguity(election_results), # queen neighborhoods by default weights = sfdep::st_weights(neighbors) ) head(election_results, 2) ``` ``` ## Simple feature collection with 2 features and 6 fields ## Geometry type: MULTIPOLYGON ## Dimension: XY ## Bounding box: xmin: 4104887 ymin: 3094313 xmax: 4107089 ymax: 3096356 ## Projected CRS: ETRS89-extended / LAEA Europe ## Stimmbezirk afd_share geometry immigrant_share inhabitants neighbors ## 1 10205 9.74026 MULTIPOLYGON (((4105588 309... 15.00740 140.1017 16, 18, 22, 25, 458, 459, 460, 462 ## 2 10213 11.76471 MULTIPOLYGON (((4106748 309... 10.66915 107.6079 8, 9, 11, 48, 49 ## weights ## 1 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125 ## 2 0.2, 0.2, 0.2, 0.2, 0.2 ``` --- ## Calculating once again Moran's I ```r library(magrittr) election_results %$% sfdep::global_moran_test(immigrant_share, neighbors, weights) ``` ``` ## ## Moran I test under randomisation ## ## data: x ## weights: listw ## ## Moran I statistic standard deviate = 20.428, p-value < 2.2e-16 ## alternative hypothesis: greater ## sample estimates: ## Moran I statistic Expectation Variance ## 0.5408375504 -0.0018450185 0.0007057415 ``` --- ## Calculating once again Geary's C ```r election_results %$% sfdep::global_c_test(immigrant_share, neighbors, weights) ``` ``` ## ## Geary C test under randomisation ## ## data: x ## weights: listw ## ## Geary C statistic standard deviate = 17.355, p-value < 2.2e-16 ## alternative hypothesis: Expectation greater than statistic ## sample estimates: ## Geary C statistic Expectation Variance ## 0.442840220 1.000000000 0.001030604 ``` --- class: middle ## Exercise 2_3_1: Neighborhood Matrices [Exercise](https://stefanjuenger.github.io/gesis-workshop-geospatial-techniques-R-2024/exercises/2_3_1_Neighborhood_Matrices.html) [Solution](https://stefanjuenger.github.io/gesis-workshop-geospatial-techniques-R-2024/solutions/2_3_1_Neighborhood_Matrices.html) --- ## Measures of local spatial autocorrelation: LISA clusters The reason why we show you the `sfdep` package is that it provides nice functions to calculate *local* measures of spatial autocorrelation. One popular choice are the estimation of Local Indicators of Spatial Autocorrelation (i.e., LISA clusters). In the most straightforward way they can be interpreted as case-specific indicators of spatial autocorrelation: `$$I_i=\frac{x_i-\bar{x}}{\frac{\sum_{i-1}^N(x_i-\bar{x})^2}{N}}\sum_{j=1}^Nw_{ij}(x_j-\bar{x})$$` --- ## LISA clusters in `sfdep` ```r lisa <- election_results |> dplyr::mutate( lisa = sfdep::local_moran(afd_share, neighbors, weights) ) |> tidyr::unnest() head(lisa, 2) ``` ``` ## Simple feature collection with 2 features and 18 fields ## Geometry type: MULTIPOLYGON ## Dimension: XY ## Bounding box: xmin: 4104887 ymin: 3095603 xmax: 4105588 ymax: 3096356 ## Projected CRS: ETRS89-extended / LAEA Europe ## # A tibble: 2 × 19 ## Stimmbezirk afd_share geometry immigrant_share inhabitants neighbors weights ii eii var_ii z_ii p_ii ## <dbl> <dbl> <MULTIPOLYGON [m]> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 10205 9.74 (((4105588 3096207, 4105571 3095… 15.0 140. 16 0.125 1.13 -0.00889 0.204 2.52 0.0116 ## 2 10205 9.74 (((4105588 3096207, 4105571 3095… 15.0 140. 18 0.125 1.13 -0.00889 0.204 2.52 0.0116 ## # ℹ 7 more variables: p_ii_sim <dbl>, p_folded_sim <dbl>, skewness <dbl>, kurtosis <dbl>, mean <fct>, median <fct>, pysal <fct> ``` --- ## They are also nice for mapping .pull-left[ ```r tm_shape(lisa) + tm_fill("afd_share", midpoint = NA, palette = "viridis") ``` <img src="data:image/png;base64,#2_3_Investigating_Spatial_Autocorrelation_files/figure-html/plain-map-1.png" style="display: block; margin: auto;" /> ] .pull-right[ ```r tm_shape(lisa) + tm_fill("ii", midpoint = NA, palette = "viridis") ``` <img src="data:image/png;base64,#2_3_Investigating_Spatial_Autocorrelation_files/figure-html/lisa-map-2-1.png" style="display: block; margin: auto;" /> ] --- ## One last bit: bivariate LISAs .pull-left[ ```r lisa_bivariate <- election_results |> dplyr::mutate( lisa = sfdep::local_moran_bv( afd_share, immigrant_share, neighbors, weights ) ) |> tidyr::unnest() tm_shape(lisa_bivariate) + tm_fill( "Ib", midpoint = NA, palette = "viridis" ) ``` ] .pull-right[ .center[ <img src="data:image/png;base64,#2_3_Investigating_Spatial_Autocorrelation_files/figure-html/lisa-map-bi-2-1.png" style="display: block; margin: auto;" /> ] ] --- ## Wrap up You now know how to - model connectedness of spatial units - investigate spatial autocorrelation - globally - locally - map it There's way more, particularly when it comes to - spatial weights (see exercise) - clustering techniques (e.g., Hot Spot Analysis) - autocorrelation with more than one or two variables **Now we know our data are spatially autocorrelated. Let's try to find out why this is the case via some spatial econometrics** --- layout: false class: center background-image: url(data:image/png;base64,#../assets/img/the_end.png) background-size: cover .left-column[ </br> <img src="data:image/png;base64,#../img/Stefan.png" width="75%" style="display: block; margin: auto;" /> ] .right-column[ .left[.small[<svg viewBox="0 0 512 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"></path> </svg> [stefan.juenger@gesis.org](mailto:stefan.juenger@gesis.org)] .small[<svg viewBox="0 0 512 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path> </svg> [`@StefanJuenger`](https://twitter.com/StefanJuenger)] .small[<svg viewBox="0 0 496 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path> </svg> [`StefanJuenger`](https://github.com/StefanJuenger)] .small[<svg viewBox="0 0 576 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z"></path> </svg> [`https://stefanjuenger.github.io`](https://stefanjuenger.github.io)]] ]