使用RangeSlider在大熊猫数据框中选择列以交互方式更改bokeh中的点图

问题描述 投票:1回答:1

我有一个熊猫数据帧df,其中前两列代表x,y坐标,其余列代表时间片(t0,... tn),其中每个时间点每个点的存在(1)或不存在(0)切片(ti)被记录。

我想使用RangeSlider(而不是Slider),以便我可以跨越一定范围的时间片并绘制出该范围内的点。

这是我到目前为止所得到的,

from bokeh.layouts import column
from bokeh.plotting import figure, show
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.models.widgets import RangeSlider

# pts is a dataframe with columns (x, y, t0, t1,...t19)
src = ColumnDataSource(data = pts) 

p = figure(plot_height = 500)
p.circle(source= src, x='x', y= 'y', size=2, color="navy", alpha=0.1)

callback = CustomJS( args = dict(source = src), code="""

    var data = source.data;
    // changed ti range
    var ti_start = cb.obj.value[0] + 2 //offset 
    var ti_end = cb.obj.value[1] + 2

    // change data (how to select columns???????) 
    data = data[ti_start:ti_end]

    source.change.emit()
""")
ti_slider = RangeSlider(start=0, end=19, value=(1,2), step=1, title="Time Period",
callback = callback)

layout = column(ti_slider, p)
show(layout)

以上代码根本不起作用。将绘制点并显示RangeSlider,但是当我更改范围或滑行时什么也没有发生。我无法限制构成数据源(即数据框)的列。我尝试更改选择列的代码,但我不知道任何JavaScript。

这是我第一次尝试将CustomJS功能用于散景。

python pandas bokeh
1个回答
0
投票

上面的代码中有很多问题:

  • cb_obj不是cb.obj
  • 您正在分配给局部变量 data,然后丢弃结果-有时需要从字面上分配给source.data
  • 要通过更新数据源来做到这一点,您将需要two数据源,因为该数据源始终具有您要提取的完整数据,而另一个数据源仅用于保存子集。如果只有一个数据源并对其进行了修改,那么您将丢弃永远无法取回的数据。
  • 因此,最好为此使用CDSView将子集表示为自身的事物
  • JS没有类似Pandas的操作,您只需要执行所有嵌套循环即可检查每一行

这是一个简化的工作示例:

from bokeh.layouts import column
from bokeh.plotting import figure, show
from bokeh.models import CustomJS, ColumnDataSource, RangeSlider, CDSView, IndexFilter

source = ColumnDataSource(data=dict(
    x=[1,2,3,4],
    y=[1,1,1,1],
    t0=[1,1,0,0],
    t1=[0,1,0,0],
    t2=[0,1,1,0],
    t3=[0,0,1,1],
    t4=[0,0,0,1],
    t5=[0,0,0,0],
))

p = figure(plot_height=500, x_range=(0,5), y_range=(0,2))

view = CDSView(source=source, filters=[IndexFilter([0, 1])])
p.circle('x', 'y', size=10, color="navy", alpha=0.8,
         source=source, view=view)

callback = CustomJS(args=dict(source=source, view=view), code="""
    const start = cb_obj.value[0]
    const end = cb_obj.value[1]
    const indices = []
    for (var i=0; i < source.get_length(); i++) {
        for (var j=start; j<=end; j++) {
            if (source.data["t" + j][i]==1) {
                indices.push(i)
                break
            }
        }
    }
    console.log(indices)
    view.indices = indices
""")

ti_slider = RangeSlider(start=0, end=5, value=(0,1), step=1, title="Time Period")
ti_slider.js_on_change('value', callback)

show(column(ti_slider, p))

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.