有没有办法保存散景数据表内容

Posted

技术标签:

【中文标题】有没有办法保存散景数据表内容【英文标题】:is there a way to save bokeh data table content 【发布时间】:2015-10-27 17:37:20 【问题描述】:

我正在尝试使用bokeh data table 在网页中显示数据embedded。它工作得很好。

有没有办法从显示的数据表中保存表内容?其他散景图具有用于各种功能的工具栏,包括保存,但 DataTable 似乎没有附带。我对散景数据表使用的 javascript 或 slickgrid 知之甚少。并且想知道是否可以做到。

谢谢!

编辑 - 我原来的问题似乎不够清楚。希望以下图片能帮助说明:

散景图有相关的工具栏:

但数据表默认没有,也不带'tools'参数:

是否可以在数据表中添加“保存”按钮,以便查看表的人可以下载制表符分隔或 csv 文件?不一定要长得一样,但保存功能要一样。

【问题讨论】:

【参考方案1】:

如果 bokeh 提供一个工具按钮来保存/导出数据表到 csv / txt / excel 文件,那就太好了。如果它已经存在,我还没有在文档中找到它。

同时,一个可能的答案是使用本机 javascript 将 js 数组(位于散景数据表下方)导出到 CSV。它已被描述为here 和here。

添加:散景具有使用 js 的回调。一个简单的描述是here。仍在阅读它...

编辑:它可能已经存在了一段时间,但我刚刚注意到 Bokeh 网站上的 saving csv from data table 示例。

【讨论】:

【参考方案2】:

2021 年更新:调整后的代码适用于 python 3.8 和 bokeh 2.2.3

对于那些难以调整或在散景网站上查找示例或只是非常懒惰的人,下面的代码可以做最少的工作:

from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import Button
from bokeh.io import show
import os

source = ColumnDataSource('list1':[0,1,2,3],'list2':[4,5,6,7])
button = Button(label="Download", button_type="success")
button.js_on_click(CustomJS(args=dict(source=source),code=open(os.path.join(os.path.dirname(__file__),"download.js")).read()))
show(button)

还有文件download.js:

function table_to_csv(source) 
    const columns = Object.keys(source.data)
    const nrows = source.get_length()
    const lines = [columns.join(',')]

    for (let i = 0; i < nrows; i++) 
        let row = [];
        for (let j = 0; j < columns.length; j++) 
            const column = columns[j]
            row.push(source.data[column][i].toString())
        
        lines.push(row.join(','))
    
    return lines.join('\n').concat('\n')



const filename = 'data_result.csv'
const filetext = table_to_csv(source)
const blob = new Blob([filetext],  type: 'text/csv;charset=utf-8;' )

//addresses IE
if (navigator.msSaveBlob) 
    navigator.msSaveBlob(blob, filename)
 else 
    const link = document.createElement('a')
    link.href = URL.createObjectURL(blob)
    link.download = filename
    link.target = '_blank'
    link.style.visibility = 'hidden'
    link.dispatchEvent(new MouseEvent('click'))

【讨论】:

谢谢,这很有帮助。出于某种原因,我无法使 Bokeh 网站上的示例正常工作。 这段代码的问题是Object.keys 检索到的列与表中的列顺序不同,而是始终按字母顺序检索。执行source.data 时,Python 中不会出现此问题。 @pablo,问题不在于列的顺序,而在于创建下载按钮。如果对您来说,要求列的顺序与数据框中的顺序相同,请创建一个新问题。或者,如果您已经知道如何操作,只需自己添加即可。【参考方案3】:

与我对this *** question 的回复有关。回复复制如下:

这是一个使用 Python 3.7.5 和 Bokeh 1.4.0 的工作示例

此 jupyter 笔记本的公共 github 链接:https://github.com/surfaceowl-ai/python_visualizations/blob/master/notebooks/bokeh_save_linked_plot_data.ipynb

环境报告:

虚拟环境 python 版本:Python 3.7.5 虚拟环境 ipython 版本:7.9.0

水印包报告:

散景 1.4.0 jupyter 1.0.0 numpy 1.17.4 熊猫 0.25.3 崛起 5.6.0 水印 2.0.2

# Generate linked plots + TABLE displaying data + save button to export cvs of selected data

from random import random

from bokeh.io import output_notebook  # prevent opening separate tab with graph
from bokeh.io import show

from bokeh.layouts import row
from bokeh.layouts import grid
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.models import Button  # for saving data
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
from bokeh.models import HoverTool
from bokeh.plotting import figure


# create data
x = [random() for x in range(500)]
y = [random() for y in range(500)]

# create first subplot
plot_width = 400
plot_height = 400

s1 = ColumnDataSource(data=dict(x=x, y=y))
fig01 = figure(
    plot_width=plot_width,
    plot_height=plot_height,
    tools=["lasso_select", "reset", "save"],
    title="Select Here",
)
fig01.circle("x", "y", source=s1, alpha=0.6)

# create second subplot
s2 = ColumnDataSource(data=dict(x=[], y=[]))

# demo smart error msg:  `box_zoom`, vs `BoxZoomTool`
fig02 = figure(
    plot_width=400,
    plot_height=400,
    x_range=(0, 1),
    y_range=(0, 1),
    tools=["box_zoom", "wheel_zoom", "reset", "save"],
    title="Watch Here",
)

fig02.circle("x", "y", source=s2, alpha=0.6, color="firebrick")

# create dynamic table of selected points
columns = [
    TableColumn(field="x", title="X axis"),
    TableColumn(field="y", title="Y axis"),
]

table = DataTable(
    source=s2,
    columns=columns,
    width=400,
    height=600,
    sortable=True,
    selectable=True,
    editable=True,
)

# fancy javascript to link subplots
# js pushes selected points into ColumnDataSource of 2nd plot
# inspiration for this from a few sources:
# credit: https://***.com/users/1097752/iolsmit via: https://***.com/questions/48982260/bokeh-lasso-select-to-table-update
# credit: https://***.com/users/8412027/joris via: https://***.com/questions/34164587/get-selected-data-contained-within-box-select-tool-in-bokeh

s1.selected.js_on_change(
    "indices",
    CustomJS(
        args=dict(s1=s1, s2=s2, table=table),
        code="""
        var inds = cb_obj.indices;
        var d1 = s1.data;
        var d2 = s2.data;
        d2['x'] = []
        d2['y'] = []
        for (var i = 0; i < inds.length; i++) 
            d2['x'].push(d1['x'][inds[i]])
            d2['y'].push(d1['y'][inds[i]])
        
        s2.change.emit();
        table.change.emit();

        var inds = source_data.selected.indices;
        var data = source_data.data;
        var out = "x, y\\n";
        for (i = 0; i < inds.length; i++) 
            out += data['x'][inds[i]] + "," + data['y'][inds[i]] + "\\n";
        
        var file = new Blob([out], type: 'text/plain');

    """,
    ),
)

# create save button - saves selected datapoints to text file onbutton
# inspriation for this code:
# credit:  https://***.com/questions/31824124/is-there-a-way-to-save-bokeh-data-table-content
# note: savebutton line `var out = "x, y\\n";` defines the header of the exported file, helpful to have a header for downstream processing

savebutton = Button(label="Save", button_type="success")
savebutton.callback = CustomJS(
    args=dict(source_data=s1),
    code="""
        var inds = source_data.selected.indices;
        var data = source_data.data;
        var out = "x, y\\n";
        for (i = 0; i < inds.length; i++) 
            out += data['x'][inds[i]] + "," + data['y'][inds[i]] + "\\n";
        
        var file = new Blob([out], type: 'text/plain');
        var elem = window.document.createElement('a');
        elem.href = window.URL.createObjectURL(file);
        elem.download = 'selected-data.txt';
        document.body.appendChild(elem);
        elem.click();
        document.body.removeChild(elem);
        """,
)

# add Hover tool
# define what is displayed in the tooltip
tooltips = [
    ("X:", "@x"),
    ("Y:", "@y"),
    ("static text", "static text"),
]

fig02.add_tools(HoverTool(tooltips=tooltips))

# display results
# demo linked plots
# demo zooms and reset
# demo hover tool
# demo table
# demo save selected results to file

layout = grid([fig01, fig02, table, savebutton], ncols=3)

output_notebook()
show(layout)

# things to try:
# select random shape of blue dots with lasso tool in 'Select Here' graph
# only selected points appear as red dots in 'Watch Here' graph -- try zooming, saving that graph separately
# selected points also appear in the table, which is sortable
# click the 'Save' button to export a csv

# TODO:  export from Bokeh to pandas dataframe

【讨论】:

以上是关于有没有办法保存散景数据表内容的主要内容,如果未能解决你的问题,请参考以下文章

使用散景绘图与kafka流媒体

散景自定义保存工具

散景图像绘图与紧轴和匹配的纵横比

r 散景图图例 - 位置不佳移动

散景折线图未绘制完整的熊猫数据框

嵌入式散景数据表未在 Django 中显示