【问题标题】:Bokeh: Display local file in DataTable widget散景:在 DataTable 小部件中显示本地文件
【发布时间】:2018-04-10 11:45:58
【问题描述】:

我希望用户能够上传 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))

【问题讨论】:

    标签: javascript python datatable bokeh


    【解决方案1】:

    这对我有用(Python 3.6 和散景 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 问题作为我的代码的基础。

    【讨论】:

    • 有了这个建议,我创建了单独的 file_source 和 table_source 对象,并且使用 table_source.data = table_source.from_df(df) 行可以让它工作。谢谢!
    猜你喜欢
    • 2018-05-11
    • 2020-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多