Bokeh:根据通过DateSlider过滤的日期更新地图

2024-09-24 00:34:21 发布

您现在位置:Python中文网/ 问答频道 /正文

这是我的代码:

import pandas as pd
import numpy as np

from bokeh.models import *
from bokeh.plotting import *
from bokeh.io import *
from bokeh.tile_providers import *
from bokeh.palettes import *
from bokeh.transform import *
from bokeh.layouts import *

radius_scale = 100
df  = pd.DataFrame({'date': ['2009-01-01', '2009-01-02', '2009-01-03', '2009-01-04', '2009-01-05', '2009-01-01', '2009-01-02', '2009-01-03', '2009-01-04', '2009-01-05','2009-01-01', '2009-01-02', '2009-01-03', '2009-01-04', '2009-01-05'],
                   'state': ['Melaka', 'Melaka', 'Melaka', 'Melaka', 'Melaka', 'Perak', 'Perak', 'Perak', 'Perak', 'Perak', 'Kuala Lumpur', 'Kuala Lumpur', 'Kuala Lumpur', 'Kuala Lumpur', 'Kuala Lumpur'],
                   'tourists': [100, 121, 235, 197, 390, 57, 49, 81, 73, 183, 351, 490, 618, 438, 557]})

df['longitude'] = df['state'].map({'Melaka': 102.2464615, 'Perak': 101.0314453, 'Kuala Lumpur': 101.6869,})
df['latitude']  = df['state'].map({'Melaka': 2.206414407, 'Perak': 4.01185976, 'Kuala Lumpur': 3.1390,})

df['date'] = df['date'].astype('datetime64[ns]')
df['tourists_plot'] = df['tourists'] * radius_scale

# Mercator units conversion
def wgs84_to_web_mercator(df, lon, lat):
    """Converts decimal longitude/latitude to Web Mercator format"""
    k = 6378137
    df["x"] = df[lon] * (k * np.pi/180.0)
    df["y"] = np.log(np.tan((90 + df[lat]) * np.pi/360.0)) * k
    return df
df = wgs84_to_web_mercator(df, 'longitude', 'latitude')

source = ColumnDataSource(df)

# Map zoom scale
scale = 2500
x = df['x']
y = df['y']

# Centers map
x_min = int(x.mean() - (scale * 1))
x_max = int(x.mean() + (scale * 1))
y_min = int(y.mean() - (scale * 350))
y_max = int(y.mean() + (scale * 350))

# Prepare Bokeh
plot = figure(
    title = 'Malaysia Tourism',
    match_aspect = True,
    tools = 'wheel_zoom, pan, reset, save',
    x_range = (x_min, x_max), y_range = (y_min, y_max),
    x_axis_type = 'mercator', y_axis_type = 'mercator',
    width = 900
    )
plot.grid.visible = True
# Get map
map = plot.add_tile(get_provider(OSM))
map.level = 'underlay'

plot.xaxis.visible = False
plot.yaxis.visible = False
plot.title.text_font_size = "20px"

def bubble_map(plot, df_source, radius_col_plot, radius_col, state, color='orange', leg_label='Bubble Map'):
    source = df_source
    c = plot.circle(x = 'x', y = 'y', color = color, source = source, size = 1, fill_alpha = 0.4, radius = radius_col_plot,\
                    legend_label = leg_label, hover_color = 'red')

    tip_label = '@' + radius_col
    state_label = '@' + state

    circle_hover = HoverTool(tooltips = [("Percentage " + leg_label, tip_label + "%"), ('State', state_label)], mode = 'mouse',\
                             point_policy = 'follow_mouse', renderers = [c])
    circle_hover.renderers.append(c)
    
    plot.tools.append(circle_hover)
    plot.legend.location = "top_right"
    plot.legend.click_policy = "hide"

bubble_map(plot = plot,
           df_source = source, radius_col_plot = 'tourists_plot', radius_col = 'tourists', leg_label = 'Tourists',
           state = 'state', color = 'blue')

date_slider = DateSlider(start = min(df.date), end = max(df.date), value = min(df.date), step = 1, title = "Date")
def update_plot(attr, old, new):
    datesel = new
    new_data = df[df['date'] == datesel]
    source.data.update(ColumnDataSource(new_data).data)
date_slider.on_change('value', update_plot)

# show(column(date_slider, plot))
curdoc().add_root(column(date_slider, plot))

简而言之,我试图绘制的是地图上每个州的df['tourists']每天的增长。我试图通过DateSlider小部件date_slider启用数据过滤,该小部件根据所选日期过滤行

但是滑块对我不起作用。我也没有得到任何错误,在这些错误中,我不知道要调试什么

这是我在选择之前得到的,所有5天的数据都杂乱无章:

Bokeh before DateSlider

与滑块帮助不同,所有点在滑动时消失:

Bokeh after DateSlider

请告知


Tags: fromimportmapsourcedfdateplotbokeh
1条回答
网友
1楼 · 发布于 2024-09-24 00:34:21

DateSlider以毫秒为单位返回一个时间戳,用于过滤新的数据帧。由于新日期的格式不是YYYY-MM-DD,因此数据框显示为空

转换从DateSlider接收的时间戳,它应该可以工作:

def update_plot(attr, old, new):
    datesel = datetime.fromtimestamp(new / 1000).strftime('%Y-%m-%d')
    new_data = df[df['date'] == datesel]
    source.data.update(ColumnDataSource(new_data).data)

由于时间戳以毫秒为单位,datetime需要秒,因此必须将其除以1000才能转换为秒

另一个建议是将步骤从1更改为86400000,以便能够在日期之间(一天中的毫秒)更轻松地进行滑动选择

date_slider = DateSlider(start = min(df.date), end = max(df.date), value = min(df.date), step = 86400000, title = "Date")

相关问题 更多 >