<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ina Krapp</title><link>https://inakrapp.github.io/</link><atom:link href="https://inakrapp.github.io/index.xml" rel="self" type="application/rss+xml"/><description>Ina Krapp</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Mon, 24 Oct 2022 00:00:00 +0000</lastBuildDate><image><url>https://inakrapp.github.io/media/icon_hu7729264130191091259.png</url><title>Ina Krapp</title><link>https://inakrapp.github.io/</link></image><item><title>sfcentralities: Calculating centralities from sf objects</title><link>https://inakrapp.github.io/post/sfcentralities/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/post/sfcentralities/</guid><description>&lt;p>While I generally like to work with the &lt;code>sf&lt;/code> library in R, it is unfortunate that the R ecosystem when it comes to spatial data is quite heterogeneous: Available spatial formats do not only include vector and raster data, but the structures in which they are stored are also usually heavily influenced by the design choices of authors of certain packages:&lt;/p>
&lt;ol>
&lt;li>
, probably the first widely used spatial package in R - at least, it is among the older ones, and
, which is often treated as its successor.&lt;/li>
&lt;li>
, a package by the authors of &amp;ldquo;Spatial Point Patterns: Methodology and Applications with R&amp;rdquo;&lt;/li>
&lt;li>
is more often used for raster data, but also has capabilities to handle vector data, and is, in many ways, a successor to the
package&lt;/li>
&lt;/ol>
&lt;p>&amp;hellip;and then there is the option to store coordinates in an ordinary R dataframe with columns named &amp;lsquo;Longitude&amp;rsquo; and &amp;lsquo;Latitude&amp;rsquo; or &amp;lsquo;X&amp;rsquo; and &amp;lsquo;Y&amp;rsquo; and ignore spatial packages entirely.&lt;/p>
&lt;p>These are just the packages and ways to work with spatial data I encountered most frequently. And spatio-temporal analysis is also growing more common (anyone interested in this field: I highly recommend taking a look at the
package).&lt;/p>
&lt;p>I think it is an unfortunate situation for beginners who aim to perform spatial analysis in R. Even if you have some experience, especially if it is in one package, working with another package can feel like starting from zero again. Many authors have valid reasons for their design choices, and there is a growing awareness of the issue; many packages also contain functions to transform data from one format to the other. But sometimes, I find functions implemented in packages that do not see themselves as &amp;lsquo;spatial&amp;rsquo; to begin with.&lt;/p>
&lt;p>For example, when I tried to find out how to calculate the geometric median in R, the R package that appeared first in the search results was
- Practical Numerical Math Functions. The package is impressive for the breadth of functions it covers, but this result also shows how different the contexts are in which functions are used. The geometric median can be used by geographers to find a central point on the map, but is also used, for example, in principal component analysis - which is an entirely different story. There are even more packages in R which offer functions for the calculation of geometric medians -
, for example, which is particularly optimized for high-dimensional, large datasets and very fast due to its reliance on C++. So many users of the geometric median do not analyse spatial data.&lt;/p>
&lt;p>But I did, for a project, and this is a part of where
(more precisely: the function &lt;code>st_geo_median&lt;/code>) comes from. It takes &lt;code>sf&lt;/code> objects as input, it gives &lt;code>sf&lt;/code> objects as output, it is not reinventing the wheel, but beginner-friendly and easy to use - at least I hope so. But it is not entirely a package purely written with convenience in mind: Since spatial data can be in projected or geographic coordinates, using purely-number based implementations like &lt;code>pracma&lt;/code> or &lt;code>Gmedian&lt;/code>, which can not properly handle longitude and latitude values can give wrong results - &lt;code>sfcentralities&lt;/code> will give an error if the user attempts it.&lt;/p>
&lt;p>Why can other packages give wrong results? Because distances in the longitude-latitude-system are not constant: One degree corresponds to a different distance in meters depending on if you are at the equator or at the poles. So, despite the sometimes complex implementations of these packages, there are good reasons to use spatial packages for spatial data.&lt;/p>
&lt;p>&lt;code>sfcentralities&lt;/code> is also a bit of an attempt to bridge the gap between &lt;code>sf&lt;/code> and the
package. &lt;code>dodgr&lt;/code> is one of the packages that just show how powerful R can be. &lt;code>dodgr&lt;/code> stands for &amp;lsquo;Distances on Directed Graphs&amp;rsquo;, and while it can take &lt;code>sf&lt;/code> objects as input and even offers a function to download data from OpenStreetMap in &lt;code>sf&lt;/code> format to use for analysis, it also heavily builds on its own data format,
. It allows very fast and precise distance and time calculations even in complex street networks, since it is able to take into account different modes of transport, one-way streets and other factors that go beyond a simple &amp;lsquo;distance in kilometers/miles&amp;rsquo; measure.&lt;/p>
&lt;p>I have been using it to calculate a similar measure to the geometric median - a measure which minimizes the sum of distances to points - along a street network. This is not really equivalent to the geometric median because it does not necessarily fulfill a global optimality condition - I can say that the geometric median is the point which minimizes the sum of distances to all points in a set since the geometric median is calculated using Euclidean (straight-line) distances. For a network distance, I can only say that a certain point I evaluated has a higher closeness than other points I evaluated. This is the closeness centrality, and in &lt;code>sfcentralities&lt;/code>, it is implemented in the function &lt;code>st_closeness_centrality&lt;/code>.&lt;/p></description></item><item><title>sfcentralities: Calculating centralities from sf objects</title><link>https://inakrapp.github.io/software/sfcentralities/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/software/sfcentralities/</guid><description>&lt;p>I published an R package which aims to simplify centrality calculations in spatial data using the sf package.&lt;/p></description></item><item><title>Spatial Data Analysis in R</title><link>https://inakrapp.github.io/teaching/spatial-data-analysis-in-r/</link><pubDate>Wed, 15 Oct 2025 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/teaching/spatial-data-analysis-in-r/</guid><description>&lt;ol>
&lt;li>Introduction&lt;/li>
&lt;/ol>
&lt;p>The spatial data ecosystem in R comprises a number of packages, each tailored to a particular data type or workflow.The most common data objects are:&lt;/p>
&lt;p>Points – e.g. locations of cities or sampling sites&lt;/p>
&lt;p>Lines – e.g. roads, rivers or migration routes&lt;/p>
&lt;p>Polygons – e.g. country borders or land‑use zones&lt;/p>
&lt;p>Rasters – gridded values such as elevation or satellite imagery&lt;/p>
&lt;p>Many packages exist for these data types in R: sp/sf handle vector data, raster/terra work with rasters, stars is specialised for spatio‑temporal objects, and spatstat focuses on point pattern analysis, but also offers raster tools.&lt;/p>
&lt;p>We will focus on sf, terra and spatstat here.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sf&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># Spatial Features - vector data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tidyverse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">here&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">spatstat&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># Point Pattern Analysis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">terra&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># For robust spatial operations, especially with raster data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mgcv&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># Generalized additive regression&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The code used for this workshop can be found under:
. Most of the spatial datasets used in this workshop are too large to be uploaded on Github. To replicate the analysis, they have to be downloaded from their original sources.&lt;/p>
&lt;ol start="2">
&lt;li>Get country borders and cities:&lt;/li>
&lt;/ol>
&lt;p>The Natural Earth project provides free vector layers that are well suited for a first look at the Korean peninsula.&lt;/p>
&lt;p>Download countries and populated places from natural earth data:
&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">countries&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">st_read&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">here&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cities&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">st_read&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">here&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;ne_10m_populated_places/ne_10m_populated_places.shp&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">ggplot&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">geom_sf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">countries&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">geom_sf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">cities&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>Select the Korean Peninsula&lt;/li>
&lt;/ol>
&lt;p>The countries and cities objects contain worldwide data; we extract only the parts that belong to North and South Korea.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Filter cities and countries for North and South Korea&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">korea_countries&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="n">countries&lt;/span> &lt;span class="o">%&amp;gt;%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">filter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SOVEREIGNT&lt;/span> &lt;span class="o">%in%&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;North Korea&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;South Korea&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cities_korea&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="n">cities&lt;/span> &lt;span class="o">%&amp;gt;%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">filter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ADM0NAME&lt;/span> &lt;span class="o">%in%&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;North Korea&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;South Korea&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Verify all korean cities lie in North or South Korea:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">ggplot&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">geom_sf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">korea_countries&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">geom_sf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">cities_korea&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="4">
&lt;li>Load Night‑Light Data&lt;/li>
&lt;/ol>
&lt;p>The Consistent and Corrected Nighttime Light dataset (CCNL) from DMSP‑OLS provides a global raster that records artificial illumination. You can download it here:
&lt;/p>
&lt;p>We will use the data from 2013.&lt;/p>
&lt;p>This is a tif file: An image with very high resolution.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">img&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">rast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">here&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;CCNL_DMSP_2013_V1.tif&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Satellite images, which covers the entire earth at high accuracy (sometimes one pixel per meter or even more detailed) can become extremly large.&lt;/p>
&lt;p>terra is very effective in handling such large datasets, but it is highly advised to select only parts/areas needed for analysis before performing any operations with them. The image above, turned entirely into R&amp;rsquo;s standard dataframe format, would have been over 5 GB large.&lt;/p>
&lt;p>For our analysis, we only need the Korean peninsula, so we crop the data to the extent of the peninsula before any further processing.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Get extent of the Korean states in a format terra understands:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">v&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">ext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">korea_countries&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Keep only part of the image which shows the peninsula:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">img_nightlight_korea&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">crop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Plot again to verify the new image shows the peninsula:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img_nightlight_korea&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="5">
&lt;li>Re‑project to a Projected CRS&lt;/li>
&lt;/ol>
&lt;p>Globally, locations are usually given in latitude/longitude. This has the disadvantage that distance calculations are difficult: One degree near the equator has a different meaning than one degree near the poles.&lt;/p>
&lt;p>For local maps, projections which usually use meters/kilometers are used instead. Because they project the earth&amp;rsquo;s three-dimensional surface onto a flat two-dimensional area, they are different in each region. For the Korean peninsula, we use a projection with the EPSG code 5179, which is also the official projection of the south korean government.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">cities_korea&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="n">cities_korea&lt;/span> &lt;span class="o">%&amp;gt;%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">st_transform&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">crs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;EPSG:5179&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">korea_countries&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="n">korea_countries&lt;/span> &lt;span class="o">%&amp;gt;%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">st_transform&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">crs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;EPSG:5179&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The satellite image also has to be reprojected:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Reproject satellite image&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">img_nightlight_korea&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img_nightlight_korea&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;EPSG:5179&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="6">
&lt;li>Prepare Data for spatstat&lt;/li>
&lt;/ol>
&lt;p>Unfortunately, since spatstat, sf and terra were developed with different data formats in mind, some more modifications have to be done to turn the data into formats used by spatstat:&lt;/p>
&lt;p>6.1 Define the Observation Window&lt;/p>
&lt;p>First, for spatstat, one needs to define a region in which the point patterns occur, called a &amp;lsquo;window&amp;rsquo;. For our analysis, we treat only the land area of the korean peninsula as window:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Create observation window: We use the union of North and South Korea&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">window_sf&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">st_union&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">korea_countries&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Convert to owin (spatstat window format)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">window_owin&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">as.owin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">window_sf&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>6.2 Convert City Coordinates to a ppp Object&lt;/p>
&lt;p>In the next step, the coordinates of the korean cities are extracted and a ppp object is formed with them:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Extract coordinates&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">city_coords&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">st_coordinates&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cities_korea&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Create ppp object&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">city_ppp&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">ppp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">city_coords[&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="n">]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">city_coords[&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">2&lt;/span>&lt;span class="n">]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">window&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">window_owin&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The cities&amp;rsquo; locations are now formed as a ppp - point pattern process. Again, we can look at the data:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">city_ppp&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>6.3 Convert Night‑Light Raster to an im Object&lt;/p>
&lt;p>To be able to include satellite data into the analysis, its format also needs to be adjusted:&lt;/p>
&lt;p>The code below turns the satellite image into the &amp;lsquo;im&amp;rsquo; format, which is used for raster data in spatstat.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Turning it into the im format used by spatstat contains two steps: &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#Turn it into a dataframe and them an &amp;#39;im&amp;#39; object.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">df&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">as.data.frame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img_nightlight_korea&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">xy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">nightlight&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">as.im&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">df&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Verify again that the data looks correct:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">nightlight&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="7">
&lt;li>Point‑Pattern Modelling&lt;/li>
&lt;/ol>
&lt;p>Point patterns in spatstat can be modeled using a number of different models. We explore three models:&lt;/p>
&lt;p>Homogeneous Poisson – assumes random locations of cities&lt;/p>
&lt;p>Clustered (Matérn) Process – assumes clustering of cities&lt;/p>
&lt;p>Inhomogeneous Poisson with Night‑Light Covariate – assumes probability of a city location changes with light intensity&lt;/p>
&lt;p>7.1 Homogeneous Poisson&lt;/p>
&lt;p>The first model below assumes a poisson process: It assumes that points appear randomly with a certain probability in space. The process is called homogenous because this probability is assumed to be equal everyhwere in the window.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Model 1: Homogeneous Poisson process&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">fit_homog&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">ppm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">city_ppp&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>7.2 Matérn Clustered Process&lt;/p>
&lt;p>In reality, points often cluster and are more likely to be found close to other points. The second model assumes a matern clustering process.&lt;/p>
&lt;p>This process models the idea the observed points were created as follows: First, a homogenous poisson process created points at random locations in the window. Then, points formed with an increased probability around these first points.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Model 2: Clustered proess:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">fit_cluster&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">kppm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">city_ppp&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">method&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;palm&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">clusters&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;MatClust&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>There are many other clustering processes which can be modeled.&lt;/p>
&lt;p>7.3 Inhomogeneous Poisson with night light&lt;/p>
&lt;p>One can also include covariates. Below is a model using the nightlight intensity from the satellite data as covariate:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Model 3: Inhomogeneous with night brightness&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">fit_light&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">ppm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">city_ppp&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">nightlight&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>7.4 Model Comparison by AIC&lt;/p>
&lt;p>To evaluate the models, one can compare the AICs. They contain information about model accuracy while penalizing overly complex models.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Step 5: Compare AICs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">AIC_homog&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">AIC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fit_homog&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">AIC_cluster&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">AIC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fit_cluster&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">AIC_light&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">AIC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fit_light&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Display comparison&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">aic_comparison&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">data.frame&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Homogeneous&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Light&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Cluster&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AIC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">AIC_homog&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">AIC_light&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">AIC_cluster&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">aic_comparison&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The model including nightlight intensity has the lowest AIC.&lt;/p>
&lt;p>We can take at look at its coefficients:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Print summary of best model&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">best_model_index&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">which.min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">aic_comparison&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">AIC&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">best_model_name&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="n">aic_comparison&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">Model[best_model_index]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">cat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;\nBest model by AIC:&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">best_model_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;\n&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">paste0&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;fit_&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">tolower&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">gsub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34; &amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;_&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">best_model_name&lt;/span>&lt;span class="p">)))))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The night light intensity has a statistically significant effect on the probability that a city is located at a certain place.&lt;/p>
&lt;ol start="8">
&lt;li>A raster regression&lt;/li>
&lt;/ol>
&lt;p>We&amp;rsquo;ve seen now that light can be used to predict where to find cities. It is often used for that purpose and as a proxy variable for income. Note that the correlation &amp;lsquo;more light = increased chance of finding a city&amp;rsquo; holds for both North and South Korea.&lt;/p>
&lt;p>But on average, South Korea is much more brightly lit at night than the north - and this is not limited to the cities we saw on the map. We will now look at a model which models this difference in brightness.&lt;/p>
&lt;p>8.1 Load Population Raster Data&lt;/p>
&lt;p>Our cities dataset does not perfectly capture where people live: While it gives the location of the peninsula&amp;rsquo;s largest cities, it only contains a small number of smaller towns and villages. In point data, cities are also modeled as points while in reality, they often cover large areas. To get another view on where people live in North and Korea South Korea, we will look at another dataset.&lt;/p>
&lt;p>Again, we will be working with tif images. This time, we will be using 2015 population estimates from the WorldPop project:
&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">img_north&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">rast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">here&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;prk_pop_2015_CN_100m_R2025A_v1.tif&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img_north&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">img_south&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">rast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">here&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;kor_pop_2015_CN_100m_R2025A_v1.tif&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img_south&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Re‑project them to EPSG:5179&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">img_north&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img_north&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;EPSG:5179&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">img_south&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img_south&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;EPSG:5179&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The North is generally less densely populated than the South: North Korea has around 25 Million inhabitants while around 50 Million people live in South Korea.&lt;/p>
&lt;p>8.2 Build a Unified Raster Stack&lt;/p>
&lt;p>In the next step, we combine the data. We now have several raster datasets, but since they are from different sources, we have to make sure they &amp;lsquo;fit&amp;rsquo; onto each other.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># --- Step 1: Define the exact extent of the area our data should cover&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">korea_mask&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">rasterize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">korea_countries&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">img_nightlight_korea&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">field&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># --- Step 2: Prepare the Brightness Response Variable ---&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Mask the light data and then log-transform it.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">korea_lights&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">mask&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img_nightlight_korea&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">korea_mask&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">final_brightness&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">korea_lights&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="m">0.01&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">names&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">final_brightness&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="s">&amp;#34;log_brightness&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Create Country Predictor from sf dataset of North and South Korea borders:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">korea_countries&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="n">korea_countries&lt;/span> &lt;span class="o">%&amp;gt;%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">mutate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">is_south&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">ifelse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ADMIN&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s">&amp;#34;South Korea&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">country_raster&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">rasterize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">korea_countries&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">img_nightlight_korea&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">field&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;is_south&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">final_country&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">mask&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">country_raster&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">korea_mask&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># Mask it to the study area.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. The Population Predictor (&amp;#39;log_pop&amp;#39;)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Align each population raster to the master grid (img_nightlight_korea or korea_mask).&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">pop_north_aligned&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img_north&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">korea_mask&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">method&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;bilinear&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">pop_south_aligned&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img_south&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">korea_mask&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">method&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;bilinear&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Merge them. The result is a raster with the correct extent but NAs in the ocean.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">pop_aligned_merged&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">cover&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pop_north_aligned&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">pop_south_aligned&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Log-transform the population count:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">log_pop_aligned&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pop_aligned_merged&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Impute all NAs with 0.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">pop_imputed&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">ifel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">is.na&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">log_pop_aligned&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">log_pop_aligned&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This clips away the values in the ocean, leaving only the data within the peninsula.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">final_pop&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">mask&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pop_imputed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">korea_mask&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">names&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">final_pop&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="s">&amp;#34;log_pop&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># --- Step 5: Assemble the Final Stack and Proceed to Modeling ---&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model_stack&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">final_brightness&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">final_country&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">final_pop&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">gam_df&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">as.data.frame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model_stack&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">xy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>8.3 Generalised Additive Modelling&lt;/p>
&lt;p>Spatial data usually contains spatial autocorrelation (neighboring observations are correlated). There is extensive literature about possible approaches to address this issue, but no consensus.&lt;/p>
&lt;p>For this course, we will use a generalized additive model, which allows to include a function that aims to predict brightness values based on x-y-coordinates.&lt;/p>
&lt;p>We can fit various kinds of models such as one which only includes population, only the country (North or South Korea) or both. We will start with one which fits the s(x,y)-function that models autocorrelation.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">gam_model&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">gam&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">log_brightness&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="nf">s&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">100&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gam_df&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># View the results&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gam_model&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Autocorrelation is often a powerful predictor.&lt;/p>
&lt;p>But that does not mean other variables, such as the indicator if an area is north or south korean, will not be significant:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">gam_model_light&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">gam&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">log_brightness&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">is_south&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nf">s&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">100&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gam_df&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># View the results&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gam_model_light&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Areas in the South are significantly brighter in general.&lt;/p>
&lt;p>Intuitively, this makes sense: Imagine you were standing at the border. Although North Korea might just be a few hundred meters away from you, if you were in South Korea, you could still expect a village on your side of the border to be much more brightly illuminated than on the other side.&lt;/p>
&lt;p>Population also plays a role. Populated areas are generally brighter at night:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">gam_model_light_population&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">gam&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">log_brightness&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">is_south&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">log_pop&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nf">s&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">100&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gam_df&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># View the results&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gam_model_light_population&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Again, this is what we would expect intuitively. Although South Korea&amp;rsquo;s cities emit much more light during the night, in areas where no one lives, there is no need to put up streetlights.&lt;/p>
&lt;p>But in North Korea, populated areas often remain dark as well. Satellite imagery of nightlights is often used as an economic indicator for areas where there is little other data available.&lt;/p>
&lt;ol start="9">
&lt;li>References&lt;/li>
&lt;/ol>
&lt;p>There are many good resources on working with spatial data in R:&lt;/p>
&lt;p>An Introduction to Spatial Data Analysis and Statistics: A Course in R by Antonio Paez:
ISBN: 978-1-7778515-0-7 DOI: 10.5281/zenodo.5155982&lt;/p>
&lt;p>Spatial Statistics for Data Science: Theory and Practice with R by Paula Moraga:
&lt;/p>
&lt;p>The terra package by Robert J. Hijmans has very extensive documentation online:
&lt;/p>
&lt;p>For more technical topics such as map projections in R, or how to work with different geodata formats, Geocomputation with R by Robin Lovelace, Jakub Nowosad and Jannes Muenchow is a good reference:
&lt;/p>
&lt;p>The photo of the korean peninsula at night is from NASA:
&lt;/p></description></item><item><title>Writing Papers in RMarkdown</title><link>https://inakrapp.github.io/event/writing_papers_with_r_markdown/</link><pubDate>Tue, 29 Apr 2025 14:00:00 +0000</pubDate><guid>https://inakrapp.github.io/event/writing_papers_with_r_markdown/</guid><description/></item><item><title>AI for non-programmers</title><link>https://inakrapp.github.io/event/ai_non_programmers/</link><pubDate>Wed, 09 Oct 2024 14:00:00 +0000</pubDate><guid>https://inakrapp.github.io/event/ai_non_programmers/</guid><description/></item><item><title>Common errors in R and how to solve them</title><link>https://inakrapp.github.io/event/errors_r/</link><pubDate>Tue, 08 Oct 2024 14:00:00 +0000</pubDate><guid>https://inakrapp.github.io/event/errors_r/</guid><description/></item><item><title>How do German energy partnerships affect trade?</title><link>https://inakrapp.github.io/projects/preprint/</link><pubDate>Fri, 20 Sep 2024 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/projects/preprint/</guid><description>&lt;p>This paper found that some energy partnerships have slight positive effects on overall trade and negative effects on fossil-fuel-intensive trade. However, overall, the results were very heterogenous.&lt;/p></description></item><item><title>Software</title><link>https://inakrapp.github.io/software/</link><pubDate>Sun, 19 May 2024 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/software/</guid><description/></item><item><title>Wisp: A locally running version of Whisper</title><link>https://inakrapp.github.io/post/wisp/</link><pubDate>Sat, 13 Apr 2024 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/post/wisp/</guid><description>&lt;p>Whisper is a transcription software that allows to turn audio files into text. I created a
locally running version of it, Wisp, with the aim to give it a simple, intutive user interface. My project can be found here:
&lt;/p>
&lt;p>Whisper itself has been developed by OpenAI, which is also the company behind ChatGPT and several other Artificial Intelligence programs.
You can try it out here: ‚
‘.&lt;/p>
&lt;p>Unlike ChatGPT, Whisper does not have a user interface designed by OpenAI. Its demos, as you might have seen if you tried it out above, often are used by many people at the same time. Since they all send their requests to the same computer, people may have to wait a very long time before they receive the text. Alternatively, Whisper can be run locally, using Python, but for anyone who does not know how to use a programming language, this is not an option.&lt;/p>
&lt;p>So the aim of my project was to make Whisper easy to use for anyone, on their own computer.&lt;/p>
&lt;p>Wisp is supposed to run without internet connection. Any user runs it on their computer, meaning that the users won‘t have to wait before the program finished the text of someone else. Since it has a graphical user interface, it is easy to use for anyone who is familiar with standard office software like Microsoft Office.&lt;/p>
&lt;p>Like with many of my other projects, I learnt a lot in the process of building this program. Before I started, I had no experience in working with audio data and very little with building a graphical user interface in Python. It was also my first time I turned a Python program into an executable windows program.&lt;/p></description></item><item><title>Wisp: A locally running version of Whisper</title><link>https://inakrapp.github.io/software/wisp/</link><pubDate>Sat, 13 Apr 2024 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/software/wisp/</guid><description>&lt;p>Whisper is an AI that turns speech into text. I created a simple, intutive user interface which allows anyone to run it locally.&lt;/p></description></item><item><title>Introduction to R</title><link>https://inakrapp.github.io/teaching/introduction-to-r/</link><pubDate>Mon, 20 Nov 2023 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/teaching/introduction-to-r/</guid><description>&lt;p>This workshop was given on the 10th of October 2023 as a SAFE Research Data Seminar at the Leibniz Institute for Financial Research SAFE.&lt;/p>
&lt;p>In this tutorial, we will use R and RStudio. RStudio is a user interface which makes working with
R much more convenient. This tutorial is intended to be run as a QuartoMarkdown file in RStudio, which allows you to execute the code yourself. To run it, download the
and open it in a recent version of RStudio.&lt;/p>
&lt;h2 id="where-can-i-get-r">Where can I get R?&lt;/h2>
&lt;p>Here:
&lt;/p>
&lt;p>We&amp;rsquo;ll also use RStudio. You can get it here:
&lt;/p>
&lt;h2 id="what-is-r">What is R?&lt;/h2>
&lt;p>R is a programming language often used for Data Analysis.&lt;/p>
&lt;p>Programming your analysis with R instead of doing it in, say, Excel, has a large number of advantages:&lt;/p>
&lt;ol>
&lt;li>It can easily be rerun. You can change your input (for example, append more data) and simply rerun everything instead of having to click through the whole analysis again. That also makes it much more replicable: You never need to worry about forgetting what you did. Anyone else can replicate your analysis easily as well.&lt;/li>
&lt;li>R is non-commercial. It is free, so you never have to worry about license fees!&lt;/li>
&lt;li>Related to that, anyone can implement and customize anything in R. You never have to wait for a company to finally release a new version with that new function everyone waited for.&lt;/li>
&lt;/ol>
&lt;p>RStudio is a user interface.&lt;/p>
&lt;p>If you are completely new to programming, you may not know yet what the code below does (though you can probably guess it).
Run it (by clicking on the green dart on the right) and see if you&amp;rsquo;re right.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="m">1&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can use R as a calculator. Change this code to calculate how much 2 and 3 are. In practice, you will often use code of other people and modify it.&lt;/p>
&lt;p>But of course, there&amp;rsquo;s much more to R than that.&lt;/p>
&lt;h2 id="vectors">Vectors&lt;/h2>
&lt;p>When you have a dataset, you&amp;rsquo;ll usually not just have one or two numbers. Assume you have data on a household. Two people are children and earn nothing, the father earns 1000 Euros a month, the mother 3000. There are different ways you could work with this data in R.&lt;/p>
&lt;p>For example, you could put it into a vector. A vector can be created in R by typing c() and entering the values, separated by commas, between the brackets. The letter c is used because this is a column vector - you can imagine it as being the column of a table.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">household_income&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="m">1000&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="m">3000&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the upper right of the monitor, in the field &amp;lsquo;Environment&amp;rsquo;, you can see the numbers now. The Environment now contains the value household_income, which is a vector with the four numbers I entered above. You can call it and look at it by typing its name - auto-complete will help with that.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">household_income&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you only want to look at individual values, you can call them by their position in the vector.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">household_income[4]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can calculate with them. A major advantage of R is that it is vectorized - you can calculate with an entire vector just like you would with a single value.&lt;/p>
&lt;p>Assume each member of the family gets 100 Euro by the state. Then, you can simply calculate that with this code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">household_income&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="m">100&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that the values in the environment are still the old ones. The result of the calculations is only shown, it is not saved. To save a value, you need to assign it to a variable name:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">new_household_income&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">household_income&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="m">100&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The equal sign in many programming languages is used in that way. But most other mathematical symbols do the same as you know from school math.&lt;/p>
&lt;p>The newly created variable is again visible in the &amp;lsquo;Environment&amp;rsquo; pane. To see it here again, simply type its name.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">new_household_income&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When you run such code, you can assign it to the same variable name you used before. Assume each family member gets another 200 Euro tax returns:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">new_household_income&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">new_household_income&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="m">200&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>But be careful when running such code. Run it again and look at how the values (in the &amp;lsquo;Environment&amp;rsquo; pane) are changing when you do so.&lt;/p>
&lt;p>Each time you run the code, another 200 gets added. Running it more than once therefore gives you a wrong value. Click on the broom (above the &amp;lsquo;Environment&amp;rsquo; pane) to clear out the wrong value. Don&amp;rsquo;t be concerned about deleting everything: Remember that R is easily replicable. All data this pane contained can be recreated by simply running the complete code again. Click on the &amp;lsquo;run all chunks above&amp;rsquo; button in the code block (this is the grew downward arrow to the left of the green arrow, ) to recreate the environment.&lt;/p>
&lt;p>You can also, for example, add or subtract vectors from each other. But you should only do that when they have the same length. Assume, for example, each family member spent some money: The children spent 10 and 20 Euros on sweets, the father was shopping for 500 Euros, the mother paid the rent for 1500 Euros. Then you can calculate how much money is left like that:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">new_household_income&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">20&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">1500&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Assume each member pays another 10 Euros when they all go to the cinema together. How can this be calculated?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">new_household_income&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="m">10&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This gives the same result:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">new_household_income&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">10&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="functions">Functions&lt;/h2>
&lt;p>There are also functions available to make calculations simpler. Assume you&amp;rsquo;d want to know how much money the family owns on average. You could calculate it like that:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="n">new_household_income[1]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">new_household_income[2]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">new_household_income[3]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">new_household_income[4]&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="m">4&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That is a perfectly valid calculation. It adds the money of all family members and divides it by the number of family members. But it took a while to type. R has a build-in function that does the job:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">new_household_income&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Likewise one can find the largest or smallest value in a vector with the functions sum, max and min, and do many other things. Assume you want to find a function but don&amp;rsquo;t know if it exists. In that case, the &amp;lsquo;Help&amp;rsquo; pane (below the &amp;lsquo;Environment&amp;rsquo; pane) is your best friend.&lt;/p>
&lt;p>Use it to find the function that sums up all elements in the vector and calculate how much all family members earn combined.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">new_household_income&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Most functions have several arguments, separated by commas. The first argument is usually the data the function should be applied to,
and it is almost always required. But often, other arguments are optional.
For example, functions like mean and sum include a na.rm argument. If you set it to TRUE, the function will ignore missing values.
As default, it is set to false.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">household&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">100&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">100&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">NA&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">200&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">household&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># False is the default, so this is the same as above:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">household&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">na.rm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">FALSE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Setting it to TRUE ignores the missing value:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">household&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">na.rm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="dataframes">Dataframes&lt;/h2>
&lt;p>There are different types of data structures in R. As mentioned above, you can imagine a vector also as column of a table. It does not necessarily have to contain numbers. The code below constructs a character vector:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">household_names&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#39;Anna&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#39;Benny&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#39;Christian&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Daniela&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>But with such vectors, of course, you can&amp;rsquo;t do many calculations.&lt;/p>
&lt;p>More frequently, you&amp;rsquo;ll encounter them as parts of dataframes. These are entire tables, which you can imagine as two or more columns side by side. You can create them by giving vectors of the same length into the data.frame function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">household_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">data.frame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">household_names&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">household_income&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, there&amp;rsquo;s a new field in the Environment in the upper right corner. It is called Data and contains &amp;lsquo;household_data&amp;rsquo;. You can click on the light blue button besides it to look at it into more detail.&lt;/p>
&lt;p>This object is described as &amp;lsquo;4 obs. of 2 variables&amp;rsquo;, in long form: &amp;lsquo;4 observations of 2 variables&amp;rsquo;.&lt;/p>
&lt;p>R always interprets dataframes in this way: It is assumed that each column in the table represents a specific variable ( here, name and income of each family member) and each row represents an observation for the variables (here, we have 4 rows, one for each family member).&lt;/p>
&lt;p>Each column of a dataframe, being a variable, has a variable name. Since the dataframe was constructed from the named vectors household_income and household_names, the variables use these words as names. You can access them by writing the name of the dataframe, then a $-sign, then the name of the column (again, autocomplete will help and show which variables are in the dataframe).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">household_data&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">household_income&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This looks like a vector and behaves like a vector because it is - surprise - a vector. Again, you can simply take the mean, median, sum or use some other function that accepts a vector as input.&lt;/p>
&lt;p>Calculate the median of the household_income from the household_data dataframe.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">median&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">household_data&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">household_income&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>On dataframes, you can also conduct much more complex forms of analysis - from fixed effects regression to timeseries forecasting, R covers almost all modern forms of statistical analysis. This tutorial will not cover all that, but the second workshop will show those who are interested how to conduct a standard linear regression.&lt;/p>
&lt;p>To finish this first part, we&amp;rsquo;ll look at packages and what you can do with them.&lt;/p>
&lt;h2 id="installing-and-activating-a-package">Installing and activating a package&lt;/h2>
&lt;p>R is Open Source. A Core Team is working on it, but anyone can contribute. Contributions are typically packages that can be installed. They allow many, many things - creating maps, developing apps or animating small films, for example.&lt;/p>
&lt;p>The package we&amp;rsquo;ll install now serves a more mundane purpose: Data comes in a wide variety of formats. The standard Stata format (dta), Excel-files (xlsx) and csv-files are just a few examples. Packages allow to work with all of them.&lt;/p>
&lt;p>We&amp;rsquo;ll load a csv-file with the readr-package.&lt;/p>
&lt;p>The library-function allows you to activate a package. But if you copy the code below, you&amp;rsquo;ll receive an error. This is because the package is not installed yet. You can install a package by going to &amp;lsquo;Packages&amp;rsquo; in the lower left pane, clicking on &amp;lsquo;Install&amp;rsquo; and typing the name of the package which you want to install.&lt;/p>
&lt;p>The run the library function and your package is activated. Now it can be used.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">readr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We&amp;rsquo;ll use the package to load the data from here:
&lt;/p>
&lt;p>Download it and put it into the same folder that you have currently open in RStudio. A file named &amp;lsquo;penguins.csv&amp;rsquo; should be visible in &amp;lsquo;Files&amp;rsquo; now.&lt;/p>
&lt;p>Now, there are two ways to load a file into R. One is to go to the &amp;lsquo;Files&amp;rsquo; pane (another pane in the lower right) and search the csv file. Once you found it, click on it with the left mouse button and then click on &amp;lsquo;Import Dataset&amp;rsquo;.&lt;/p>
&lt;p>The other one is using code. It has the advantage that it can easily be replicated. For that reason, if you click on &amp;lsquo;Import &amp;lsquo;, you get a code preview in the lower right corner. Copy it to the code block below.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">penguins&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">read_csv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;penguins.csv&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">View&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">penguins&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We already loaded readr, so you can delete the library command before running the code.&lt;/p>
&lt;p>The View function shows the dataset. But you can also look at it by clicking on the word &amp;lsquo;penguins&amp;rsquo; in the &amp;lsquo;Environment&amp;rsquo; pane.&lt;/p>
&lt;p>In the workshop &amp;lsquo;Linear Regression with R&amp;rsquo;, we&amp;rsquo;ll work with this dataset and look what it tells us about the penguins.&lt;/p>
&lt;p>Finally, to convince yourself that R code is replicable, click on the broom in the environment pane to delete all objects.
Then click on the Run-button above and on &amp;lsquo;Run All&amp;rsquo;. You&amp;rsquo;ll see the objects will be created again.&lt;/p></description></item><item><title>Linear Regression in R</title><link>https://inakrapp.github.io/teaching/linear-regression-with-r/</link><pubDate>Mon, 20 Nov 2023 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/teaching/linear-regression-with-r/</guid><description>&lt;h2 id="load-data">Load data&lt;/h2>
&lt;p>If you were not attending the previous workshop &amp;lsquo;Introduction to R&amp;rsquo;, you&amp;rsquo;ll need to load the data first:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">readr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We&amp;rsquo;ll use the package to load the data from here:
&lt;/p>
&lt;p>Download it and put it into the same folder that you have currently open in RStudio.&lt;/p>
&lt;p>Read it into R:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">penguins&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">read_csv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;penguins.csv&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">View&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">penguins&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="modifying-data">Modifying data&lt;/h2>
&lt;p>Often, when you&amp;rsquo;ve loaded the data, you will not be immediately able to work with it. More often than not, it needs to be changed beforehand.&lt;/p>
&lt;p>You can not simply write in a dataframe by clicking on it. This is because such changes would not be reproducible. You can modify a dataframe in any way you want, but in R, it has to be done using code.&lt;/p>
&lt;p>This dataset contains no identifier yet. We&amp;rsquo;ll add one:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">ID&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="nf">nrow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">penguins&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For example, assume that we knew the penguin in row 48 (which currently has a NA value) was female. The gender is in the seventh column. You indicate the position of a value in the dataframe by giving the row first and the column second.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">penguins[48&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">7&lt;/span>&lt;span class="n">]&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="s">&amp;#39;female&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If we want our dataset to only include rows in which all values are known, we remove the NA&amp;rsquo;s with this command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">penguins&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">na.omit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">penguins&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Remember that in R, it are always entire rows which get removed. It is impossible to only remove individual values because a dataframe always needs to have one entry for each column in every row.&lt;/p>
&lt;p>There are also other options such as replacing NA&amp;rsquo;s with the mean value of their column, but these are not always scientifically sound. Which strategy for missing data is appropriate depends on the dataset and your research question.&lt;/p>
&lt;p>Once we are confident the data is what it should be, we can start our regression.&lt;/p>
&lt;h2 id="running-a-regression">Running a regression&lt;/h2>
&lt;p>We&amp;rsquo;ll use the fixest package to run regressions. Many packages in R can be used for this purpose, and base R allows to run regressions, too. But the fixest package is very fast - for large datasets, that&amp;rsquo;s important.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fixest&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A regression in R is a function. It has a name and function arguments. Since we work with linear regressions, we use the feols command (ols are ordinary least squares).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">print_3&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="kr">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">3&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;I printed 3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">print_3&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">4&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first regression checks if penguins who weigh more have longer flippers:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">lm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">flipper_length_mm&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">body_mass_g&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">feols&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">flipper_length_mm&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">body_mass_g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">penguins&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>They do, the estimate is positive and significant (with a very small p-value). The effect is not very large, though.&lt;/p>
&lt;p>Regression results can be saved by assigning them to a name:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">flipper_regression&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">feols&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">flipper_length_mm&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">body_mass_g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">penguins&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This ensures that you can always access them. You can call the table of results we saw above with the summary function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">flipper_regression&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It is good practice to calculate heteroskedasticity-robust standard errors. This is done with the vcov-argument. You can do it in the regression function or in the summary function.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">flipper_regression&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;hetero&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see, the estimate remains the same. But the standard error and the metrics calculated based on it (the t-value) change.&lt;/p>
&lt;p>In the next step, write your own regression. Is the bill length positively related to the bill depth? Calculate heteroskedasticity-robust standard errors for this.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">bill_regression&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">feols&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bill_length_mm&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">bill_depth_mm&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">bill_depth_mm^2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">penguins&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;hetero&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bill_regression&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;hetero&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Surprisingly, the relation is significantly negative. Penguins, it appears, either have a short, but thick bill or a long, but thin one.&lt;/p>
&lt;h2 id="a-technical-remark-on-lists">A technical remark on lists&lt;/h2>
&lt;p>The regression is stored in R as a list. A list is a special form of datatype.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;/code>&lt;/pre>&lt;/div>&lt;p>Important to know: If you extract an element from a list with single brackets, even if it only contains a single value, it will be a smaller list. For example, the first element of a regression is the number of observations.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">bill_regression[1]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">class&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bill_regression[1]&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To get the element itself, use double brackets:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">bill_regression[[&lt;/span>&lt;span class="s">&amp;#34;nobs&amp;#34;&lt;/span>&lt;span class="n">]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">class&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bill_regression[[&lt;/span>&lt;span class="s">&amp;#34;nobs&amp;#34;&lt;/span>&lt;span class="n">]]&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="fixed-effects">Fixed effects.&lt;/h2>
&lt;p>In many datasets, such regressions as the ones above are not a good approach. That is because they are only valid when the data is a random sample of the underlying population. But what is the population we are considering here?&lt;/p>
&lt;p>The penguin data contains three species, covers three islands and was collected over 3 years. The bills may not be formed the same for all three species. To test this, we use a fixed effect.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">species_regression&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">feols&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bill_length_mm&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">bill_depth_mm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">species&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">penguins&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Again, we need to think carefully about standard errors. If the data is clustered according to specific variables (here: species), the standard errors have to be clustered as well.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">species_regression&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;hetero&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">species_regression&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;cluster&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>How does it look like when we also want to control for the sex of the penguins?&lt;/p>
&lt;p>Clustering by sex:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">species_regression&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">feols&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bill_length_mm&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">bill_depth_mm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">species&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">sex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">penguins&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">species_regression&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;hetero&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">species_regression&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;cluster&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="running-a-regression-with-factor-variables">Running a regression with factor variables&lt;/h2>
&lt;p>This form of clustering is to control for a certain variable, like species. If, instead, we wanted to know the effect such a variable has, we need to run the regression with a factor variable.&lt;/p>
&lt;p>Transforming a variable into a factor is straightforward:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">sex&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">as.factor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">sex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">penguinsex&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">sex&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">ID&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">as.factor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">ID&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">penguinID&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">ID&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that the type changed: Previously, the type of genres was &amp;lsquo;chr&amp;rsquo;. Now, it is &amp;lsquo;Factor w/ 2 levels&amp;rsquo; &amp;ldquo;female&amp;rdquo;, &amp;ldquo;male&amp;rdquo;.&lt;/p>
&lt;p>Levels are the different values a factor variable can take.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">weight_regression&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">feols&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">body_mass_g&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">sex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">penguins&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">weight_regression&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;hetero&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The variable &amp;lsquo;sexmale&amp;rsquo; is created automatically when the regression has a factor or character vector as input. The system creates a dummy, a variable &amp;lsquo;sexmale&amp;rsquo; that can take 0 or 1, depending on if the penguin is male or not. It then estimates the effect of this dummy, compared to a baseline (here, female penguins are the baseline).&lt;/p>
&lt;p>Again, we want to control for species effects:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">weight_regression&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">feols&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">body_mass_g&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">sex&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">species&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">island&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">species^island&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">penguins&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">weight_regression&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;cluster&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">weight_regression&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;twoway&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We see that although the effect is still significant at the 5%-level, the species fixed effects have a certain influence as well.&lt;/p>
&lt;p>In the next step, create your own factor variable. Answer the following question: Does the body weight of the penguins change with the years?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">year&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">as.factor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">penguins&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">year&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Do penguins weigh more or less depending on the year?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">year_regression&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">feols&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">body_mass_g&lt;/span> &lt;span class="o">~&lt;/span> &lt;span class="n">year&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">penguins&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">summary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">year_regression&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vcov&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#39;hetero&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>No. The estimates are positive, but not significant (the p-value is very large). That is also important to know: If they had lost weight in 2009 or 2008, it may mean that the colonies were endangered.&lt;/p></description></item><item><title>Experience</title><link>https://inakrapp.github.io/experience/</link><pubDate>Tue, 24 Oct 2023 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/experience/</guid><description/></item><item><title>An ARIMA model of the global average temperature</title><link>https://inakrapp.github.io/post/arima/</link><pubDate>Sat, 12 Aug 2023 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/post/arima/</guid><description>&lt;p>I wrote an ARIMA model to predict the average global temperature.&lt;/p>
&lt;p>I would not call it a climate model because it only covers one of the many aspects of the climate. Of course, it is much more limited compared to those developed by experts in the field. Still, writing it taught me a lot about forecasting of time series.&lt;/p>
&lt;p>Today, I would do some things different. In particular, I probably would discourage people who use ARIMA models from interpolating missing data points.
The ARIMA model can still be used when some data is missing. I used interpolation originally because I also experimented with ETS models, who require a time series without gaps. But the ETS model is not in the published version of the code because its predictions were not very good.&lt;/p>
&lt;p>The code (with extensive commentary) can be downloaded
. It is written in a quarto document and should run on any relatively recent version of R and Rstudio.
The &amp;lsquo;Global_Temperature.txt&amp;rsquo; and &amp;lsquo;merged_ice_core_yearly.csv&amp;rsquo; files contain the data the model uses, so they have to be downloaded into the same folder as well to run the code.
For anyone who just wants to take a look at the results and doesn‘t want to run or modify the code themselves, here is the
.&lt;/p>
&lt;p>Edit from October 19th 2023: I uploaded a version that can be used for Workshops in Germany. It is in the subfolder &amp;lsquo;Workshop&amp;rsquo;.&lt;/p></description></item><item><title>An ARIMA model of the global average temperature</title><link>https://inakrapp.github.io/software/arima/</link><pubDate>Sat, 12 Aug 2023 00:00:00 +0000</pubDate><guid>https://inakrapp.github.io/software/arima/</guid><description>&lt;p>I created an ARIMA model to predict the increase in global temperature. Although the model is very simple, it can predict some characteristics of the global temperature increase well.&lt;/p></description></item></channel></rss>