javascript - Bokeh:在 DataTable 小部件中显示本地文件

标签 javascript python datatable bokeh

我希望用户能够上传一个 csv 文件,然后在 Bokeh 中对其进行处理和可视化。输入文件有三列(a、b 和 c),我想读取其中两列以供显示。

我从 https://github.com/bokeh/bokeh/issues/6096 复制了一些东西获取 Javascript 输入按钮。

现在我的输入文件显示在控制台中,但我不知道如何将它放入 DataTable 小部件中。我需要写一个更新函数还是什么?感谢您的帮助!

import pandas as pd
from bokeh.layouts import row
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import Button, DataTable, TableColumn
from bokeh.io import curdoc
import io
import base64

file_source = ColumnDataSource(data=dict(a=[],b=[],c=[]))
def file_callback(attr,old,new):
    print ('filename:', file_source.data['file_name'])
    raw_contents = file_source.data['file_contents'][0] 
    prefix, b64_contents = raw_contents.split(",", 1)
    file_contents = base64.b64decode(b64_contents)
    file_io = io.StringIO(bytes.decode(file_contents))
    df = pd.read_csv(file_io)
    print('file contents:', df)
    return df

file_source.on_change('data', file_callback)


columns = [
    TableColumn(field="a", title="a"),
    TableColumn(field="b", title="b")
]

table = DataTable(source=file_source.data,columns=columns, width=400)

button = Button(label="Upload", button_type="success")
button.callback =     CustomJS(args=dict(file_source=file_source,table=table), code = """
function read_file(filename) {
    var reader = new FileReader();
    reader.onload = load_handler;
    reader.onerror = error_handler;
    // readAsDataURL represents the file's data as a base64 encoded string
    reader.readAsDataURL(filename);
    }

function load_handler(event) {
    var b64string = event.target.result;
    file_source.data = {'file_contents' : [b64string], 'file_name':[input.files[0].name]};
    file_source.trigger("change");
    }

function error_handler(evt) {
    if(evt.target.error.name == "NotReadableError") {
        alert("Can't read file!");
    }
}

var input = document.createElement('input');
input.setAttribute('type', 'file');
input.onchange = function(){
    if (window.FileReader) {
        read_file(input.files[0]);
    } else {
        alert('FileReader is not supported in this browser');
    }
}
input.click();
""")

curdoc().add_root(row(button,table))

最佳答案

这对我有用(Python 3.6 和 bokeh 0.12.13):

import pandas as pd
from bokeh.layouts import row
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import Button, DataTable, TableColumn
from bokeh.io import curdoc

from io import StringIO
import base64

dict1 = {
    'x':[0]*6,
    'y':[1,1,1,2,2,2]
}

table_source = ColumnDataSource(data=dict1)

columns = [
    TableColumn(field="x", title="x"),
    TableColumn(field="y", title="y")
]

data_table = DataTable(
    source=table_source, columns=columns,
    width=800, editable=True
)

file_source = ColumnDataSource({'file_contents':[], 'file_name':[]})

def file_callback(attr, old, new):
    print('filename:', file_source.data['file_name'])
    raw_contents = file_source.data['file_contents'][0]  
    # remove the prefix that JS adds  
    prefix, b64_contents = raw_contents.split(",", 1)    
    file_contents = base64.b64decode(b64_contents)
    # print("B64 decoded: file_contents : %s" % file_contents)
    # Remove byte order mask and decode to str
    file_contents = file_contents.decode("utf-8-sig")
    # print("Decoded file_contents : %s" % file_contents)
    file_io = StringIO(file_contents)
    # Separator might need to be adjusted
    df = pd.read_csv(file_io, sep=";")
    table_source.data = table_source.from_df(df)
    print("Table source data: %s" % table_source.data)

file_source.on_change('data', file_callback)

button = Button(label="Upload", button_type="success")

button.callback = CustomJS(args=dict(file_source=file_source), code = """
function read_file(filename) {
    var reader = new FileReader();
    reader.onload = load_handler;
    reader.onerror = error_handler;
    // readAsDataURL represents the file's data as a base64 encoded string
    reader.readAsDataURL(filename);
}

function load_handler(event) {
    var b64string = event.target.result;
    file_source.data = {'file_contents' : [b64string], 'file_name':[input.files[0].name]};
    file_source.trigger("change");
}

function error_handler(evt) {
    if(evt.target.error.name == "NotReadableError") {
        alert("Can't read file!");
    }
}

var input = document.createElement('input');
input.setAttribute('type', 'file');
input.onchange = function(){
    if (window.FileReader) {
        read_file(input.files[0]);
    } else {
        alert('FileReader is not supported in this browser');
    }
}
input.click();
""")

curdoc().add_root(row(button, data_table))

我创建了一个 ; 分隔的 .csv 文件。为了将它作为 DataFrame 加载,我必须在 file_callback 中添加和调整您的代码。这是因为(至少我的 .csv)包含一个 Byte Order mark我删除了:

file_contents = file_contents.decode("utf-8-sig")
df = pd.read_csv(file_io, sep=";")

然后,我为 DataTable 小部件定义了一个新的 table_source。在将其转换为 DataFrame 后,它会使用上传的 .csv 中的数据进行更新。这发生在 file_callback 中:

table_source.data = table_source.from_df(df)

这些最后的部分是你遗漏的部分。 我用了这个answer到你提到的 github 问题作为我的代码的基础。

关于javascript - Bokeh:在 DataTable 小部件中显示本地文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49752809/

相关文章:

javascript - 如何定位数据表导出按钮组?

javascript - jQuery 以编程方式选择值 - 奇怪的问题

javascript - instanceof 检查 JavaScript 工厂模式

javascript - jQuery DataTables 从 Controller 加载数据但不显示它

python - 如何在基类方法中使用派生类变量

python - 按查询集中的对象排除

r - 在 Rmd/RStudio 中与 cat 命令结合使用时不打印数据表

javascript - 如何使用事件重新加载或重新生成 jstree?

javascript - 使用reactjs分割数组值

python - 如何向 django 管理面板添加自定义 View 和操作?