散景:在DataTable小部件中显示本地文件

时间:2018-04-10 11:45:58

标签: 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))

1 个答案:

答案 0 :(得分: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问题,作为我的代码的基础。