Considering equity in our solutions

from lokigi.site import SiteProblem
import pandas as pd
problem = SiteProblem()
problem.add_demand("../../../sample_data/brighton_demand.csv", demand_col="demand", location_id_col="LSOA")
problem.add_sites("../../../sample_data/brighton_sites_existing.geojson", candidate_id_col="site")
problem.add_travel_matrix(
    travel_matrix_df="../../../sample_data/brighton_travel_matrix_driving.csv",
    source_col="LSOA",
    from_unit="seconds",
    to_unit="minutes"
    )
problem.add_region_geometry_layer(
    "https://github.com/hsma-programme/h6_3d_facility_location_problems/raw/refs/heads/main/h6_3d_facility_location_problems/example_code/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4.geojson",
    common_col="LSOA11NM"
    )

This time, we will add a new set of equity data.

Before we add it to our problem, let’s load in our equity dataset normally and have a look at it.

This dataset has come from the UK government open geography portal and is licenced under the open government licence. You can take a look at it here.

equity_df = pd.read_csv("../../../sample_data/Index_of_Multiple_Deprivation_(Dec_2015)_Lookup_in_England.csv")

equity_df.head()
ObjectId LSOA11CD LSOA11NM IMD15
0 1 E01021988 Tendring 018A 1
1 2 E01012673 Blackpool 010A 2
2 3 E01012681 Blackpool 006A 3
3 4 E01024657 Thanet 001A 4
4 5 E01012751 Blackpool 013D 5
equity_df.tail()
ObjectId LSOA11CD LSOA11NM IMD15
32839 32840 E01028400 Rushcliffe 008D 32840
32840 32841 E01022863 Hart 007B 32841
32841 32842 E01016695 Wokingham 002F 32842
32842 32843 E01016676 Wokingham 001B 32843
32843 32844 E01016709 Wokingham 020E 32844

It looks like the IMD15 ranks every single LSOA. That’s fine - we can ask lokigi to turn it into deciles for us!

We know that in this dataset, 1 = most deprived and 32844 = least deprived, so we set reverse=False (which is the default).

problem.add_equity_data(
    "../../../sample_data/Index_of_Multiple_Deprivation_(Dec_2015)_Lookup_in_England.csv",
    equity_col="IMD15",
    common_col="LSOA11NM",
    label="Indices of Multiple Deprivation",
    continuous_measure=True,
    reverse=False
    )

Let’s see what our dataset looks like after loading it in.

problem.show_equity_data()
LSOA11NM IMD15 IMD15_raw
0 Tendring 018A 1 1
1 Blackpool 010A 1 2
2 Blackpool 006A 1 3
3 Thanet 001A 1 4
4 Blackpool 013D 1 5
... ... ... ...
32839 Rushcliffe 008D 10 32840
32840 Hart 007B 10 32841
32841 Wokingham 002F 10 32842
32842 Wokingham 001B 10 32843
32843 Wokingham 020E 10 32844

32844 rows × 3 columns

We can see we have the original column, but also a new column that has classified this data into deciles.

Let’s now plot this using our .plot_region_geometry_layer() method. We’ll set plot_equity to True.

problem.plot_region_geometry_layer(plot_equity=True, linewidth=0.03)

However, this is not that useful to us as it’s very zoomed out and showing the whole dataset (as our region geometry layer is for the whole of England too).

However, we have passed in demand data for only our region of interest, so if we set ‘plot_region_of_interest_only’ to True, it will limit the output plot to that region.

problem.plot_region_geometry_layer(
    plot_equity=True,
    plot_region_of_interest_only=True
    )

We can change the colourmap to something else.

problem.plot_region_geometry_layer(
    plot_equity=True,
    plot_region_of_interest_only=True,
    cmap="magma"
    )

We can also opt to plot it in an interactive format instead.

problem.plot_region_geometry_layer(
    plot_equity=True,
    plot_region_of_interest_only=True,
    interactive=True
    )
Make this Notebook Trusted to load map: File -> Trust Notebook

Let’s now solve the problem.

solutions = problem.solve(p=4, objectives="p_median", threshold_for_coverage=10)
solutions.show_solutions()
site_names site_indices coverage_threshold weighted_average unweighted_average 90th_percentile max proportion_within_coverage_threshold problem_df
0 None [0, 2, 3, 4] 10 5.08 5.12 7.82 16.69 0.96 LSOA                  L...
1 None [2, 3, 4, 5] 10 5.11 5.05 7.42 16.69 0.96 LSOA                  L...
2 None [1, 2, 3, 5] 10 5.20 5.07 7.75 16.69 0.95 LSOA                  L...
3 None [0, 1, 2, 3] 10 5.22 5.21 8.34 16.69 0.95 LSOA                  L...
4 None [0, 2, 3, 5] 10 5.23 5.19 8.06 16.69 0.95 LSOA                  L...
5 None [1, 2, 3, 4] 10 5.28 5.33 8.32 16.69 0.95 LSOA                  L...
6 None [0, 2, 4, 5] 10 6.05 5.90 9.07 16.69 0.95 LSOA                  L...
7 None [0, 1, 2, 5] 10 6.14 5.92 9.33 16.69 0.94 LSOA                  L...
8 None [0, 1, 2, 4] 10 6.24 6.17 9.69 16.69 0.92 LSOA                  L...
9 None [1, 2, 4, 5] 10 6.33 6.17 9.26 16.69 0.93 LSOA                  L...
10 None [0, 1, 3, 4] 10 6.63 6.43 11.32 21.71 0.84 LSOA                  L...
11 None [1, 3, 4, 5] 10 6.66 6.37 11.32 21.71 0.84 LSOA                  L...
12 None [0, 1, 3, 5] 10 6.83 6.48 11.58 22.86 0.81 LSOA                  L...
13 None [0, 3, 4, 5] 10 6.91 6.56 12.26 21.71 0.80 LSOA                  L...
14 None [0, 1, 4, 5] 10 7.67 7.27 11.67 21.71 0.82 LSOA                  L...
example_problem_df = solutions.show_solutions()["problem_df"].iloc[0]
example_problem_df
LSOA LSOA_x Site 1 Site 3 Site 4 Site 5 min_cost selected_site within_threshold LSOA_y demand LSOA11NM IMD15 IMD15_raw
0 Brighton and Hove 027E Brighton and Hove 027E 12.898833 7.404833 8.197500 10.125667 7.404833 Site 3 True Brighton and Hove 027E 3627 Brighton and Hove 027E 2 5040
1 Brighton and Hove 027F Brighton and Hove 027F 12.623167 8.626167 9.351167 9.649500 8.626167 Site 3 True Brighton and Hove 027F 2323 Brighton and Hove 027F 3 9104
2 Brighton and Hove 027A Brighton and Hove 027A 12.720667 8.633000 6.840000 11.353833 6.840000 Site 4 True Brighton and Hove 027A 2596 Brighton and Hove 027A 3 7483
3 Brighton and Hove 029E Brighton and Hove 029E 12.393667 11.006000 6.328667 12.193000 6.328667 Site 4 True Brighton and Hove 029E 3132 Brighton and Hove 029E 2 6218
4 Brighton and Hove 029D Brighton and Hove 029D 11.097500 10.970000 5.216667 12.408333 5.216667 Site 4 True Brighton and Hove 029D 2883 Brighton and Hove 029D 3 7018
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
160 Brighton and Hove 012A Brighton and Hove 012A 7.442333 18.468500 8.652667 10.433667 7.442333 Site 1 True Brighton and Hove 012A 2497 Brighton and Hove 012A 3 8891
161 Brighton and Hove 005C Brighton and Hove 005C 7.830000 16.804000 9.490000 8.769167 7.830000 Site 1 True Brighton and Hove 005C 2570 Brighton and Hove 005C 2 5951
162 Brighton and Hove 012B Brighton and Hove 012B 7.742167 18.876667 8.952500 10.841833 7.742167 Site 1 True Brighton and Hove 012B 2051 Brighton and Hove 012B 3 8814
163 Brighton and Hove 005A Brighton and Hove 005A 9.458167 18.432167 11.068500 10.397333 9.458167 Site 1 True Brighton and Hove 005A 1164 Brighton and Hove 005A 7 21094
164 Brighton and Hove 005B Brighton and Hove 005B 8.258333 17.232333 9.918333 9.197500 8.258333 Site 1 True Brighton and Hove 005B 1097 Brighton and Hove 005B 7 20761

165 rows × 14 columns

example_problem_df.groupby("IMD15")['min_cost'].agg("mean").round(2).reset_index()
IMD15 min_cost
0 1 4.74
1 2 6.23
2 3 5.50
3 4 4.14
4 5 3.61
5 6 4.47
6 7 5.63
7 8 6.30
8 9 5.49
9 10 4.90
solutions.check_solution_equity()
IMD15 min_cost
0 1 4.74
1 2 6.23
2 3 5.50
3 4 4.14
4 5 3.61
5 6 4.47
6 7 5.63
7 8 6.30
8 9 5.49
9 10 4.90
solutions.check_solution_equity(return_plot=True)
solutions.check_solution_equity(return_plot=True, interactive=False);

solutions.check_solution_equity(return_plot=True, interactive=False, colour_mode="gradient");

solutions.check_solution_equity(return_plot=True, interactive=False, colour_mode="above_below_avg");

solutions.plot_top_n_solution_equity(cols=5, n=10);

solutions.plot_top_n_solution_equity(cols=5, n=10, colour_mode="above_below_avg");

We can see for example that solution 2 is slightly worse for the most deprived group (IMD 1) than solution 1.

example_problem_df
LSOA LSOA_x Site 1 Site 3 Site 4 Site 5 min_cost selected_site within_threshold LSOA_y demand LSOA11NM IMD15 IMD15_raw
0 Brighton and Hove 027E Brighton and Hove 027E 12.898833 7.404833 8.197500 10.125667 7.404833 Site 3 True Brighton and Hove 027E 3627 Brighton and Hove 027E 2 5040
1 Brighton and Hove 027F Brighton and Hove 027F 12.623167 8.626167 9.351167 9.649500 8.626167 Site 3 True Brighton and Hove 027F 2323 Brighton and Hove 027F 3 9104
2 Brighton and Hove 027A Brighton and Hove 027A 12.720667 8.633000 6.840000 11.353833 6.840000 Site 4 True Brighton and Hove 027A 2596 Brighton and Hove 027A 3 7483
3 Brighton and Hove 029E Brighton and Hove 029E 12.393667 11.006000 6.328667 12.193000 6.328667 Site 4 True Brighton and Hove 029E 3132 Brighton and Hove 029E 2 6218
4 Brighton and Hove 029D Brighton and Hove 029D 11.097500 10.970000 5.216667 12.408333 5.216667 Site 4 True Brighton and Hove 029D 2883 Brighton and Hove 029D 3 7018
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
160 Brighton and Hove 012A Brighton and Hove 012A 7.442333 18.468500 8.652667 10.433667 7.442333 Site 1 True Brighton and Hove 012A 2497 Brighton and Hove 012A 3 8891
161 Brighton and Hove 005C Brighton and Hove 005C 7.830000 16.804000 9.490000 8.769167 7.830000 Site 1 True Brighton and Hove 005C 2570 Brighton and Hove 005C 2 5951
162 Brighton and Hove 012B Brighton and Hove 012B 7.742167 18.876667 8.952500 10.841833 7.742167 Site 1 True Brighton and Hove 012B 2051 Brighton and Hove 012B 3 8814
163 Brighton and Hove 005A Brighton and Hove 005A 9.458167 18.432167 11.068500 10.397333 9.458167 Site 1 True Brighton and Hove 005A 1164 Brighton and Hove 005A 7 21094
164 Brighton and Hove 005B Brighton and Hove 005B 8.258333 17.232333 9.918333 9.197500 8.258333 Site 1 True Brighton and Hove 005B 1097 Brighton and Hove 005B 7 20761

165 rows × 14 columns

solutions.plot_combination_by_equity();

Back to top