Multiscalar Segregation Profiles

%load_ext watermark
%watermark -a 'eli knaap' -v -d -u -p segregation,geopandas,libpysal
Author: eli knaap

Last updated: 2026-09-04

Python implementation: CPython
Python version       : 3.14.7
IPython version      : 9.17.1

segregation: 2.5.6.dev83+g2e095ced1
geopandas  : 1.1.4
libpysal   : 4.15.0

For measuring spatial segregation dynamics, the segregation package provides a function for measuring multiscalar segregation profiles, as introduced by Reardon et al. The multiscalar profile is a tool for measuring spatial segregation dynamics–the way that a segregation index changes values as the concept of a neighborhood changes, and what that tells us about macro versus micro patterns of segregation.

The basic idea is to calculate a segregation statistic, then expand the spatial scope of a neighborhood, recalculate the statistic, and repeat. A multiscalar profile can be computed for any generalized spatial segregation index, which in the case of the segregation package, means a total of 23 indices, including single and multigroup varieties

from segregation.batch import implicit_multi_indices, implicit_single_indices
len(implicit_single_indices) + len(implicit_multi_indices)
24

Computing a Single Group Profile

import geopandas as gpd
import matplotlib.pyplot as plt

from libpysal.examples import load_example
from segregation.singlegroup import Dissim, Gini
from segregation.dynamics import compute_multiscalar_profile
sacramento = gpd.read_file(load_example("Sacramento1").get_path("sacramentot2.shp"))
sacramento = sacramento.to_crs(sacramento.estimate_utm_crs())
sac_gini_profile =  compute_multiscalar_profile(sacramento, segregation_index=Gini, 
                                                group_pop_var="BLACK", total_pop_var="TOT_POP", 
                                                distances= range(500,5500,500))

The function returns a pandas Series whose index is the neighborhood distance threshold, and the value is the segregation statistic.

sac_gini_profile
distance
0       0.636176
500     0.636060
1000    0.623794
1500    0.585519
2000    0.536802
2500    0.499355
3000    0.472811
3500    0.452414
4000    0.436664
4500    0.424351
5000    0.412981
Name: Gini, dtype: float64

As such, the profile is easy to plot:

sac_gini_profile.plot()
<Axes: xlabel='distance'>
../_images/5c63d604c6a57d4039a0279a8b4f959690f17b60d0abe7d78b2f318d014a2345.png

A good way to compare multiscalar profiles is to plot them in the same figure. For example to compare profiles for gini and dissimilarity indices:

sac_dissim_profile = compute_multiscalar_profile(sacramento, segregation_index=Dissim,
                                                group_pop_var="BLACK", total_pop_var="TOT_POP", 
                                                distances= range(500,5500,500))
from libpysal.weights import DistanceBand
fig, ax = plt.subplots(figsize=(8,8))

sac_dissim_profile.plot(ax=ax)
sac_gini_profile.plot(ax=ax)
ax.legend()
<matplotlib.legend.Legend at 0x7f6bcfd95fd0>
../_images/9de34675b717c784897c601d4b716fc7a58b0845e7cbbad8be22d5b698aecbdb.png

The multiscalar profiles for Gini and Dissimilarity indices are very similar, but have slightly different shapes.

Network versus Euclidian Multiscalar Profiles

To calculate a multiscalar profile using travel network distance instead of Euclidian distance, simply pass a pandana.Network object to the function

Note: the network-distance workflow below relies on the optional pandarm package and a pre-built network file, so those cells are tagged skip-execution and are not run during automated testing.

import pandana as pdna
net = pdna.Network.from_hdf5("../40900.h5")
net_dissim_profile = compute_multiscalar_profile(sacramento, segregation_index=Dissim,
                                                group_pop_var="BLACK", total_pop_var="TOT_POP", 
                                                network = net,
                                                distances= range(500,5500,500))
fig, ax = plt.subplots(figsize=(8,8))

sac_dissim_profile.name='Dissim'
net_dissim_profile.name='Network Dissim'
sac_dissim_profile.plot(ax=ax)
net_dissim_profile.plot(ax=ax)
ax.legend()
<matplotlib.legend.Legend at 0x1b28f0d60>
../_images/55557076f556aae83b88f016e1fb89c464b9925627cfa073050c6fb1a30e33c6.png

In this case, comparing the two profiles reveals the role of travel infrastructure on the experience and measurement of segregation; the network-based dissimilarity profile falls slower, indicating that travel networks help insulate segregation at larger distances

Computing a Multi Group Profile

To calculate a multigroup index (e.g. the multigroup information theory index from the original paper), simply pass a MultiGroupIndex class to the function with multigroup arguments instead of singlegroup

from segregation.multigroup import MultiInfoTheory, MultiGini
multi_info_profile = compute_multiscalar_profile(sacramento, segregation_index=MultiInfoTheory, 
                                          groups=["HISP", 'BLACK', "WHITE"], distances=range(500,5000,500))

multi_gini_profile = compute_multiscalar_profile(sacramento, segregation_index=MultiGini, 
                                          groups=["HISP", 'BLACK', "WHITE"], distances=range(500,5000,500))
fig, ax = plt.subplots(figsize=(8,8))

multi_gini_profile.plot(ax=ax)
multi_info_profile.plot(ax=ax)
ax.legend()
<matplotlib.legend.Legend at 0x7f6bcfd9fb60>
../_images/703cea004389e9a0ab0a1edc6e8eaf2e0455177baaeaff315f7868c897cdd070.png

Batch-Computing Profiles

from segregation.batch import batch_multiscalar_singlegroup, batch_multiscalar_multigroup
single_profs =  batch_multiscalar_singlegroup(sacramento,group_pop_var="BLACK", total_pop_var="TOT_POP", 
                                                distances= range(500,5500,500))
multi_profs = batch_multiscalar_multigroup(sacramento, distances=range(500,5000,500), groups=["HISP", 'BLACK', "WHITE"])

With several profiles to examine at once, it’s helpful to use an interactive plotting library like hvplot

Note: hvplot is an optional dependency used only for interactive plotting; the cells below are tagged skip-execution and are not run during automated testing.

import hvplot.pandas
single_profs.hvplot(width=850, height=450)
multi_profs.hvplot(width=850, height=550)