交互式多过滤 Dash Plotly GIS 地图——图不起作用

Posted

技术标签:

【中文标题】交互式多过滤 Dash Plotly GIS 地图——图不起作用【英文标题】:Interactive Multi-Filterable Dash Plotly GIS Map-- Figure Not Working 【发布时间】:2021-11-24 02:35:43 【问题描述】:

你好堆栈交换,

对于我的一生,我无法弄清楚我在这一点上做错了什么。我是一名初级程序员,所以我为来到这里感到自豪;但是,如果有其他更有效的解决方案,请随时提出建议。

数据文件:

gdf_final_serial ^这是一个精简版,因为实际数据集约为 680K 行 geo_school ^正在使用的Json

需要 Dash 应用程序做什么:

在列出的字段(县、区名称、年龄、RE、四分位数、住房不安全)方面用作多过滤 GIS 地图 根据选择的过滤器,使用“点”的准确计数更新 px.choropleth_mapbox。

让我们跳入代码。 我也在 Google Colab Notebook 上运行它。

filename_1 = root_path+"schoolDistrictBoundaries_Fixed.json"
file = open(filename_1)
schoolDistricts = gpd.read_file(file)

^^ 在全局单元格中运行

import dash
import dash.dependencies
import plotly.express as px
import pandas as pd
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
import numpy as np

px.set_mapbox_access_token(mapbox_access_token)

###   Creating gdf_final_serial

geo_df = gpd.GeoDataFrame.from_features(geo_school["features"]).merge(joined, on="OBJECTID").set_index("OBJECTID") #GeoJson Join to Joined Dataframe
cleaned_geo_df = geo_df[["geometry_x", "CountyName_x", "RE", "QUARTILES", "HOUSING_INSECURE", "County", "Age", "DistrictName_x"]] #Picks the right columns
geo_df_final = cleaned_geo_df.rename(columns="CountyName_x": "CountyName", "geometry_x": "geometry", 'DistrictName_x': 'DistrictName') #Changing Column Names
geo_df_final.to_pickle(root_path+"geo_df_final") #Write to drive as a pickle to serialize
df_final_serial = pd.read_pickle(root_path+"geo_df_final") #Read as DataFrame
gdf_final_serial = gpd.GeoDataFrame(data = df_final_serial, geometry= "geometry", crs = "epsg:4326") #Turn into GeoPandas DataFrame and set the geometry and crs


re_indicators = gdf_final_serial.RE.unique()
county_indicators = gdf_final_serial.CountyName.unique()
age_indicators = gdf_final_serial.Age.unique()
district_indicators = gdf_final_serial.DistrictName.unique()

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div(
    [
        html.Div(
            children=[
                html.Label("County"),
                dcc.Checklist(
                    id="county",
                    options=["label": i, "value": i for i in county_indicators],
                    value=[]
                ),
                html.Label("District Name"),
                dcc.Dropdown(
                    id="dname",
                    options=[],
                    value=[],
                    multi=True,
                ),
                html.Label("Age"),
                dcc.Dropdown(id="age", 
                             options=[], 
                             value=[],
                             multi = True),
                html.Label("RE"),
                dcc.Dropdown(id="re", 
                             options=[], 
                             value=[], 
                             multi = True),
                html.Label("Quartiles"),
                dcc.Dropdown(id="quartiles", 
                             options=[], 
                             value=[], 
                             multi=True),
                html.Label("Housing"),
                dcc.Dropdown(id="housing", 
                             options=[], 
                             value=[], 
                             multi=True),
                dcc.Graph(id="my_map", figure=)])
            ]
        )

@app.callback(
  dash.dependencies.Output("dname", "options"), 
  dash.dependencies.Input("county", "value")
)
def choose_county(county_pick): 
  if len(county_pick) > 0: 
      dff=gdf_final_serial[gdf_final_serial.CountyName.isin(county_pick)]
  else: 
      raise dash.exceptions.PreventUpdate
  return ["label": i, "value": i for i in (dff.DistrictName.unique())]

@app.callback(
  dash.dependencies.Output("dname", "value"),
  dash.dependencies.Input("dname", "options"),
)
def set_city_value(available_options_dname):
  return [x["value"] for x in available_options_dname]


@app.callback(
  dash.dependencies.Output("age", "options"), 
  dash.dependencies.Input("dname", "value")
)
def dname_age_picker(choose_dname):
  print(choose_dname)
  if len(choose_dname) > 0:
    dff = gdf_final_serial[gdf_final_serial.DistrictName.isin(choose_dname)]
  else:
    raise dash.exceptions.PreventUpdate
  return ["label": i, "value": i for i in (dff.Age.unique())]


@app.callback(
  dash.dependencies.Output("age", "value"),
  dash.dependencies.Input("age", "options"),
)
def set_age_value(available_options_age):
  return [x["value"] for x in available_options_age]


@app.callback(
  dash.dependencies.Output("re", "options"), 
  dash.dependencies.Input("age", "value")
)
def age_re_picker(choose_age):
  if len(choose_age) > 0:
    dff = gdf_final_serial[gdf_final_serial.Age.isin(choose_age)].dropna(axis = 0, how = 'any', subset = ["RE"])
  else:
    raise dash.exceptions.PreventUpdate
  return ["label": i, "value": i for i in (dff.RE.unique())]

@app.callback(
  dash.dependencies.Output("re", "value"),
  dash.dependencies.Input("re", "options")
)
def set_re_value(available_options_re):
  return [x["value"] for x in available_options_re]


@app.callback(
  dash.dependencies.Output("quartiles", "options"), 
  dash.dependencies.Input("re", "value")
)
def re_quartile_picker(choose_re_value):
  if len(choose_re_value) >= 0:
    dff = gdf_final_serial[gdf_final_serial.RE.isin(choose_re_value)].dropna(axis = 0, how = 'any', subset = ["QUARTILES"])
  else:
    raise dash.exceptions.PreventUpdate
  return ["label": i, "value": i for i in (dff.QUARTILES.unique())]

@app.callback(
  dash.dependencies.Output("quartiles", "value"),
  dash.dependencies.Input("quartiles", "options"),
)
def set_quart_value(available_options_quart):
  return [x["value"] for x in available_options_quart]

@app.callback(
  dash.dependencies.Output("housing", "options"), 
  dash.dependencies.Input("quartiles", "value")
)
def quart_picker(choose_quart_value):
  if len(choose_quart_value) >= 0:
      dff = gdf_final_serial[gdf_final_serial.QUARTILES.isin(choose_quart_value)]
  else:
      raise dash.exceptions.PreventUpdate
  return ["label": i, "value": i for i in (dff.HOUSING_INSECURE.unique())]

@app.callback(
  dash.dependencies.Output("housing", "value"),
  dash.dependencies.Input("housing", "options"),
)
def set_housing_value(available_options_housing):
  return [x["value"] for x in available_options_housing]

@app.callback(
  dash.dependencies.Output("my_map", "figure"),
  [dash.dependencies.Input("housing", "value"), 
  dash.dependencies.Input("quartiles", "value"), 
  dash.dependencies.Input("re", "value"),
  dash.dependencies.Input("age", "value"),
  dash.dependencies.Input("dname", "value"),
  dash.dependencies.Input("county", "value")]
)
def update_fig(selected_housing, selected_quartiles, selected_re, selected_age, selected_dname, selected_county):
  gdff_1 = gdf_final_serial[gdf_final_serial.CountyName.isin(selected_county) & 
                            gdf_final_serial.DistrictName.isin(selected_dname) &
                            gdf_final_serial.Age.isin(selected_age) &
                            gdf_final_serial.RE.isin(selected_re) &
                            gdf_final_serial.QUARTILES.isin(selected_quartiles) & 
                            gdf_final_serial.HOUSING_INSECURE.isin(selected_housing)]
  count_points = gdff_1.groupby("OBJECTID").size().rename("points")
  gdf_last = gdff_1.merge(count_points, on="OBJECTID", how="right", right_index = True)
  gdf_last.to_pickle(root_path+"gdf_last") #Write to drive as a pickle to serialize
  ddf_final_serial = pd.read_pickle(root_path+"gdf_last") #Read as DataFrame
  gddf_final_serial = gpd.GeoDataFrame(data = ddf_final_serial, geometry= "geometry", crs = "epsg:4326")
  gdff_2 = gddf_final_serial.head(50)
  px.set_mapbox_access_token(mapbox_access_token)
  fig = px.choropleth_mapbox(data_frame= gdff_2,
                             geojson= geo_school,
                             locations= 'DistrictName',
                             featureidkey = 'properties.DistrictName',
                             color="points",
                             center="lat": 38.5941, "lon": -119.8815, 
                             mapbox_style = 'dark').update_layout(mapbox_accesstoken =  mapbox_access_token)
  return fig
# Run app and display result inline in the notebook
if __name__ == "__main__":
  app.run_server(host="127.0.0.1", port="8888",debug=True, use_reloader=False)

我已经能够收到各种条件,其中地图/过滤器会生成一个县,有时是两个县,但绝不会产生整个 shebang。请记住,我的最终目标是为 ENTIRE 数据集创建它(可能使用 NVIDIA 的 RapidsAI;但是我在 Google Colab 中也无法安装它)。

更有趣的是,当我在 dash 之外运行此代码(作为实际数据集的示例)并将其传递给 fig.show() 时,它会显示出来;但是,它在 Dash 应用程序中不起作用。

我怀疑问题出在我的 json 处理或最终回调上,因为调试器表明当输入存在时,my_map.figure 没有输出数据。感谢您的帮助。

【问题讨论】:

我的回答我的指导你,而不是你想听到的。请务必联系 RAPIDS GoAi slack,因为我很想看看您构建了什么 :)。 join.slack.com/t/rapids-goai/shared_invite/… 【参考方案1】:
    目前很难在 Colab 中做你想做的事,因为 Plot.ly dash will run inline,但不是作为 Dash app dashboard 由于 Colab 当前对其实例的访问限制。 You should be using the RAPIDS install template for Colab, btw。 We have something working similar to what you're working on, deployed in paperspace。我们创建了一个RAPIDS based tutorial。如果您愿意,也可以在其他地方take the code and deploy it。

【讨论】:

以上是关于交互式多过滤 Dash Plotly GIS 地图——图不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Plotly-Dash:- 文件上传后在 plotly dash 中进行多列过滤

Plotly Dash:选择 DataTable 中的行作为回调输出 + 过滤器

Plotly Dash 回调错误更新输出图

Dash by Plotly vs Jupyter Dashboards 的优缺点是啥? [关闭]

Choropleth Plotly Graph 未出现在 Dash 上

Plotly-Dash:反应迟钝的情节