代码无法识别 ColumnDataSource 的本质
Code not recognizing ColumnDataSource for what it is
我想根据用户从下拉菜单中选择的内容更改简单线图的数据源。
我有2个dataframes,我和我男朋友的体重和年龄。
my_weight = [60,65,70]
my_age = [21,22,25]
d_weight = [65,70,80]
d_age = [21,22,25]
me = pd.DataFrame(list(zip(my_weight, my_age)),
columns =['weight', 'age'], index=None)
dillon = pd.DataFrame(list(zip(d_weight, d_age)),
columns =['weight', 'age'], index=None)
我将这两个数据框转换为 ColumnDataSource 对象,创建我的绘图和线条,添加我的下拉列表和 jslink。还有一个演示滑块来展示如何更改我的行的 line_width。
from bokeh.models import ColumnDataSource
from bokeh.core.properties import Any, Bool, ColumnData
pn.extension()
source = ColumnDataSource(me, name="Me")
source2 = ColumnDataSource(dillon, name="Dillon")
# print("Me: ", source.data, "Dillon: ", source2.data)
plot = figure(width=300, height=300)
myline = plot.line(x='weight', y='age', source=source, color="pink")
width_slider = pn.widgets.FloatSlider(name='Line Width', start=0.1, end=10)
width_slider.jslink(myline.glyph, value='line_width')
dropdown2 = pn.widgets.Select(name='Data', options=[source, source2])
dropdown2.jslink(myline, value='data_source')
pn.Column(dropdown2, width_slider, plot)
当我 运行 这段代码时,我得到错误
ValueError: expected an instance of type DataSource, got ColumnDataSource(id='5489', ...) of type str
错误发生在代码的 dropdown2
部分。
是什么阻止了代码将 source 和 source2 识别为 ColumnDataSource() 对象?
got ColumnDataSource(id='5489', ...) of type str 是什么意思?怎么是字符串?
我不能真正谈论 Bokeh 之上的面板抽象,但值得一提的是,实际的 Bokeh 滑块不能有复杂的东西,例如 ColumnDataSource
作为 options
值。只有简单的类型,如数字、字符串等。碰巧的是:
ColumnDataSource(id='5489', ...)
是 CDS 的 "repr",即如果您尝试打印生成的字符串的 CDS。所以我的猜测是 Panel 在某处将 CDS 转换为其字符串表示形式,并将该字符串传递给底层的 Bokeh Select
小部件。那当然不会做你想要的。
可能有更好的特定于面板的方法来做事,但一种方法是获取底层的 Bokeh Select
小部件,然后调用 js_on_change
添加 CustomJS
回调更新数据。您可以在文档的 JavaScript Callbacks 章节中模拟很多示例。
这里有多个问题。首先是 Select
小部件实际上并没有使复杂对象在 Javascript 中可用,因此尝试在 JS 回调中访问这些模型的回调将不起作用。因此,唯一的解决方案是编写一个实际的 JS 回调并提供实际模型 args
。这里的次要问题是两个数据源包含不同的列,具体来说 'Me' ColumnDataSource
包含一个年龄列,而 'Dillon' 数据源包含一个身高列。这意味着您还需要更新字形以查看这些不同的来源。实际上这看起来像这样:
import panel as pn
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, DataRange1d
from bokeh.core.properties import Any, Bool, ColumnData
source = ColumnDataSource(me, name="Me")
source2 = ColumnDataSource(dillon, name="Dillon")
plot = figure(width=300, height=300)
myline = plot.line(x='weight', y='age', source=source, color="pink")
width_slider = pn.widgets.FloatSlider(name='Line Width', start=1, end=10)
width_slider.jslink(myline.glyph, value='line_width')
dropdown2 = pn.widgets.Select(name='Data', options={'Me': source, 'Dillon': source2})
code = """
if (cb_obj.value == 'Me') {
myline.data_source = source
myline.glyph.y = {'field': 'age'}
} else {
myline.data_source = source2
myline.glyph.y = {'field': 'height'}
}
"""
dropdown2.jscallback(args={'myline': myline, 'source': source, 'source2': source2}, value=code)
也就是说,我建议在 Panel 中实现它的方式是这样的:
dropdown2 = pn.widgets.Select(name='Data', options={'Me': me, 'Dillon': dillon}, value=me)
width_slider = pn.widgets.FloatSlider(name='Line Width', start=1, end=10)
@pn.depends(dropdown2)
def plot(data):
source = ColumnDataSource(data)
plot = figure(width=300, height=300)
column = 'age' if 'age' in source.data else 'height'
myline = plot.line(x='weight', y=column, source=source, color="pink")
width_slider.jslink(myline.glyph, value='line_width')
return plot
pn.Column(dropdown2, width_slider, plot).embed()
最后,如果您愿意 hvPlot 试一试,可以进一步简化为:
import hvplot.pandas
dropdown2 = pn.widgets.Select(name='Data', options={'Me': me, 'Dillon': dillon}, value=me)
width_slider = pn.widgets.FloatSlider(name='Line Width', start=1, end=10)
@pn.depends(dropdown2)
def plot(data):
p = data.hvplot('weight', color='pink')
width_slider.jslink(p, value='glyph.line_width')
return p
pn.Column(dropdown2, width_slider, plot).embed()
我想根据用户从下拉菜单中选择的内容更改简单线图的数据源。
我有2个dataframes,我和我男朋友的体重和年龄。
my_weight = [60,65,70]
my_age = [21,22,25]
d_weight = [65,70,80]
d_age = [21,22,25]
me = pd.DataFrame(list(zip(my_weight, my_age)),
columns =['weight', 'age'], index=None)
dillon = pd.DataFrame(list(zip(d_weight, d_age)),
columns =['weight', 'age'], index=None)
我将这两个数据框转换为 ColumnDataSource 对象,创建我的绘图和线条,添加我的下拉列表和 jslink。还有一个演示滑块来展示如何更改我的行的 line_width。
from bokeh.models import ColumnDataSource
from bokeh.core.properties import Any, Bool, ColumnData
pn.extension()
source = ColumnDataSource(me, name="Me")
source2 = ColumnDataSource(dillon, name="Dillon")
# print("Me: ", source.data, "Dillon: ", source2.data)
plot = figure(width=300, height=300)
myline = plot.line(x='weight', y='age', source=source, color="pink")
width_slider = pn.widgets.FloatSlider(name='Line Width', start=0.1, end=10)
width_slider.jslink(myline.glyph, value='line_width')
dropdown2 = pn.widgets.Select(name='Data', options=[source, source2])
dropdown2.jslink(myline, value='data_source')
pn.Column(dropdown2, width_slider, plot)
当我 运行 这段代码时,我得到错误
ValueError: expected an instance of type DataSource, got ColumnDataSource(id='5489', ...) of type str
错误发生在代码的 dropdown2
部分。
是什么阻止了代码将 source 和 source2 识别为 ColumnDataSource() 对象? got ColumnDataSource(id='5489', ...) of type str 是什么意思?怎么是字符串?
我不能真正谈论 Bokeh 之上的面板抽象,但值得一提的是,实际的 Bokeh 滑块不能有复杂的东西,例如 ColumnDataSource
作为 options
值。只有简单的类型,如数字、字符串等。碰巧的是:
ColumnDataSource(id='5489', ...)
是 CDS 的 "repr",即如果您尝试打印生成的字符串的 CDS。所以我的猜测是 Panel 在某处将 CDS 转换为其字符串表示形式,并将该字符串传递给底层的 Bokeh Select
小部件。那当然不会做你想要的。
可能有更好的特定于面板的方法来做事,但一种方法是获取底层的 Bokeh Select
小部件,然后调用 js_on_change
添加 CustomJS
回调更新数据。您可以在文档的 JavaScript Callbacks 章节中模拟很多示例。
这里有多个问题。首先是 Select
小部件实际上并没有使复杂对象在 Javascript 中可用,因此尝试在 JS 回调中访问这些模型的回调将不起作用。因此,唯一的解决方案是编写一个实际的 JS 回调并提供实际模型 args
。这里的次要问题是两个数据源包含不同的列,具体来说 'Me' ColumnDataSource
包含一个年龄列,而 'Dillon' 数据源包含一个身高列。这意味着您还需要更新字形以查看这些不同的来源。实际上这看起来像这样:
import panel as pn
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, DataRange1d
from bokeh.core.properties import Any, Bool, ColumnData
source = ColumnDataSource(me, name="Me")
source2 = ColumnDataSource(dillon, name="Dillon")
plot = figure(width=300, height=300)
myline = plot.line(x='weight', y='age', source=source, color="pink")
width_slider = pn.widgets.FloatSlider(name='Line Width', start=1, end=10)
width_slider.jslink(myline.glyph, value='line_width')
dropdown2 = pn.widgets.Select(name='Data', options={'Me': source, 'Dillon': source2})
code = """
if (cb_obj.value == 'Me') {
myline.data_source = source
myline.glyph.y = {'field': 'age'}
} else {
myline.data_source = source2
myline.glyph.y = {'field': 'height'}
}
"""
dropdown2.jscallback(args={'myline': myline, 'source': source, 'source2': source2}, value=code)
也就是说,我建议在 Panel 中实现它的方式是这样的:
dropdown2 = pn.widgets.Select(name='Data', options={'Me': me, 'Dillon': dillon}, value=me)
width_slider = pn.widgets.FloatSlider(name='Line Width', start=1, end=10)
@pn.depends(dropdown2)
def plot(data):
source = ColumnDataSource(data)
plot = figure(width=300, height=300)
column = 'age' if 'age' in source.data else 'height'
myline = plot.line(x='weight', y=column, source=source, color="pink")
width_slider.jslink(myline.glyph, value='line_width')
return plot
pn.Column(dropdown2, width_slider, plot).embed()
最后,如果您愿意 hvPlot 试一试,可以进一步简化为:
import hvplot.pandas
dropdown2 = pn.widgets.Select(name='Data', options={'Me': me, 'Dillon': dillon}, value=me)
width_slider = pn.widgets.FloatSlider(name='Line Width', start=1, end=10)
@pn.depends(dropdown2)
def plot(data):
p = data.hvplot('weight', color='pink')
width_slider.jslink(p, value='glyph.line_width')
return p
pn.Column(dropdown2, width_slider, plot).embed()