'Creating custom colourmap for geopandas.explore plot
all code:
def rgb2hex(r,g,b):
return '#{:02x}{:02x}{:02x}'.format(r,g,b)
def rg(num):
num = int(np.round((num / 100) * 124))
r = (124 - num)
g = (124 + num)
b = (0)
x = rgb2hex(r,g,b)
return x
def colourmap(value):
if math.isnan(value) == False:
y = rg(value)
else:
y = '#808080'
return y
m = homes.explore(
column="Percentage",
cmap=lambda value: colourmap(value),#map to custom colour scheme
marker_kwds=dict(radius=15, fill=True), # make marker radius 15px with fill
tooltip=labels, # show labels in the tooltip
legend=False,
name="Homes" # name of the layer in the map
)
m #plot
Hi, I'm relatively new to stackoverflow and using geopandas so any comments are appreciated.
I'm trying to create a custom colour scheme that handles NaN values.
Green being high percentages, yellow being low but gray meaning NaN.
However I get this error:
ValueError Traceback (most recent call last)
_______________________________________.ipynb Cell 33' in <module>
3 labels = ["Worker Type","Postcode","Name","Percentage"]
---> 16 m = homes.explore(
17 column="Percentage",
18 cmap=lambda value: colourmap(value)
19 marker_kwds=dict(radius=15, fill=True)
20 tooltip=labels
22 legend=False, # do not show column label in the tooltip
23 name="Homes" # name of the layer in the map
File ____________________\ref_env\lib\site-packages\geopandas\geodataframe.py:1858, in GeoDataFrame.explore(self, *args, **kwargs)
1855 @doc(_explore)
1856 def explore(self, *args, **kwargs):
1857 """Interactive map based on folium/leaflet.js"""
-> 1858 return _explore(self, *args, **kwargs)
File ________________\ref_env\lib\site-packages\geopandas\explore.py:457, in _explore(df, column, cmap, color, m, tiles, attr, tooltip, popup, highlight, categorical, legend, scheme, k, vmin, vmax, width, height, categories, classification_kwds, control_scale, marker_type, marker_kwds, style_kwds, highlight_kwds, missing_kwds, tooltip_kwds, popup_kwds, legend_kwds, **kwargs)
454 nan_color = missing_kwds.pop("color", None)
456 gdf["__folium_color"] = nan_color
--> 457 gdf.loc[~nan_idx, "__folium_color"] = color
458 else:
459 gdf["__folium_color"] = color
File _______________\ref_env\lib\site-packages\pandas\core\indexing.py:723, in _LocationIndexer.__setitem__(self, key, value)
720 self._has_valid_setitem_indexer(key)
722 iloc = self if self.name == "iloc" else self.obj.iloc
--> 723 iloc._setitem_with_indexer(indexer, value, self.name)
File _________________\ref_env\lib\site-packages\pandas\core\indexing.py:1730, in _iLocIndexer._setitem_with_indexer(self, indexer, value, name)
1727 # align and set the values
1728 if take_split_path:
1729 # We have to operate column-wise
-> 1730 self._setitem_with_indexer_split_path(indexer, value, name)
1731 else:
1732 self._setitem_single_block(indexer, value, name)
File ______________\ref_env\lib\site-packages\pandas\core\indexing.py:1785, in _iLocIndexer._setitem_with_indexer_split_path(self, indexer, value, name)
1780 if len(value) == 1 and not is_integer(info_axis):
1781 # This is a case like df.iloc[:3, [1]] = [0]
1782 # where we treat as df.iloc[:3, 1] = 0
1783 return self._setitem_with_indexer((pi, info_axis[0]), value[0])
-> 1785 raise ValueError(
1786 "Must have equal len keys and value "
1787 "when setting with an iterable"
1788 )
1790 elif lplane_indexer == 0 and len(value) == len(self.obj.index):
1791 # We get here in one case via .loc with a all-False mask
1792 pass
ValueError: Must have equal len keys and value when setting with an iterable
Can someone describe to me this error and direct me on where to look at next?
EDIT: I should have included that I am using Point Geometry
Solution 1:[1]
- have used standard geometry to simulate data for this question
- what I have found appears to be a bug when cmap is a callable and their are NaN values in column
- have worked around by
fillna(-99)
- adapted your
colormap()
function to treat -99 same as NaN
- have extended answer based on comment around JSON serialisation. Created a column that is functions. Test columns are JSON serialisable and then exclude columns that are not
I have contributed enhancements to
explore()
in geopandas. I'm working through permutations of parameters that leads to this issue / bug. Will raise an issue against Geopandas and possibly commit a PR. For meantime I recommend using work around noted above.
Update have created an issue https://github.com/geopandas/geopandas/issues/2408
import geopandas as gpd
import numpy as np
import math
import json
def rgb2hex(r, g, b):
return "#{:02x}{:02x}{:02x}".format(r, g, b)
def rg(num):
num = int(np.round((num / 100) * 124))
r = 124 - num
g = 124 + num
b = 0
x = rgb2hex(r, g, b)
return x
def colourmap(value):
if value != -99 and math.isnan(value) == False: # changed to treat -99 as NaN :-(
y = rg(value)
else:
y = "#808080"
return y
# some geometry to demonstrate
def foo():
return True
homes = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
homes["Percentage"] = np.random.randint(1, 100, len(homes))
homes["func_col"] = foo
homes.loc[homes.sample(20).index, "Percentage"] = np.nan
# miss this line and error is generated...
homes["Percentage"] = homes["Percentage"].fillna(-99)
excl = {}
for c in homes.columns:
try:
json.dumps(homes[c].to_dict())
except TypeError as e:
if c != "geometry":
excl[c] = str(e)
m = homes.loc[:, [c for c in homes.columns if c not in excl.keys()]].explore(
column="Percentage",
cmap=lambda value: colourmap(value), # map to custom colour scheme
marker_kwds=dict(radius=15, fill=True), # make marker radius 15px with fill
# tooltip=labels, # show labels in the tooltip
legend=False,
name="Homes", # name of the layer in the map
)
m # plot
Solution 2:[2]
Combining from Rob's answer and discussion in comments, here was the final solution that is pretty much what I ended up using:
import geopandas as gpd
import numpy as np
import math
import json
import random
import pandas as pd
import random
def rgb2hex(r, g, b):
return "#{:02x}{:02x}{:02x}".format(r, g, b)
def rg(p):
d = [255,0,0]
d[1] = int((510*p)/100)
if d[1]>255:
d[0] -= d[1]-255
d[1] = 255
x = rgb2hex(d[0],d[1],d[2])
return x
def colourmap(value):
if value != -99 and math.isnan(value) == False: # changed to treat -99 as NaN :-(
y = rg(value)
else:
y = "#808080"
return y
def foo(df):
x = np.random.randint(-180, 180, len(df))
y = np.random.randint(-90, 90, len(df))
s = gpd.GeoSeries.from_xy(x, y, crs="EPSG:4326")
return s
homes = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
homes["Percentage"] = np.random.randint(1, 100, len(homes))
homes["func_col"] = foo(homes)
homes.loc[homes.sample(20).index, "Percentage"] = np.nan
# miss this line and error is generated...
homes["Percentage"] = homes["Percentage"].fillna(-99)
homes = homes.set_geometry('func_col')
excl = {}
for c in homes.columns:
try:
json.dumps(homes[c].to_dict())
except TypeError as e:
if c != "geometry":
excl[c] = str(e)
m = homes.explore(
column="Percentage",
cmap=lambda value: colourmap(value),
marker_kwds=dict(radius=15, fill=True), # make marker radius 15px with fill
tooltip=["name"], # show "name" column in the tooltip
legend_kwds=dict(caption="Percentage",colorbar=True), # do not use colorbar
legend=False, # do not show column label in the tooltip
name="homes" # name of the layer in the map
)
m
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Ross Perry |