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