|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "id": "0", |
| 6 | + "metadata": {}, |
| 7 | + "source": [ |
| 8 | + "# Working with categorical data: land cover\n", |
| 9 | + "\n", |
| 10 | + "This example builds upon the sample data for São Miguel introduced [here](basic_usage.ipynb) to demonstrate the use of exactextract with categorical land cover data.\n", |
| 11 | + "\n", |
| 12 | + "The following plot shows a sample of the [CORINE 2018 landcover dataset](https://land.copernicus.eu/en/products/corine-land-cover) that is distributed with exactextractr." |
| 13 | + ] |
| 14 | + }, |
| 15 | + { |
| 16 | + "cell_type": "code", |
| 17 | + "execution_count": null, |
| 18 | + "id": "1", |
| 19 | + "metadata": {}, |
| 20 | + "outputs": [], |
| 21 | + "source": [ |
| 22 | + "import geopandas as gpd\n", |
| 23 | + "import rasterio\n", |
| 24 | + "import rasterio.plot\n", |
| 25 | + "from matplotlib import pyplot\n", |
| 26 | + "\n", |
| 27 | + "landcov = 'sao_miguel/clc2018_v2020_20u1.tif'\n", |
| 28 | + "concelhos = 'sao_miguel/concelhos.gpkg'\n", |
| 29 | + "\n", |
| 30 | + "fig, ax = pyplot.subplots()\n", |
| 31 | + "\n", |
| 32 | + "with rasterio.open(landcov) as r:\n", |
| 33 | + " rasterio.plot.show(r, ax=ax, cmap='tab20c')\n", |
| 34 | + " \n", |
| 35 | + "admin = gpd.read_file(concelhos)\n", |
| 36 | + "admin.plot(ax=ax, facecolor='none', edgecolor='black')" |
| 37 | + ] |
| 38 | + }, |
| 39 | + { |
| 40 | + "cell_type": "markdown", |
| 41 | + "id": "2", |
| 42 | + "metadata": {}, |
| 43 | + "source": [ |
| 44 | + "## Summarizing land cover classifications\n", |
| 45 | + "\n", |
| 46 | + "One of the most basic questions we might ask is which land cover type is predominant in each _concelho_. We can do this with the built-in `mode` summary operation. The `minority` and `variety` operations are also applicable to categorical data and provide the least-common classification and number of distinct classifications, respectively." |
| 47 | + ] |
| 48 | + }, |
| 49 | + { |
| 50 | + "cell_type": "code", |
| 51 | + "execution_count": null, |
| 52 | + "id": "3", |
| 53 | + "metadata": {}, |
| 54 | + "outputs": [], |
| 55 | + "source": [ |
| 56 | + "from exactextract import exact_extract\n", |
| 57 | + "\n", |
| 58 | + "df = exact_extract(landcov, concelhos, [\"variety\", \"mode\", \"minority\"], include_cols='name', output='pandas')\n", |
| 59 | + "display(df)" |
| 60 | + ] |
| 61 | + }, |
| 62 | + { |
| 63 | + "cell_type": "markdown", |
| 64 | + "id": "4", |
| 65 | + "metadata": {}, |
| 66 | + "source": [ |
| 67 | + "If needed, we can load the attribute table distributed with the landcover dataset and replace the numeric codes above with their descriptions." |
| 68 | + ] |
| 69 | + }, |
| 70 | + { |
| 71 | + "cell_type": "code", |
| 72 | + "execution_count": null, |
| 73 | + "id": "5", |
| 74 | + "metadata": {}, |
| 75 | + "outputs": [], |
| 76 | + "source": [ |
| 77 | + "import geopandas as pd\n", |
| 78 | + "\n", |
| 79 | + "classes = gpd.read_file('sao_miguel/clc2018_v2020_20u1.tif.vat.dbf').set_index('Value')\n", |
| 80 | + "for col in ('mode', 'minority'):\n", |
| 81 | + " df[col] = df[col].map(classes['LABEL3'])\n", |
| 82 | + "display(df)" |
| 83 | + ] |
| 84 | + }, |
| 85 | + { |
| 86 | + "cell_type": "markdown", |
| 87 | + "id": "6", |
| 88 | + "metadata": {}, |
| 89 | + "source": [ |
| 90 | + "## Calculating the fraction of each land cover type\n", |
| 91 | + "\n", |
| 92 | + "If we want more detailed information, the `unique` operation provides an array of distinct landcover types within each polygon.\n", |
| 93 | + "The `frac` operation provides a matching array with fraction of the polygon's Cartesian area that is covered by each type." |
| 94 | + ] |
| 95 | + }, |
| 96 | + { |
| 97 | + "cell_type": "code", |
| 98 | + "execution_count": null, |
| 99 | + "id": "7", |
| 100 | + "metadata": {}, |
| 101 | + "outputs": [], |
| 102 | + "source": [ |
| 103 | + "df = exact_extract(landcov, concelhos, [\"unique\", \"frac\"], include_cols='name', output='pandas')\n", |
| 104 | + "display(df)" |
| 105 | + ] |
| 106 | + }, |
| 107 | + { |
| 108 | + "cell_type": "markdown", |
| 109 | + "id": "8", |
| 110 | + "metadata": {}, |
| 111 | + "source": [ |
| 112 | + "To join these codes with the description, we can unnest the data using the pandas ``explode`` function and then remap the fields as done previously." |
| 113 | + ] |
| 114 | + }, |
| 115 | + { |
| 116 | + "cell_type": "code", |
| 117 | + "execution_count": null, |
| 118 | + "id": "9", |
| 119 | + "metadata": {}, |
| 120 | + "outputs": [], |
| 121 | + "source": [ |
| 122 | + "df = df.explode(['unique', 'frac'])\n", |
| 123 | + "df['unique'] = df['unique'].map(classes['LABEL3'])\n", |
| 124 | + "display(df)" |
| 125 | + ] |
| 126 | + }, |
| 127 | + { |
| 128 | + "cell_type": "markdown", |
| 129 | + "id": "10", |
| 130 | + "metadata": {}, |
| 131 | + "source": [ |
| 132 | + "## Condensed output with ``frac_as_map``\n", |
| 133 | + "\n", |
| 134 | + "If we are working with JSON output, it is also possible to view the same information in a map format:" |
| 135 | + ] |
| 136 | + }, |
| 137 | + { |
| 138 | + "cell_type": "code", |
| 139 | + "execution_count": null, |
| 140 | + "id": "11", |
| 141 | + "metadata": {}, |
| 142 | + "outputs": [], |
| 143 | + "source": [ |
| 144 | + "exact_extract(landcov, concelhos, 'frac', include_cols='name', output='geojson', output_options={'frac_as_map': True})[:2]" |
| 145 | + ] |
| 146 | + }, |
| 147 | + { |
| 148 | + "cell_type": "markdown", |
| 149 | + "id": "12", |
| 150 | + "metadata": {}, |
| 151 | + "source": [ |
| 152 | + "## Summarizing population land cover\n", |
| 153 | + "\n", |
| 154 | + "One extension of the analysis above is to see which land covers are associated with human population in a given concelho. Is the population primary urban or rural?\n", |
| 155 | + "\n", |
| 156 | + "As described in the [basic usage example](basic_usage.ipynb), the population density raster provides the most robust results in the presence of partially-covered pixels.\n", |
| 157 | + "\n", |
| 158 | + "We are able to perform this analysis because the CORINE sample distributed with exactextractr has been reprojected from its native Lambert Equal Area projection into geographic coordinates consistent with GPW. Otherwise, working with multiple rasters in different projections requires transformation to a common grid using tools such as GDAL.\n", |
| 159 | + "\n", |
| 160 | + "Very little about the call to `exact_extract` requires changing to incorporate population. We swap `weighted_frac` for `frac` and set `weights = pop_density`:" |
| 161 | + ] |
| 162 | + }, |
| 163 | + { |
| 164 | + "cell_type": "code", |
| 165 | + "execution_count": null, |
| 166 | + "id": "13", |
| 167 | + "metadata": {}, |
| 168 | + "outputs": [], |
| 169 | + "source": [ |
| 170 | + "pop_density = 'sao_miguel/gpw_v411_2020_density_2020.tif'\n", |
| 171 | + "\n", |
| 172 | + "df = exact_extract(landcov, concelhos, [\"unique\", \"weighted_frac\"], weights=pop_density, include_cols='name', output='pandas')\n", |
| 173 | + "df = df.explode(['unique', 'weighted_frac']).astype({'weighted_frac':'float64'})\n", |
| 174 | + "df['unique'] = df['unique'].map(classes['LABEL3'])\n", |
| 175 | + "df.sort_values('weighted_frac', ascending=False).drop_duplicates('name')" |
| 176 | + ] |
| 177 | + }, |
| 178 | + { |
| 179 | + "cell_type": "markdown", |
| 180 | + "id": "14", |
| 181 | + "metadata": {}, |
| 182 | + "source": [ |
| 183 | + "Looking at the highest-population land cover type in each concelho, we can can see that the western/central concelhos of Lagoa, Ponta Delgada, Ribeira Grande, and Vila Franca do Campo have a more urban population than Nordeste or Povoação to the east." |
| 184 | + ] |
| 185 | + } |
| 186 | + ], |
| 187 | + "metadata": { |
| 188 | + "kernelspec": { |
| 189 | + "display_name": "Python 3 (ipykernel)", |
| 190 | + "language": "python", |
| 191 | + "name": "python3" |
| 192 | + }, |
| 193 | + "language_info": { |
| 194 | + "codemirror_mode": { |
| 195 | + "name": "ipython", |
| 196 | + "version": 3 |
| 197 | + }, |
| 198 | + "file_extension": ".py", |
| 199 | + "mimetype": "text/x-python", |
| 200 | + "name": "python", |
| 201 | + "nbconvert_exporter": "python", |
| 202 | + "pygments_lexer": "ipython3", |
| 203 | + "version": "3.11.2" |
| 204 | + } |
| 205 | + }, |
| 206 | + "nbformat": 4, |
| 207 | + "nbformat_minor": 5 |
| 208 | +} |
0 commit comments