Bokeh CustomJS回调无法更新DataFrame字段如何解决?
问题根因
你的distance字段无法动态更新,是几个明确的代码错误导致的:
- 回调绑定对象错误:当前你把CustomJS回调绑定在
source.js_on_change('data', callback)上,只有整个ColumnDataSource的data对象被整体替换时才会触发执行。TextInput输入值变化不会触发这个事件,所以你的距离计算逻辑根本没有运行过。 - JS循环逻辑bug:遍历数组时索引起始值设为1,会漏掉第一条数据;调用距离计算函数时,y坐标没有取当前遍历的索引值,传入的是整个y数组而非单条数据的y值,计算逻辑本身无效。
- 类型不匹配:TextInput的
value返回值是字符串类型,直接传入数值计算函数会得到NaN结果,没有做类型转换。 - 初始值缺失:两个TextInput没有设置默认值,页面刚加载时输入框为空,即使触发回调也拿不到有效坐标。
修正后的完整代码
import numpy as np import pandas as pd from bokeh.plotting import figure from bokeh.models import ColumnDataSource, CustomJS, TextInput import math from bokeh.layouts import column, row from bokeh.io import show # 调整测试数据量级和经纬度匹配,避免点挤在角落 df = pd.DataFrame(np.random.rand(100,2)*10 + [30,70],columns=list('Xy')) def distance(origin, destination): lat1, lon1 = origin lat2, lon2 = destination radius = 6371 # km dlat = math.radians(lat2 - lat1) dlon = math.radians(lon2 - lon1) a = (math.sin(dlat / 2) * math.sin(dlat / 2) + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(dlon / 2) * math.sin(dlon / 2)) c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) d = radius * c return d cord_1, cord_2 = 30.56289,70.38185 df['distance'] = [distance((cord_1,cord_2),(df['X'][i],df['y'][i])) for i in range(0,len(df['X']))] source=ColumnDataSource(df) # 给输入框设置初始默认值,和初始计算逻辑匹配 cord_x = TextInput(title="X-Coordinates", value=str(cord_1)) cord_y = TextInput(title="Y-Coordinates", value=str(cord_2)) TOOLTIPS = [ ('Distance','@distance') ] p = figure(title='Sample Distance',width = 800, height = 600,tooltips = TOOLTIPS) p.circle(x='X',y='y',size = 10,source=source) callback = CustomJS(args=dict(source=source, cord_x=cord_x, cord_y=cord_y), code=""" function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) { var R = 6371; var dLat = deg2rad(lat2-lat1); var dLon = deg2rad(lon2-lon1); var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2) ; var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); var d = R * c; return d; } function deg2rad(deg) { return deg * (Math.PI/180) } var data = source.data; // 字符串转数值,加空值兜底避免计算出NaN var A = parseFloat(cord_x.value) || 30.56289; var B = parseFloat(cord_y.value) || 70.38185; // 从索引0开始遍历所有数据,修正y坐标取值逻辑 for(var i = 0; i < data['X'].length ; i++) { data['distance'][i] = getDistanceFromLatLonInKm(A,B,data['X'][i],data['y'][i]); } source.change.emit(); """) # 回调绑定到输入框的值变更事件,输入变化就触发重算 cord_x.js_on_change('value', callback) cord_y.js_on_change('value', callback) layout = row( p, column(cord_x, cord_y), ) show(layout)
关键修正点
- 回调绑定目标改为两个TextInput的
value变更事件,只要输入内容修改就会自动重算所有点的距离 - 修正循环索引和参数错误,覆盖所有数据点,传入正确的单条坐标值
- 增加字符串转数值逻辑和空值兜底,避免无效计算结果
- 补充输入框初始值,打开页面时显示的距离和初始坐标匹配
- 调整测试数据范围,和经纬度量级一致,可视化效果正常
内容的提问来源于stack exchange,提问作者user19295781
相关产品推荐
相关产品推荐

