我想在散景应用中点击上传按钮(代码来自here)时返回data.frame对象。下面是代码,
## Load library
import pandas as pd
from xlrd import XLRDError
import io
import base64
from bokeh.layouts import row, column, widgetbox, layout
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import Button, Div, TextInput, DataTable, TableColumn, Panel, Tabs
from bokeh.io import curdoc
from bokeh.plotting import figure
sheet_names_expected = ['Sheet'+str(i) for i in range(1,7)]
## Uploaded Data Handling
file_source = ColumnDataSource({'file_contents':[], 'file_name':[], 'upload_message':[]})
def file_callback(attr,old,new):
print('filename:', file_source.data['file_name'])
#print('filename:', type(file_source.data['file_contents'][0]))
raw_contents = file_source.data['file_contents'][0]
prefix, b64_contents = raw_contents.split(",", 1)
file_contents = base64.b64decode(b64_contents)
file_io = io.BytesIO(file_contents)
try:
df_dict = pd.read_excel(file_io, sheet_name = sheet_names_expected)
file_source.data['upload_message'] = 'Successfully loaded data'
except XLRDError:
file_source.data['upload_message'] = 'One or more of the sheet names are mis-spelled/missing.\n'
except :
file_source.data['upload_message'] = 'Error occured while uploading the file. Ensure it\'s a xlsx file'
## How to now have the 6 dataframes within `df_dict` outside the callback function for further analysis and plotting ?
file_source.on_change('data', file_callback)
###########################################################################
## Upload Button Widget
###########################################################################
button = Button(label="Upload Data", button_type="success")
# when butotn is clicked, below code in CustomJS will be called
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();
""")
不确定如何使用ColumnDataSource。 excel_file有6个选项卡,因此6个pandas data.frames将被pd.read_excel()
答案 0 :(得分:1)
如果我理解你的问题,那么我认为你对散景的运作方式有些困惑。我建议您仔细阅读文档,并了解库中的一些示例如何工作。
从不如此,我相信你需要使用ColumnDataSource来实现你想要的。首先,您可以读取数据,然后将其加载到一个或多个columndatasources中。然后当您按下按钮时,它将执行回调函数中的所有代码。 (请阅读https://bokeh.pydata.org/en/latest/docs/user_guide/data.html#columndatasource)
此处的回调函数执行以下操作:
1:访问现有列源数据
2:运行其他python函数然后
3:更新存储在CDS中的现有数据。这将自动更新图表。
如果您不使用列数据源,则无法在不重新创建绘图的情况下更新绘图并更新显示在其中的html文档。
from os.path import dirname, abspath
import pandas as pd
from bokeh.layouts import row
from bokeh.plotting import figure
from bokeh.plotting import curdoc
from bokeh.models.widgets import Button
from bokeh.models import ColumnDataSource
button = Button(label='Click')
# replace this with file i/o
df = pd.DataFrame({'x':[1,2,3,4],'y':[10,20,30,40]})
# create a column data source from the read in data
# source.data = dictionary containing the data
# using a CDS will allow any plots or tables
# to automatically update when the source.data
# is modified
source = ColumnDataSource(df)
def mycalc(x):
"""
Arbitary python function
"""
x *= 2
return x
def callback():
x = source.data['x']
# compute python based functions here to manipulate data
x = mycalc(x)
# update the column data source with the new data
source.data['x'] = x
p = figure(plot_width=400, plot_height=400)
p.circle('x','y', source=source)
button.on_click(callback)
curdoc().add_root(row([button, p]))