Black Lives Matter. Please consider donating to Black Girls Code today.
Learn how to use COVID-19 data in open source Dash apps. Register for the Sept 23rd webinar with IQT!

How to add a ColorBar in custom Choropleth (error)

I’m trying to add a color bar for median income (per county) in a choropleth.

When I add a ColorBar to the below, I get the error:
TypeError: Object of type 'range' is not JSON serializable

When I remove the Color Bar code, the chart renders properly.
Any push in the right direction is appreciated.

My colorscl array looks like:

[[0, '#ffffe0'],
 [1, '#fffddb'],
 [2, '#fffad7'],
 [3, '#fff7d1'],
 [4, '#fff5cd']]
...
[[96, '#94010e'],
 [97, '#91000a'],
 [98, '#8e0006'],
 [99, '#8b0000'],
 [100, '#8b0000']]

The Code

import plotly.plotly as py
from plotly.graph_objs import *
import pandas as pd


def popup_text(row):
    val = '''{0}<br>\
    Total Profit: ${1:,.0f}<br>\
    {2}, {3}<br>\
    ShipToZip: {4}<br>\
    CityPop: {5:,.0f}<br>\
    CityGrowth: {6:.0%}<br>\
    Density: {7:,.0f}<br>\
    County Density: {8:,.0f}<br>\
    Households: {9:,.0f}<br>\
    AvgIncome: ${10:,.0f}<br>\
    CountyAvgIncome: ${11:,.0f}<br>\
    White: {12:.0%}<br>\
    FEMA Disaster Count: {13:,.0f}<br>\
    Bldg Valuation Growth: {14:.0%}'''.format(
        row['Company'],
        pd.to_numeric(row['Profit_sum'],errors='coerce'), 
        row['City'], row['State'], row['ShipToLocZip'],
        row['CityPop2016'], row['CityPopGrowth'], row['Density'],
        row['County_PopDensity'], row['Households'], row['AvgIncome'],
        row['County_MedianHouseoldIncome'], row['PopulationRaceWhite'],
        row['DisasterCount'], row['ValuationGrowth'])
    
    return val.replace('\\','').strip()


def f(df, start_year, end_year, client_count, mytitle):

    # Set range
    df = df[df.OrderDate.dt.year.between(start_year, end_year)]

    # Add Profit Total for Every Client
    df['Profit_sum'] = df.groupby('Company')['Profit'].transform(sum)
    df = df.sort_values(['Profit_sum', 'Company'], ascending=[False, False])

    # Entire Sales History
    plotly_df = df[[
        'Company', 'ShipToLocZip', 'City', 'State', 'Population', 'Density',
        'CityPop2016', 'CityPopGrowth', 'AvgIncome',
        'County_MedianHouseoldIncome', 'County_PopDensity', 'DisasterCount',
        'ValuationGrowth', 'PopulationRaceWhite', 'Households', 'Latitude',
        'Longitude', 'Profit_sum'
    ]]

    # plotly_df.head(1)
    plotly_df['Text'] = plotly_df.apply(lambda row: popup_text(row), axis=1)

    # 20 best clients
    bestclient_df = (plotly_df.groupby('Company').agg({
        'Profit_sum':
        'min',
        'Latitude':
        lambda x: x.value_counts().index[0],
        'Longitude':
        lambda x: x.value_counts().index[0],
        'Text':
        'min'
    }).reset_index().sort_values('Profit_sum',
                                 ascending=False)[0:client_count])
    # bestclient_df.head(1)

    data =  graph_objs.Data([
        
        # All Sales
        Scattermapbox(
            lat=list(plotly_df.Latitude),
            lon=list(plotly_df.Longitude),
            mode='markers',
            marker=Marker(
                size=4,
                #color=colors,
                opacity=0.7),
            text=list(plotly_df.Text),
            hoverinfo='text',
            showlegend=False),
        
        # Best Clients
        Scattermapbox(
            lat=list(bestclient_df.Latitude),
            lon=list(bestclient_df.Longitude),
            mode='markers',
            marker=Marker(size=12, color='rgb(50, 255, 100)', opacity=0.8),
            text=list(bestclient_df.Text),
            hoverinfo='text',
            showlegend=False),
        
        # Color Bar for Census Data
        Scattermapbox(
                    lat = [0],
                    lon = [0],
                    marker = graph_objs.Marker(
                                  cmax=100,
                                  cmin=0,
                                  colorscale = colorscl,
                                  showscale = True,
                                  autocolorscale=False,
                                  color=range(0,101),
                                  colorbar= graph_objs.ColorBar(len = .89)
                                       ),
                    mode = 'markers')    
            
    ])

    layout =  graph_objs.Layout(
        title= '{0} and {1} Best Clients ({2}-{3})'.format(
            mytitle, client_count, start_year, end_year),
        autosize=True,
        hovermode='closest',
        mapbox=dict(
        layers=[
            dict(sourcetype = 'geojson',
                     source =sources[k],
                     below="water",
                     type = 'fill',
                     color = sources[k]['features'][0]['properties']['Color'],
                     opacity=0.5,
                    ) for k in range(len(sources))
           ],
        accesstoken=MAPBOX_API_KEY,
        bearing=0,
        center=dict(lat=38, lon=-94),
        pitch=0,
        zoom=3.6,
        style='light'
        )
    )

    fig = dict(data=data, layout=layout)
    return py.iplot(fig, filename='Affinity Sales')
f(af_df, start_year=2013, end_year=2016, client_count=30, mytitle='Sales over Median Income')`

Error Dump

TypeError                                 Traceback (most recent call last)
<ipython-input-229-73b59cacc0ad> in <module>()
    137 # profit_df= pd.read_pickle(url)
    138 # profit_df.columns
--> 139 f(af_df, start_year=2013, end_year=2016, client_count=30, mytitle='Sales over Median Income')

<ipython-input-229-73b59cacc0ad> in f(df, start_year, end_year, client_count, mytitle)
    131 #                     image_filename='MytestwithColorBar')
    132 
--> 133     return py.iplot(fig, filename='Affinity Sales')
    134 
    135 

//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/plotly/plotly.py in iplot(figure_or_data, **plot_options)
    133     if 'auto_open' not in plot_options:
    134         plot_options['auto_open'] = False
--> 135     url = plot(figure_or_data, **plot_options)
    136 
    137     if isinstance(figure_or_data, dict):

//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/plotly/plotly.py in plot(figure_or_data, validate, **plot_options)
    226     data = fig.get('data', [])
    227     plot_options['layout'] = fig.get('layout', {})
--> 228     response = v1.clientresp(data, **plot_options)
    229 
    230     # Check if the url needs a secret key

//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/api/v1/clientresp.py in clientresp(data, **kwargs)
     27     payload = {
     28         'platform': 'python', 'version': version.__version__,
---> 29         'args': _json.dumps(data, **dumps_kwargs),
     30         'un': creds['username'], 'key': creds['api_key'], 'origin': 'plot',
     31         'kwargs': _json.dumps(kwargs, **dumps_kwargs)

//anaconda/envs/OSMNX/lib/python3.6/json/__init__.py in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    236         check_circular=check_circular, allow_nan=allow_nan, indent=indent,
    237         separators=separators, default=default, sort_keys=sort_keys,
--> 238         **kw).encode(obj)
    239 
    240 

//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/utils.py in encode(self, o)
    134 
    135         # this will raise errors in a normal-expected way
--> 136         encoded_o = super(PlotlyJSONEncoder, self).encode(o)
    137 
    138         # now:

//anaconda/envs/OSMNX/lib/python3.6/json/encoder.py in encode(self, o)
    197         # exceptions aren't as detailed.  The list call should be roughly
    198         # equivalent to the PySequence_Fast that ''.join() would do.
--> 199         chunks = self.iterencode(o, _one_shot=True)
    200         if not isinstance(chunks, (list, tuple)):
    201             chunks = list(chunks)

//anaconda/envs/OSMNX/lib/python3.6/json/encoder.py in iterencode(self, o, _one_shot)
    255                 self.key_separator, self.item_separator, self.sort_keys,
    256                 self.skipkeys, _one_shot)
--> 257         return _iterencode(o, 0)
    258 
    259 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,

//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/utils.py in default(self, obj)
    202             except NotEncodable:
    203                 pass
--> 204         return _json.JSONEncoder.default(self, obj)
    205 
    206     @staticmethod

//anaconda/envs/OSMNX/lib/python3.6/json/encoder.py in default(self, o)
    178         """
    179         raise TypeError("Object of type '%s' is not JSON serializable" %
--> 180                         o.__class__.__name__)
    181 
    182     def encode(self, o):

TypeError: Object of type 'range' is not JSON serializable

Fixed it. Removing color=range(0,101) did the trick.
Mostly done.
Would love to see a feature where Plotly rasters the layered county geojson file ––over drawing each county by SVG…or something. Maybe the rasterized file is 3-4X so zooming in to the image could still work quality-wise.

Or, I could just rent that Chinese super computer (Sunway TaihuLight) to zoom in and out of the chart. That would be cool.