From 92bd9186430bafd21b5027107f8356cdf994bf3d Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Tue, 9 Jul 2024 17:26:30 +0200 Subject: [PATCH] DOC: add migration guide to new API (#637) * DOC: add migration guide to new API * cleanup * Apply suggestions from code review Co-authored-by: James Gaboardi * lint --------- Co-authored-by: James Gaboardi --- docs/user_guide/intro.rst | 1 + docs/user_guide/migration.ipynb | 310 ++++++++++++++++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 docs/user_guide/migration.ipynb diff --git a/docs/user_guide/intro.rst b/docs/user_guide/intro.rst index a33d1e87..ada362e6 100644 --- a/docs/user_guide/intro.rst +++ b/docs/user_guide/intro.rst @@ -26,3 +26,4 @@ Notebooks cover just a small selection of functions as an illustration of princi Using spatial weights matrix Street network analysis Data preprocessing + migration diff --git a/docs/user_guide/migration.ipynb b/docs/user_guide/migration.ipynb new file mode 100644 index 00000000..6a1a65b8 --- /dev/null +++ b/docs/user_guide/migration.ipynb @@ -0,0 +1,310 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Migration to momepy 1.0 API\n", + "\n", + "Starting with version 0.8, momepy contains a completely new API (and refactored internals) that will become the only API in the momepy 1.0. Given this is a complete reimplementation of nearly the entire package, it is not compatible with the legacy class-based API. This notebook contains a succinct migration guide, highlighting the key differences between the two APIs and outlines required changes to existing code to make it compatible with the upcoming 1.0 release." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import geopandas as gpd\n", + "from libpysal import graph, weights\n", + "\n", + "import momepy\n", + "\n", + "buildings = gpd.read_file(\n", + " momepy.datasets.get_path(\"bubenec\"), layer=\"buildings\"\n", + ")\n", + "tessellation = gpd.read_file(\n", + " momepy.datasets.get_path(\"bubenec\"), layer=\"tessellation\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Functions over classes\n", + "\n", + "The first key difference you may notice is the vast majority of functionality is now offered as functions, rather than classes. Take equivalent rectangular index as an example and its assignment as a new column.\n", + "\n", + "The new API is simple:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/martin/miniforge3/envs/momepy/lib/python3.11/site-packages/pandas/core/arraylike.py:492: RuntimeWarning: invalid value encountered in oriented_envelope\n", + " return getattr(ufunc, method)(*new_inputs, **kwargs)\n" + ] + } + ], + "source": [ + "buildings[\"eri\"] = momepy.equivalent_rectangular_index(buildings)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The old API, which further required extracting the series from the class:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/2f/fhks6w_d0k556plcv3rfmshw0000gn/T/ipykernel_47459/3862675108.py:1: FutureWarning: Class based API like `momepy.EquivalentRectangularIndex` is deprecated. Replace it with `momepy.equivalent_rectangular_index` to use functional API instead or pin momepy version <1.0. Class-based API will be removed in 1.0. \n", + " buildings[\"eri\"] = momepy.EquivalentRectangularIndex(buildings).series\n", + "/Users/martin/miniforge3/envs/momepy/lib/python3.11/site-packages/pandas/core/arraylike.py:492: RuntimeWarning: invalid value encountered in oriented_envelope\n", + " return getattr(ufunc, method)(*new_inputs, **kwargs)\n" + ] + } + ], + "source": [ + "buildings[\"eri\"] = momepy.EquivalentRectangularIndex(buildings).series" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When running the code, you will see a warning about the deprecation.\n", + "\n", + "```\n", + "FutureWarning: Class based API like `momepy.EquivalentRectangularIndex` is deprecated. Replace it with `momepy.equivalent_rectangular_index` to use functional API instead or pin momepy version <1.0. Class-based API will be removed in 1.0.\n", + "```\n", + "\n", + "If there is a direct equivalent, it also tells you its name. In some cases, there is no equivalent in `momepy` but one elsewhere. \n", + "\n", + "Measuring area with the new API will require using `geopandas` directly." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "buildings[\"area\"] = buildings.area" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While the legacy API offered a wrapper." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/2f/fhks6w_d0k556plcv3rfmshw0000gn/T/ipykernel_47459/1190336779.py:1: FutureWarning: `momepy.Area` is deprecated. Replace it with `.area` attribute of a GeoDataFrame or pin momepy version <1.0. This class will be removed in 1.0. \n", + " buildings['area'] = momepy.Area(buildings).series\n" + ] + } + ], + "source": [ + "buildings[\"area\"] = momepy.Area(buildings).series" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The warning is a bit different but still provides guidance.\n", + "\n", + "```\n", + "FutureWarning: `momepy.Area` is deprecated. Replace it with `.area` attribute of a GeoDataFrame or pin momepy version <1.0. This class will be removed in 1.0.\n", + "```\n", + "\n", + "## Dependency on libpysal `Graph` over `W`\n", + "\n", + "Spatial relationships in the legacy API are represented using `libpysal.weights.W` objects. In the new one, `momepy` depends on the new `libpysal.graph.Graph` implementation. That has two consequences - different ways of building the object and reliance on `GeoDataFrame` indices.\n", + "\n", + "`Graph` encodes geometries using the index they have in the `GeoDataFrame`. It does not use positional indexing nor custom column. The two objects are tied together via the index. For momepy, this means that indices plays a central role in the implementation and there's no `\"unique_id\"` column any longer, which is superseded by index.\n", + "\n", + "Example of computing a number of neighbors relative to the perimeter of each geometry using the new API:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# build contiguity of order 2\n", + "contiguity_k2 = graph.Graph.build_contiguity(tessellation).higher_order(\n", + " 2, lower_order=True\n", + ")\n", + "\n", + "# measure neighbors\n", + "tessellation[\"neighbours_weighted\"] = momepy.neighbors(\n", + " tessellation, contiguity_k2, weighted=True\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And using the old API:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/2f/fhks6w_d0k556plcv3rfmshw0000gn/T/ipykernel_47459/1307576710.py:1: FutureWarning: `momepy.sw_high` is deprecated. Replace it with .higher_order() method of libpysal.graph.Graph or pin momepy version <1.0. This class will be removed in 1.0. \n", + " contiguity_k2 = momepy.sw_high(k=2, gdf=tessellation, ids='uID')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/2f/fhks6w_d0k556plcv3rfmshw0000gn/T/ipykernel_47459/1307576710.py:3: FutureWarning: Class based API like `momepy.Neighbors` is deprecated. Replace it with `momepy.neighbors` to use functional API instead or pin momepy version <1.0. Class-based API will be removed in 1.0. \n", + " tessellation['neighbours'] = momepy.Neighbors(tessellation, contiguity_k2,'uID', weighted=True).series\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "44f9ec7d1da54ae794472e6669c9c458", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/144 [00:00