news 2026/9/9 23:25:48

Gradio + Plotly 交互地图实战:用 gr.Plot 构建纽约 Airbnb 房源筛选应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Gradio + Plotly 交互地图实战:用 gr.Plot 构建纽约 Airbnb 房源筛选应用

Gradio + Plotly 交互地图实战:用 gr.Plot 构建纽约 Airbnb 房源筛选应用

【免费下载链接】gradioBuild and share delightful machine learning apps, all in Python. 🌟 Star to support our work!项目地址: https://gitcode.com/GitHub_Trending/gr/gradio

导读

本文以仓库中的 map_airbnb 演示 为主体,讲解如何用 Gradio 的gr.Plot组件承载 Plotly 的Scattermapbox散点地图,并配合gr.Numbergr.CheckboxGroupgr.Button实现"按价格区间 + 按行政区"联动过滤 Airbnb 房源的可交互 Web 应用。读完本文你将掌握:从 Hugging Face Datasets 加载真实表格数据、用 pandas 做多条件筛选、用 Plotly 配置带 hover 详情的气泡地图,以及在gr.Blocks中通过demo.load与按钮click事件驱动图表刷新的完整链路。

一、演示功能与定位

仓库中 demo/map_airbnb/DESCRIPTION.md 用一句话点明了该 demo 的核心目标:

Display an interactive map of AirBnB locations with Plotly. Data is hosted on HuggingFace Datasets.

即:用 Plotly 渲染 Airbnb 房源的交互式地图,数据托管在 Hugging Face Datasets 上。落实到具体代码上,它实现了如下三层能力:

  1. 数据接入:通过datasets.load_dataset拉取纽约市 Airbnb 开放数据并转为 pandas DataFrame;
  2. 交互过滤:前端输入最低价、最高价和行政区集合,后端对 DataFrame 做布尔筛选,重新生成地图;
  3. 地图展示:以gr.Plot为输出组件承载 Plotly Figure,无需自行编写任何前端 JavaScript。

这与仓库中官方教程 plot-component-for-maps 指南 所述主题一致——Gradio 的gr.Plot组件本身支持 Matplotlib、Bokeh、Plotly、Altair 四类图(见 plot.py),本例选用的正是 Plotly。

二、运行前准备与启动方式

demo 目录结构非常简单,仅包含三个文件:

  • run.py:完整应用代码;
  • requirements.txt:第三方依赖;
  • DESCRIPTION.md:功能说明。

其中 requirements.txt 内容为:

plotly datasets

plotly用于构建Scattermapbox地图对象,datasets用于从 Hugging Face Hub 加载数据。启动前需确保当前 Python 环境已安装 gradio 本身,以及上述两个依赖,随后在仓库根目录执行:

pip install plotly datasets python demo/map_airbnb/run.py

两点运行前提需要留意(由代码行为可推断):

  • 需要联网:首次启动时load_dataset会从 Hugging Face Hub 下载gradio/NYC-Airbnb-Open-Data数据集,之后由datasets库本地缓存,离线重启时不再重复下载;
  • 依赖首次调用加载from datasets import load_dataset属于模块顶层导入,若当前环境未安装该库,应用会在启动阶段直接报ImportError

三、数据接入:从 Hugging Face Datasets 读取并转 DataFrame

import gradio as gr import plotly.graph_objects as go from datasets import load_dataset dataset = load_dataset("gradio/NYC-Airbnb-Open-Data", split="train") df = dataset.to_pandas()

代码在模块顶层一次性完成数据加载:从数据集gradio/NYC-Airbnb-Open-Datatrain分片读取数据,再调用to_pandas()将 Arrow Table 转换为 pandas DataFrame,后续的过滤、取列都基于df完成。

从后面filter_map对列名的引用可以看出,该 DataFrame 至少包含以下与本例直接相关的字段:

字段含义在本例中的用途
name房源名称作为 hover 气泡的标题文本
price每晚价格(美元)价格上下限过滤
neighbourhood_group所在行政区行政区集合过滤
latitude/longitude经纬度地图 marker 的坐标

四、核心过滤逻辑:filter_map 函数拆解

def filter_map(min_price, max_price, boroughs): filtered_df = df[(df['neighbourhood_group'].isin(boroughs)) & (df['price'] > min_price) & (df['price'] < max_price)] names = filtered_df["name"].tolist() prices = filtered_df["price"].tolist() text_list = [(names[i], prices[i]) for i in range(0, len(names))]

该函数签名与前端组件一一对应:min_pricemax_price来自两个gr.Numberboroughs来自gr.CheckboxGroup返回的被勾选选项列表。筛选采用 pandas 布尔索引,三个条件同时成立:

  1. df['neighbourhood_group'].isin(boroughs):行政区 ∈ 用户勾选集合(多选);
  2. df['price'] > min_price:价格严格大于下限;
  3. df['price'] < max_price:价格严格小于上限(两端均为开区间)。

随后从筛选结果中取出nameprice两列,按行打包成text_list二元组列表,作为地图气泡的自定义数据(customdata),用于 hover 时展示房源名称与价格。

实操建议(基于上面区间语义推断):若希望边界值也被包含,需把比较符改为>=/<=;此外价格过滤是"开区间"设计,当min_pricemax_price过于接近或筛选条件过严时,可能出现筛选结果为空的情形,此时地图上不会有任何 marker,属于符合过滤逻辑的正常表现,可在真实业务中额外提示"无匹配房源"。

五、绘制 Plotly Scattermapbox 地图并配置 hover 详情

fig = go.Figure(go.Scattermapbox( customdata=text_list, lat=filtered_df['latitude'].tolist(), lon=filtered_df['longitude'].tolist(), mode='markers', marker=go.scattermapbox.Marker( size=6 ), hoverinfo="text", hovertemplate='<b>Name</b>: %{customdata[0]}<br><b>Price</b>: $%{customdata[1]}' ))

关键配置项逐条说明:

  • lat/lon:房源经纬度列表,决定每个 marker 的地理位置;
  • mode='markers':绘制散点;marker.size=6控制气泡大小;
  • customdata=text_list:携带每行的(名称, 价格)附加数据,不直接展示、仅供 hover 模板引用;
  • hoverinfo="text"+hovertemplate:hover 时按模板渲染。%{customdata[0]}取名称、%{customdata[1]}取价格,<b>/<br>实现加粗与换行,效果即"名称 + $价格"的悬浮信息卡。

接着通过update_layout设置地图的底图风格与视野:

fig.update_layout( mapbox_style="open-street-map", hovermode='closest', mapbox=dict( bearing=0, center=go.layout.mapbox.Center( lat=40.67, lon=-73.90 ), pitch=0, zoom=9 ), ) return fig
  • mapbox_style="open-street-map":选用 OpenStreetMap 免费底图,因此无需配置 Mapbox access token 即可直接渲染,这是该 demo 能"零密钥开箱即用"的关键;
  • center将视野中心固定到纽约市区附近(lat 40.67 / lon -73.90),zoom=9控制缩放级别;
  • bearing=0pitch=0保持地图正北朝向、无俯仰角;
  • hovermode='closest'让鼠标贴近某个气泡时只高亮并展示最近的那个,避免重叠 marker 干扰阅读。

六、Blocks 页面布局与组件组装

with gr.Blocks() as demo: with gr.Column(): with gr.Row(): min_price = gr.Number(value=250, label="Minimum Price") max_price = gr.Number(value=1000, label="Maximum Price") boroughs = gr.CheckboxGroup(choices=["Queens", "Brooklyn", "Manhattan", "Bronx", "Staten Island"], value=["Queens", "Brooklyn"], label="Select Boroughs:") btn = gr.Button(value="Update Filter") map = gr.Plot()

页面结构与各组件职责:

  • gr.Column/gr.Row布局:价格输入放在同一行(Row),整块 UI 垂直排布(Column);
  • 两个gr.Number:提供价格上下限。默认值分别为 250 与 1000(value=250value=1000),即应用打开时默认只展示 $250~$1000 区间的房源;
  • gr.CheckboxGroup:提供纽约五大行政区多选。choices为 ["Queens", "Brooklyn", "Manhattan", "Bronx", "Staten Island"],默认勾选value=["Queens", "Brooklyn"]。从 checkboxgroup.py 的源码注释可见,choices决定可选项、value决定默认选中项,组件默认以type="value"返回被选中的选项值列表,正好对应filter_mapboroughs参数;
  • gr.Button:文本为 "Update Filter" 的触发按钮;
  • gr.Plot:地图输出容器,不接受用户输入、仅承载函数返回的 Plotly Figure。

七、事件绑定:首屏加载与按钮点击双触发

demo.load(filter_map, [min_price, max_price, boroughs], map) btn.click(filter_map, [min_price, max_price, boroughs], map) if __name__ == "__main__": demo.launch()
  • demo.load(fn, inputs, outputs):页面加载完成即执行一次filter_map,让用户无需点击就能先看到"默认条件(Queens + Brooklyn、$250~$1000)"下的初始地图;
  • btn.click(fn, inputs, outputs):点击 "Update Filter" 按钮时,把当前三个组件的值实时传给filter_map并刷新map
  • 两次事件绑定的inputs/outputs完全一致([min_price, max_price, boroughs]map),因此初始渲染与按钮刷新共用同一套过滤-绘图链路;
  • 末尾demo.launch()启动本地服务;若需要临时的公网分享地址,可参照仓库指南为launch()追加share=True

八、底层原理:gr.Plot 如何承载 Plotly 地图

弄清gr.Plot在服务端做了什么,有助于理解为什么返回一个普通go.Figure就能被前端渲染成可交互地图。核心实现在 gradio/components/plot.py:

  • 组件声明其数据模型为PlotData(plot.py),其字段为type(取值限定为"altair" | "bokeh" | "plotly" | "matplotlib")与plot(图内容的序列化字符串);
  • 函数返回值会经过postprocess(plot.py)处理:通过检测对象所属模块("bokeh""altair""matplotlib"等关键字)自动判别图类型,再各自序列化。对 Plotly 图走value.to_json()分支,将 Figure 序列化为 JSON 字符串装入PlotData
  • 前端拿到PlotData后依据type="plotly"使用 Plotly 运行时还原交互图形,因此 Scattermapbox 的缩放、拖拽、hover 行为在浏览器中原生生效;
  • 组件文档(plot.py)同时注明 Plot 几乎不充当输入组件,本 demo 中它仅作输出使用,符合组件定位。

换句话说,filter_map每次返回的新go.Figure都会在postprocess中被转成 Plotly JSON 推送到前端替换旧图,由此实现"改参数 → 重筛数据 → 重绘地图"的完整交互闭环。

九、总结与扩展思路

整个 demo 呈现了一条非常典型的 Gradio 数据可视化应用范式:真实外部数据集 + pandas 过滤函数 + Plotly 地图 + Blocks 组件编排 + 双事件刷新。示例展示的是 Airbnb 房源分布,但同一套骨架可平移复用到房价分布、POI 分布、订单地理可视化等任何带经纬度字段的 Plotly 地图场景。

若基于本 demo 做进一步扩展,可优先考虑的方向包括:为gr.Number设置minimum/maximum约束输入合法性;增加"全部行政区"快捷选项或在行政区全不选时给出提示;用gr.Slider替代Number提供更顺滑的价格区间操作;或在go.Scattermapbox中用marker.color按价格给气泡着色,将价格维度从 hover 文本升级为视觉编码。这些改造都只需要在 run.py 的组件定义与filter_map内部增量完成,不需要改动组件架构,足见该模板的扩展弹性。

【免费下载链接】gradioBuild and share delightful machine learning apps, all in Python. 🌟 Star to support our work!项目地址: https://gitcode.com/GitHub_Trending/gr/gradio

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 23:25:47

FineBI零基础实战:从数据连接到仪表板发布

第一次接触FineBI是在一个做零售数据分析的项目里&#xff0c;当时客户要求把销售、库存、会员三块数据整合到一个看板上&#xff0c;业务部门提的需求一周能改八回。技术同事被缠得没脾气&#xff0c;后来索性上了FineBI&#xff0c;把数据准备做完之后&#xff0c;业务自己拖…

作者头像 李华
网站建设 2026/9/9 23:23:14

Seata XA模式实战:订单库存跨库强一致,从原理到踩坑全解析

如果你手头也有一个“下单成功但库存没扣”、“库存扣了但订单失败”这种跨库数据不一致的问题&#xff0c;那你已经站在分布式事务的门槛上了。这篇文章聊的 Seata XA 模式&#xff0c;是我在一个电商后端项目里实际落地过的方案&#xff0c;用在两个 MySQL 库之间做订单和库存…

作者头像 李华
网站建设 2026/9/9 23:22:52

Unity C# Socket实战:从零搭建TCP多人联机合作生存游戏

如果你现在打开一份 Unity 游戏开发岗位的面试简历&#xff0c;会发现不少候选人写着“熟悉 TCP/IP”“了解 Socket 网络编程”。但面试官追问一句“你用 Socket 做过什么完整项目”&#xff0c;很多人只能答出“写过回显 Demo”。更尴尬的是&#xff0c;那个 Demo 在现场跑不起…

作者头像 李华
网站建设 2026/9/9 23:22:06

Java实现IEC 61850:从SCL模型到Client/Server实战指南

简介&#xff1a;面向电力自动化领域Java开发者的IEC 61850协议实现资源&#xff0c;解决Java端与智能电子设备&#xff08;IED&#xff09;通信、MMS报文处理及GOOSE/SV实时数据交换等核心问题。资源共427个文件&#xff0c;以260个Java源码文件为主&#xff0c;涵盖逻辑节点、…

作者头像 李华