You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Plotly Choropleth地图:缺失值区域处理、多列数据提示及年份动画实现技术问询

Answers to Your Plotly Choropleth Questions

Hey, let's work through each of your questions with practical code snippets and straightforward explanations:


1. Show Country Names on Hover for Missing Value Regions

The problem here is that Plotly's Choropleth only generates hover data for countries present in your dataset. To fix this, we can add a secondary Scattergeo trace that covers every country (including those with missing data) — we'll just hide the visible elements of this trace so it only contributes hover text.

Here's the adjusted code:

import plotly.graph_objects as go
import geopandas as gpd

# Get a full list of ISO3 country codes and names using GeoPandas' built-in dataset
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
full_countries = world[['name', 'iso_a3']].rename(columns={'name':'Zone', 'iso_a3':'Code zone (ISO3)'})
full_countries = full_countries.dropna(subset=['Code zone (ISO3)'])

# Your original choropleth trace
choropleth_trace = go.Choropleth(
    locations = world_map_df_sous_nutrition["Code zone (ISO3)"],
    z = round((world_map_df_sous_nutrition["Proportion pop en sous-nutrition"]),2),
    text = world_map_df_sous_nutrition["Zone"],
    colorscale = "earth",
    autocolorscale = False,
    reversescale = True,
    marker_line_color = "white",
    marker_line_width = .2,
    colorbar_tickprefix = "%",
    colorbar_title = "Proportion de personnes en sous nutrition"
)

# Scattergeo trace to add hover text for all countries
scatter_trace = go.Scattergeo(
    locations = full_countries["Code zone (ISO3)"],
    text = full_countries["Zone"],
    mode = 'text',
    textfont = {'color':'rgba(0,0,0,0)'},  # Make text invisible
    hoverinfo = 'text',
    marker = {'opacity':0}  # Hide markers entirely
)

# Combine both traces in the figure
fig = go.Figure(data=[choropleth_trace, scatter_trace])

# Your existing layout code remains the same
fig.update_layout(
    title_text="L'état de la sous-nutrition dans le monde en 2017",
    geo=dict(
        landcolor = 'lightgray',
        showland = True,
        showcountries = True,
        countrycolor = 'gray',
        countrywidth = 0.5,
        showframe=False,
        showcoastlines=False,
        projection_type='equirectangular'
    ),
    annotations = [dict(
        x=0.55,
        y=0.1,
        xref='paper',
        yref='paper',
        text='Source: FAO',
        showarrow = False
    )],
    margin={"r":0,"t":0,"l":0,"b":0},
)
fig.show()

Now when you hover over any country (even missing data regions), you'll see the country name pop up.


2. Replicate GeoPandas Missing Value Styles in Plotly

Plotly doesn't support hatch patterns directly in Choropleth traces, but we can combine multiple traces to mimic most of the GeoPandas style:

  1. A base Choropleth trace for countries with valid data
  2. A second Choropleth trace for missing countries, styled with light gray fill and red borders
  3. A custom legend entry to label missing regions

Here's the code:

import plotly.graph_objects as go
import geopandas as gpd

# Get full country list
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
full_countries = world[['name', 'iso_a3']].rename(columns={'name':'Zone', 'iso_a3':'Code zone (ISO3)'})
full_countries = full_countries.dropna(subset=['Code zone (ISO3)'])

# Split your data into present and missing values
present_df = world_map_df_sous_nutrition.dropna(subset=["Proportion pop en sous-nutrition"])
missing_df = full_countries[~full_countries["Code zone (ISO3)"].isin(present_df["Code zone (ISO3)"])]

# Trace for countries with valid data
present_trace = go.Choropleth(
    locations = present_df["Code zone (ISO3)"],
    z = round(present_df["Proportion pop en sous-nutrition"],2),
    text = present_df["Zone"],
    colorscale = "earth",
    autocolorscale = False,
    reversescale = True,
    marker_line_color = "white",
    marker_line_width = .2,
    colorbar_tickprefix = "%",
    colorbar_title = "Proportion de personnes en sous nutrition",
    name = "Data Available"
)

# Trace for missing countries
missing_trace = go.Choropleth(
    locations = missing_df["Code zone (ISO3)"],
    z = [0]*len(missing_df),  # Dummy value to lock color
    text = missing_df["Zone"] + "<br>Missing values",
    colorscale = [[0, 'lightgray'], [1, 'lightgray']],  # Fixed light gray fill
    autocolorscale = False,
    marker_line_color = "red",
    marker_line_width = 1,
    showscale = False,  # Hide extra colorbar
    name = "Missing Values"
)

fig = go.Figure(data=[present_trace, missing_trace])

fig.update_layout(
    title_text="L'état de la sous-nutrition dans le monde en 2017",
    geo=dict(
        showland=False,  # Disable default land since we use custom traces
        showcountries=False,
        showframe=False,
        showcoastlines=False,
        projection_type='equirectangular'
    ),
    annotations = [dict(
        x=0.55,
        y=0.1,
        xref='paper',
        yref='paper',
        text='Source: FAO',
        showarrow = False
    )],
    margin={"r":0,"t":0,"l":0,"b":0},
    legend=dict(
        title="Status",
        x=0.1, y=0.1
    )
)
fig.show()

Note: Plotly doesn't support hatch patterns in choropleths yet, so this is the closest we can get without complex SVG overlays.


3. Add Multiple Column Values to Hover & Switch Coloring Modes

Adding Multiple Columns to Hover

Simply modify the text parameter in your Choropleth trace to include all columns you want to display. Use <br> to create line breaks for readability:

text = (
    world_map_df_sous_nutrition["Zone"]
    + "<br>Sous-nutrition: " 
    + round(world_map_df_sous_nutrition["Proportion pop en sous-nutrition"],2).astype(str) 
    + "%"
    + "<br>Population: " 
    + world_map_df_sous_nutrition["Population"].astype(str)
)

Plotly will automatically show this combined text when you hover over a country.

Switching Coloring Modes

You can add a dropdown menu to let users switch between different columns for coloring. Here's how:

import plotly.graph_objects as go

# Define columns you want to switch between
color_modes = [
    {"label": "Sous-nutrition", "col": "Proportion pop en sous-nutrition", "unit": "%"},
    {"label": "Population", "col": "Population", "unit": ""}
]

# Initial trace with first column
initial_mode = color_modes[0]
fig = go.Figure(data=go.Choropleth(
    locations = world_map_df_sous_nutrition["Code zone (ISO3)"],
    z = round(world_map_df_sous_nutrition[initial_mode["col"]],2),
    text = (
        world_map_df_sous_nutrition["Zone"]
        + "<br>" + initial_mode["label"] + ": "
        + round(world_map_df_sous_nutrition[initial_mode["col"]],2).astype(str)
        + initial_mode["unit"]
    ),
    colorscale = "earth",
    autocolorscale = False,
    reversescale = True,
    marker_line_color = "white",
    marker_line_width = .2,
    colorbar_tickprefix = initial_mode["unit"],
    colorbar_title = initial_mode["label"]
))

# Create dropdown buttons
buttons = []
for mode in color_modes:
    button = dict(
        label=mode["label"],
        method="update",
        args=[
            {"z": [round(world_map_df_sous_nutrition[mode["col"]],2)]},
            {
                "colorbar": {"tickprefix": mode["unit"], "title": mode["label"]},
                "data[0].text": [
                    world_map_df_sous_nutrition["Zone"]
                    + "<br>" + mode["label"] + ": "
                    + round(world_map_df_sous_nutrition[mode["col"]],2).astype(str)
                    + mode["unit"]
                ]
            }
        ]
    )
    buttons.append(button)

fig.update_layout(
    title_text="L'état de la sous-nutrition dans le monde en 2017",
    geo=dict(
        landcolor = 'lightgray',
        showland = True,
        showcountries = True,
        countrycolor = 'gray',
        countrywidth = 0.5,
        showframe=False,
        showcoastlines=False,
        projection_type='equirectangular'
    ),
    annotations = [dict(
        x=0.55,
        y=0.1,
        xref='paper',
        yref='paper',
        text='Source: FAO',
        showarrow = False
    )],
    margin={"r":0,"t":0,"l":0,"b":0},
    updatemenus=[dict(
        buttons=buttons,
        direction="down",
        x=0.1,
        y=1.1
    )]
)
fig.show()

This adds a dropdown at the top of the figure that updates the coloring, hover text, and colorbar when users select a different metric.


4. Animated Choropleth Map by Year

Plotly supports animated choropleths using the frame parameter. First, make sure your data is in long format (one row per country per year). Then use this code:

import plotly.graph_objects as go
import geopandas as gpd

# Assume your data is in long format: columns = ["Code zone (ISO3)", "Zone", "Year", "Proportion pop en sous-nutrition"]
# Get full country list (same as before)
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
full_countries = world[['name', 'iso_a3']].rename(columns={'name':'Zone', 'iso_a3':'Code zone (ISO3)'})
full_countries = full_countries.dropna(subset=['Code zone (ISO3)'])

# Get unique sorted years
years = sorted(world_map_df_long["Year"].unique())

# Create frames for each year
frames = []
for year in years:
    year_data = world_map_df_long[world_map_df_long["Year"] == year]
    # Merge with full countries to ensure all regions are covered
    year_data_merged = full_countries.merge(year_data, on="Code zone (ISO3)", how="left")
    frame = go.Frame(
        data=[go.Choropleth(
            locations = year_data_merged["Code zone (ISO3)"],
            z = round(year_data_merged["Proportion pop en sous-nutrition"],2),
            text = (
                year_data_merged["Zone_x"]
                + "<br>Année: " + str(year)
                + "<br>Sous-nutrition: " 
                + year_data_merged["Proportion pop en sous-nutrition"].fillna("Missing").astype(str)
                + "%"
            ),
            colorscale = "earth",
            autocolorscale = False,
            reversescale = True,
            marker_line_color = "white",
            marker_line_width = .2,
            colorbar_tickprefix = "%",
            colorbar_title = "Proportion de personnes en sous nutrition"
        )],
        name=str(year)
    )
    frames.append(frame)

# Initial figure (first year)
initial_year = years[0]
initial_data = world_map_df_long[world_map_df_long["Year"] == initial_year]
initial_data_merged = full_countries.merge(initial_data, on="Code zone (ISO3)", how="left")

fig = go.Figure(
    data=[go.Choropleth(
        locations = initial_data_merged["Code zone (ISO3)"],
        z = round(initial_data_merged["Proportion pop en sous-nutrition"],2),
        text = (
            initial_data_merged["Zone_x"]
            + "<br>Année: " + str(initial_year)
            + "<br>Sous-nutrition: " 
            + initial_data_merged["Proportion pop en sous-nutrition"].fillna("Missing").astype(str)
            + "%"
        ),
        colorscale = "earth",
        autocolorscale = False,
        reversescale = True,
        marker_line_color = "white",
        marker_line_width = .2,
        colorbar_tickprefix = "%",
        colorbar_title = "Proportion de personnes en sous nutrition"
    )],
    frames=frames,
    layout=go.Layout(
        title_text="Evolution de la sous-nutrition dans le monde",
        geo=dict(
            landcolor = 'lightgray',
            showland = True,
            showcountries = True,
            countrycolor = 'gray',
            countrywidth = 0.5,
            showframe=False,
            showcoastlines=False,
            projection_type='equirectangular'
        ),
        annotations = [dict(
            x=0.55,
            y=0.1,
            xref='paper',
            yref='paper',
            text='Source: FAO',
            showarrow = False
        )],
        margin={"r":0,"t":0,"l":0,"b":0},
        updatemenus=[dict(
            type="buttons",
            showactive=True,
            buttons=[dict(
                label="Play",
                method="animate",
                args=[None, {"frame": {"duration": 500, "redraw": True}, "fromcurrent": True}]
            ), dict(
                label="Pause",
                method="animate",
                args=[[None], {"frame": {"duration": 0, "redraw": False}, "mode": "immediate"}]
            )]
        )],
        sliders=[dict(
            steps=[dict(
                method="animate",
                args=[[str(year)], {"frame": {"duration": 500, "redraw": True}, "mode": "immediate"}],
                label=str(year)
            ) for year in years],
            active=0,
            currentvalue={"prefix": "Année: "}
        )]
    )
)

fig.show()

This creates an animated map with play/pause buttons and a year slider, letting users watch the data evolve over time.


内容的提问来源于stack exchange,提问作者ElMeTeOr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 09:52:30