Search code examples
pythoncolorsplotlychoroplethcontinuous

Continuous Color Scale Adjusts on Filtering


I am working on a presidential elections project that involves filtering a choropleth map. My data is at the county level, and I have a drop down box that allows a user to select a state. The counties are colored by a blue to red continuous color scale representing the lean from democrat to republican. The variable I use for the color scale is the margin between the vote of both parties.

If the margin is positive, the county should be colored a shade of blue. If the margin is negative, the county should colored a shade of red.

However, when I filter to a particular state and all counties in that state voted for one party, the scale finds the lowest margin value and assigns that a color on the blue end of the spectrum even if that county voted more for the Republican.

Is there a way to fix the color scale when filtering so the counties are colored correctly?

Here is some example code:

import pandas as pd
import dash
import os
from urllib.request import urlopen
import json
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px

with urlopen(
    "https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json"
) as response:
    counties = json.load(response)

data = [
    ["Delaware", "Kent County", 10001, 0.467, 0.517, -75.513210, 39.156876],
    ["Delaware", "New Castle County", 10003, 0.322, 0.663, -75.513210, 39.156876],
    ["Delaware", "Sussex County", 10005, 0.559, 0.428, -75.513210, 39.156876],
    ["District of Columbia", "District of Columbia",11001,0.0712,0.913,-77.014468,38.910270],
    ["Rhode Island", "Bristol County",44001,0.2429,0.7352,-71.41572,41.65665],
    ["Rhode Island", "Kent County",44003,0.45117,0.5275,-71.41572,41.65665],
    ["Rhode Island", "Newport County",44005,0.3406,0.6389,-71.41572,41.65665],
    ["Rhode Island", "Providence County",44007,0.3761,0.605177,-71.41572,41.65665],
    ["Rhode Island", "Washington County",44009,0.392032,0.5857,-71.41572,41.65665]
]

data = pd.DataFrame(
    data,
    columns=[
        "State",
        "County",
        "fips_code",
        "perc_gop",
        "perc_dem",
        "lon",
        "lat",
    ],
)
state_choices = data["State"].sort_values().unique()

data['margin_perc'] = data['perc_dem'] - data['perc_gop']

app = dash.Dash(__name__, assets_folder=os.path.join(os.curdir, "assets"))
server = app.server
app.layout = html.Div([
        html.Div([
                dcc.Dropdown(
                    id="dropdown1",
                    options=[{"label": i, "value": i} for i in state_choices],
                    value=state_choices[0],
                )
        ],style={"width": "100%", "display": "inline-block", "text-align": "center"}
        ),
        # State Map with County Choropleth
        html.Div([
            dcc.Graph(id="state_map")],
            style={"width": "100%", "display": "inline-block", "text-align": "center"},
        )
    ]
)


@app.callback(Output("state_map", "figure"), Input("dropdown1", "value"))
def update_figure3(state_select):
    new_df = data[data["State"] == state_select]

    avg_lat = new_df["lat"].mean()
    avg_lon = new_df["lon"].mean()

    fig = px.choropleth_mapbox(
        new_df,
        geojson=counties,
        locations="fips_code",
        color="margin_perc",
        color_continuous_scale="balance",
        mapbox_style="carto-positron",
        zoom=6,
        center={"lat": avg_lat, "lon": avg_lon},
        opacity=0.5,
        labels={
            "State": "State",
            "County": "County",
            "perc_gop": "% Republican",
            "perc_dem": "% Democratic",
            "margin_perc":"% Margin"
        },
        hover_data={
            "fips_code": False,
            "State": True,
            "County": True,
            "perc_gop": True,
            "perc_dem": True,
        },
    )
    fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0})
    return fig


app.run_server(host="0.0.0.0", port="8051")

Solution

  • Figured it out --> needed to read documentation more carefully :/

    The color_continuous_midpoint argument came in handy. Just calculated the midpoint for the color variable for the entire dataset and used that as the fixed midpoint in the scale.