【问题标题】:Bokeh - Pandas not able to read bytesIO object of excel file from JS散景 - Pandas 无法从 JS 读取 excel 文件的 bytesIO 对象
【发布时间】:2018-01-19 01:47:04
【问题描述】:

我需要您对我几天以来面临的一些挑战提供意见。

我的目标是有一个上传按钮,我从中分享.xlsx 文件和两张纸。一旦我加载这些数据并将其读入pandas DataFrame,我执行一些pythonic计算/优化代码等并得到很少的结果(表格总结)。现在,根据唯一“级别”/“组”的数量,我将创建那么多选项卡,然后在每个选项卡中显示此汇总结果。在主页中也有一个一般(通用)图。

以下是我的努力(不是我的,而是社区的:)):

1。上传按钮代码:(来自here

## Load library
###########################################################################
import pandas as pd
import numpy as np
from xlrd import XLRDError
import io
import base64
import os

from bokeh.layouts import row, column, widgetbox, layout
from bokeh.models import ColumnDataSource, CustomJS, LabelSet
from bokeh.models.widgets import Button, Div, TextInput, DataTable, TableColumn, Panel, Tabs
from bokeh.io import curdoc
from bokeh.plotting import figure
###########################################################################
## Upload Button Widget
file_source = ColumnDataSource({'file_contents':[], 'file_name':[]})
cds_test = ColumnDataSource({'v1':[], 'v2':[]})


def file_callback(attr,old,new):

    global tabs, t
    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.BytesIO(file_contents)

# Here it errors out when trying '.xlsx' file but work for .csv Any Idea ???? 
    #df1 = pd.read_excel(file_io, sheet = 'Sheet1')
    #df2 = pd.read_excel(file_io, sheet = 'Sheet2')

    # call some python functions for analysis
    # returns few results
    # for now lets assume main_dt has all the results of analysis


    df1 = pd.read_excel(file_path, sheet_name = 'Sheet1')
    df2 = pd.read_excel(file_path, sheet_name = 'Sheet2')

    main_dt = pd.DataFrame({'v1':df1['v1'], 'v2': df2['v2']})
    level_names = main_dt['v2'].unique().tolist()

    sum_v1_level = []
    for i in level_names:
        csd_temp = ColumnDataSource(main_dt[main_dt['v2'] == i])
        columns = [TableColumn(field=j, title="First") for j in main_dt.columns] 
        dt = DataTable(source = csd_temp, columns = columns, width=400, height=280)
        temp = Panel(child = dt, title = i)
        t.append(temp)
        sum_v1_level.append(sum(csd_temp.data['v1']))
    
    tabs = Tabs(tabs = t)
    cds_plot = ColumnDataSource({'x':level_names, 'y':sum_v1_level})

    p_o = figure(x_range = level_names, plot_height=250, title="Plot")
    p_o.vbar(x='x', top = 'y', width=0.9, source = cds_plot)
    p_o.xgrid.grid_line_color = None
    p_o.y_range.start = 0
    p_o.y_range.end = max(sum_v1_level)*1.2
    labels_o = LabelSet(x='x', y = 'y', text='y', level='glyph',
        x_offset=-13.5, y_offset=0, render_mode='canvas', source = cds_plot)
    p_o.add_layout(labels_o)

    curdoc().add_root(p_o)
    curdoc().add_root(tabs)
    print('successful upload')

file_source.on_change('data', file_callback)

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();
""")

Bdw:有什么方法可以抑制这个警告还是我做错了?(在将读取列插入 CDS 时)

BokehUserWarning:ColumnDataSource 的列必须具有相同的长度。当前长度:('v1', 19), ('v2', 0)

2。添加到布局中

curdoc().title = 'Test Joel'
curdoc().add_root(button)

以下是输出:

这是原始数据: 注意:这里分享的所有数据都是虚拟的,真实案例有更多的表和更多的维度。

总结一下:

  1. 无法通过上传按钮读取.xlsx文件

  2. 在按钮回调函数本身中执行所有步骤是否正确?

【问题讨论】:

    标签: python pandas web-applications bokeh


    【解决方案1】:

    对于那些你以后会参考这个线程的人:这是一个有一个上传按钮来处理.xlsx文件的解决方案。这是给 python3 的

    我只分享主要的数据处理代码。其余一切如上。

    import pandas as pd
    import io
    import base64
    
    
    def file_callback_dt1(attr,old,new):
        print('filename:', file_source_dt1.data['file_name'])
        raw_contents = file_source_dt1.data['file_contents'][0]
        prefix, b64_contents = raw_contents.split(",", 1)
        file_contents = base64.b64decode(b64_contents)
        file_io = io.BytesIO(file_contents)
        excel_object = pd.ExcelFile(file_io, engine='xlrd')
        dt_1 = excel_object.parse(sheet_name = 'Sheet1', index_col = 0)
        # rest is upto you :)
    

    【讨论】:

    • 相当古老的评论,我知道,但任何遇到此问题的人 - 尝试使用 openpyxl 而不是 xlrd 作为引擎读取(如果适用,则写入).xlsx。从 xlrd docs 开始,该库现在仅支持 .xls
    【解决方案2】:

    目前以这种方式使用 CDS 的总体思路是合理的。未来应该会有更好的机制,但我无法推测它们何时会实施。关于read_excel 的错误,这是熊猫问题/问题,而不是散景问题。

    至于关于列长度的警告,这几乎肯定表明存在使用问题。它告诉您您的 v2 列是空的,这似乎不是您想要的,并且还违反了所有 CDS 列始终具有相同长度的基本假设。我不确定您为什么要为v2 生成一个空列表,但无法运行代码。

    编辑:例如,如果所有内容的长度相同,则一一添加就可以了:

    In [4]: s.add([1,2,3], 'foo')
    Out[4]: 'foo'
    
    In [5]: s.add([1,2,3], 'bar')
    Out[5]: 'bar'
    

    只有当您添加的内容长度不正确时才会出现问题,这就是错误消息所述:

    In [6]: s.add([], 'baz')
    /Users/bryanv/work/bokeh/bokeh/models/sources.py:138: BokehUserWarning: ColumnDataSource's columns must be of the same length. Current lengths: ('bar', 3), ('baz', 0), ('foo', 3)
      "Current lengths: %s" % ", ".join(sorted(str((k, len(v))) for k, v in data.items())), BokehUserWarning))
    Out[6]: 'baz'
    

    如果您没有预先设置列的数据,请不要将“空列表”作为占位符放入,或者一旦放入实际列,长度就会不一致。这就是你的问题的原因。

    【讨论】:

    • 关于最后一点,我有一个疑问:假设您读取了一个带有 2 列 v1,v2 的 pandas dataFrame 对象,然后将这些列相加得到 sum_v1, sum_v2 然后您将其插入到CDS 一张一张。因此,当您第一次将sum_v1 插入 CDS 时,它会抛出长度不等于(1,0)的警告
    • 不,它没有。该错误意味着您没有将它们一一添加。这意味着两列同时存在,但其中一列是空的
    • 那么如何同时插入两者?我的意思是实际数字
    • source.data.update(foo=[...], bar=[..]) 是一种方式。或source.data = new_dict,其中new_dict 中的所有列大小相同。
    猜你喜欢
    • 2023-02-10
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多