Plotly Choroplethmapbox 未显示所有多边形

Plotly Choroplethmapbox not showing all polygons

我在使用 Plotly 时遇到了一个奇怪的问题,下图将提供一些背景信息:

This is the map made with Bokeh

This is the map made with Plotly

相同的转换步骤适用于两个版本,但由于某些原因,Plotly 将排除某些形状。

这些是我正在使用的转换步骤:

import pandas as pd
import plotly.io as pio
import plotly.graph_objs as go
import json
import geopandas as gpd
import matplotlib.pyplot as plt
from shapely import wkt
from bokeh.plotting import save, figure
from bokeh.models import GeoJSONDataSource, LinearColorMapper, ColorBar
from bokeh.io import show, output_file
from bokeh.palettes import brewer

df_test = pd.read_csv(f'{filepath}')
df_blocks = pd.read_csv(f'{filepath}')
group_2 = df_test[['geo_name', 'edited_characteristics', 'total', 'male', 'female']]
group_2 = group_2.pivot(index='geo_name', columns='edited_characteristics', values=['total', 'male', 'female'])
cat = 'Total - Low-income status in 2015 for the population in private households to whom low-income concepts are applicable - 100% data'
group_2['LIM 0-17 percent'] = (
        group_2[( 'total', f'{cat}//0 to 17 years')] /
        group_2[( 'total', cat)]
        )
group_2.reset_index(inplace=True)
g2 = group_2[['geo_name', 'LIM 0-17 percent']]
g2.rename(columns={'geo_name': 'DAUID'}, inplace=True)
df_g2 = pd.merge(g2, df_blocks, on='DAUID')
df_g2['geometry'] = df_g2['geometry'].apply(wkt.loads)

geo_df_g2 = gpd.GeoDataFrame(df_g2, geometry='geometry')
geo_df_g2.crs = {'init': 'epsg:3347'}
geo_df_g2 = geo_df_g2.to_crs({'init': 'epsg:4326'})
geo_df_g2 = geo_df_g2[geo_df_g2[('LIM 0-17 percent', '')] < 1]
mean = geo_df_g2[('LIM 0-17 percent', '')].mean()
std = geo_df_g2[('LIM 0-17 percent', '')].std()
geo_df_g2 = geo_df_g2[(geo_df_g2[('LIM 0-17 percent', '')] < (mean - 1 
    * std)) | (geo_df_g2[('LIM 0-17 percent', '')] > (mean + 1 * std))]
geo_df_g2.columns = [x[0] if type(x) is tuple else x for x in 
    geo_df_g2.columns]
geo_df_g2 = geo_df_g2.loc[:, ~geo_df_g2.columns.duplicated()]
geo_df_g2_j = geo_df_g2.copy()
geo_df_g2_j['DAUID'] = geo_df_g2_j['DAUID'].astype(str)
geo_df_g2_j.set_index('DAUID', inplace=True)
geo_df_g2_json = json.loads(geo_df_g2_j.to_json())

使用情节

geo_df_g2 = geo_df_g2[['DAUID', 'LIM 0-17 percent']]
geo_df_g2['DAUID'] = geo_df_g2['DAUID'].astype(str)
fig = go.Figure(go.Choroplethmapbox(geojson=geo_df_g2_json,
                                    locations=geo_df_g2['DAUID'],
                                    z=geo_df_g2['LIM 0-17 percent'],
                                    colorscale='Viridis',
                                    zauto=True,
                                    marker_opacity=0.5,
                                    marker_line_width=0.5)
                )
fig.update_layout(mapbox_style='white-bg',
                  #mapbox_accesstoken=mapbox_token,
                  mapbox_zoom=12,
                  mapbox_center={'lat': 45.41117, 'lon': -75.69812})
fig.update_layout(margin={'r':0, 't':0, 'l':0, 'b':0})
pio.renderers.default = 'browser'
fig.show()

使用散景

json_data = json.dumps(geo_df_g2_json)

geosource = GeoJSONDataSource(geojson=json_data)
palette = brewer['YlGnBu'][8]
palette = palette[::-1]
color_mapper = LinearColorMapper(palette = palette, low = 0, high = 40)
    tick_labels = {'0': '0%', '5': '5%', '10':'10%', '15':'15%', 
    '20':'20%', '25':'25%', '30':'30%','35':'35%', '40': '>40%'}
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=8,width 
    = 500, height = 20,
    border_line_color=None,location = (0,0), orientation = 
    'horizontal', major_label_overrides = tick_labels)
p = figure(title='LIM', plot_height=600, plot_width=950, 
    toolbar_location=None)
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
p.patches('xs', 'ys', source=geosource, fill_color={'field': 'LIM 0-17 percent', 'transform': color_mapper}, line_color='black', line_width=0.25, fill_alpha=1)
output_file('test_bokeh.html')
show(p)

如您所见,它们都使用相同的投影、相同的数据帧转换和相同的类别。有没有办法来解决这个问题?

TIA

编辑:形状位置正确,图中缺少了很多形状。

更新: 为了看看其他 Plotly 模块是否可以解决问题,我缩小了问题范围。使用 Plotly 上的教程创建 Scattermapbox,他们调用 mapbox 特征的方式比教程在 Choroplethmapbox 上更能揭示继承问题。显然发生的事情是 Plotly(或 Mapbox)没有将附近的几组点识别为多边形的坐标,因此将它们排除在外,直到您指定您希望它们存在。这是通过将 'type' 的 mapbox 字典值设置为 'fill'、'line' 或 'circle' 来完成的。这当然会导致另一个问题,即这些新形状的颜色或标记方式与原始多边形不同,因为默认情况下它们不存在。

以下代码示例有助于说明多边形点未形成完整形状的问题:

fig = go.Figure(go.Choroplethmapbox(geojson=geo_df_g2_json,

                                    locations=geo_df_g2['DAUID'],
                                    z=geo_df_g2['LIM 0-17 percent'],
                                    below='traces',
                                    colorscale='Viridis',
                                    zauto=True,
                                    marker_opacity=0.5,
                                    marker_line_width=0.5)
                        )
fig.update_layout(
        mapbox = {
            'style': 'carto-positron',
            'center': {'lat': 45.41117, 'lon': -75.69812},
            'zoom': 12, 'layers': [{
                'source': {
                    'type': "FeatureCollection",
                    'features': geo_df_g2_json['features']
                },
            'type': 'fill', 'below': 'traces', 'color': 'lightblue'}]},
        margin = {'l':0, 'r':0, 'b':0, 't':0})
fig.show()

为了阐明我的意图,我想回答两个问题:

  1. 为什么 Plotly 将一些多边形坐标转换为形状,而将其他多边形坐标转换为单个点?

  2. 使用上述函数后,是否有基于 'z' 值填充形状的变通方法?

我找到了导致多边形消失的原因。由于 Plotly 使用 geojson 文件 vs. 与 geopandas dataframes 交互(我相信这就是原因),它对数据格式有更严格的要求。其他库,如 Bokeh、contextily 或 geopandas,在绘制之前聚合多行多边形,这些多边形共享一个公共父级,而 Plotly 单独查看它们。在我的例子中,由于每个 'id' 都有多个子 ID,每个子 ID 都有自己的多边形坐标,Plotly 在绘制它们时只会选择一个。它会将其余部分存储为点,并且只有在我使用 'fill' 选项时才会显示它们。这是我的数据框的粗略示例:

DAUID DBUID Total geometry
001   00101 5     Polygon(x1, y1)
001   00102 5     Polygon(x2, y2)
001   00103 5     Polygon(x3, y3)

因此,虽然主要 ID 和总值保持不变,但几何图形却没有。我在尝试编写颜色映射器时偶然发现了这一点,并注意到我有重复的 DAUID 条目。最后,是我没有使用正确的数据库造成的。

看来 Plotly 很快就会引入对 geopandas 的支持,所以我很想知道它是否解决了这样的边缘情况。

我遇到了类似的问题。那是我的 geopandas 数据框的一部分,看起来像 -

   province_id  geometry
0  1            POLYGON (x1, y1)
1  1            POLYGON (x2, y2)
2  1            POLYGON (x3, y3)

我用province_id_data.dissolve(by='province_id', aggfunc='first')把它们组合成一个多边形,然后用plotly绘图。