Доступ к методам внутри класса из bokeh виджета FileInput - PullRequest
0 голосов
/ 20 апреля 2020

Я работаю над пользовательским интерфейсом службы Bokeh и сталкиваюсь с проблемами при взаимодействии класса (и его методов) с виджетом FileInput. Я использую класс (в этом примере, называемый «EIS_data»), который при создании экземпляра загружает файл, используя pd.read_csv. Класс EIS_data также имеет метод для построения графика данных определенным образом, и я хотел бы иметь возможность загружать фрейм данных pandas, а также вызывать и манипулировать данными, используя методы, уже существующие в классе.

До сих пор я был в состоянии успешно загрузить данные с помощью виджета FileInput, но я не могу понять, как снова получить доступ к фрейму данных после его загрузки. В автономной записной книжке Jupyter я мог запустить d = EIS_data("filename") и затем `` `d.plot '' ', чтобы загрузить данные в pandas фрейм данных и затем отобразить их в соответствии с методом, определенным в классе EIS_data, но я не могу понять, как повторить это в код пользовательского интерфейса после загрузки данных с помощью виджета FileInput.

Есть ли способ связать это с виджетами Bokeh, чтобы я мог просто добавить d.plot() в curdo c ()? Я нашел обходной путь, используя ColumnDataSource, но, кажется, стыдно переопределять методы построения и обработки данных, когда они уже определены в классе. Ниже приведены минимальные рабочие примеры кода пользовательского интерфейса и определения класса.

Код пользовательского интерфейса:

import numpy as np
import pandas as pd
from eis_analysis_trimmed import EIS_data
import bokeh
from bokeh.io import curdoc
from bokeh import layouts
from bokeh.layouts import column,row,gridplot
from bokeh.plotting import figure
from bokeh.models import *
import base64
import io

## Instantiate the EIS_data class for loading data
def load_data(f):
    return EIS_data(f)

## updater function called to load data with FileInput widget
## Must be decoded using base64
def load_file(attr, old, new):
    decoded = base64.b64decode(new)
    d = io.BytesIO(decoded)
    dat = load_data(d)
    print(dat.df)
    print(dat)
    print("EIS Data Uploaded Successfully")
    return dat

f_load = Paragraph(text="""Load Data""",height=15)
f = FileInput()
f.on_change('value',load_file)

curdoc().add_root(column(f))

, а вот класс EIS_data:

import numpy as np
from scipy.optimize import curve_fit
from sklearn.metrics import r2_score
from bokeh.plotting import figure, show
from bokeh.models import LinearAxis, Range1d
from bokeh.resources import INLINE
import bokeh.io 

#locally include javascript dependencies in html
bokeh.io.output_notebook(INLINE)

class EIS_data:
    def __init__(self, file_name, delimiter='\t',
                 header=0, f_low=None, f_high=None):

        #load eis data into a pandas dataframe
        eis_data = pd.read_csv(file_name, delimiter=delimiter, header=header)

        #iterate through all of the columns and check to see
        #if all of the values in that column are null
        #if they are, then remove that column
        for c in eis_data.columns:
            if eis_data[c].isnull().all():
                eis_data = eis_data.drop([c], axis=1)

        #make sure that the data are imported as floats and not strings
        eis_data = eis_data[['freq/Hz', 'Re(Z)/Ohm', '-Im(Z)/Ohm']]
        eis_data['freq/Hz'] = pd.to_numeric(eis_data['freq/Hz'])
        eis_data['Re(Z)/Ohm'] = pd.to_numeric(eis_data['Re(Z)/Ohm'])
        eis_data['-Im(Z)/Ohm'] = pd.to_numeric(eis_data['-Im(Z)/Ohm'])


        self.df = eis_data.sort_values(by='freq/Hz')

    def plot(self, fit_vals = None):
        plot = figure(title="Nyquist Plot",
                          x_axis_label='Re(Z) Ohm',
                          y_axis_label='-Im(Z) Ohm',
                          plot_width=600, 
                           plot_height=600)

        plot.circle(self.df['Re(Z)/Ohm'], self.df['-Im(Z)/Ohm'],
                        size=7, color='navy', name='Data')

        return plot

EDIT: Добавление обходного пути с использованием ColumnDataSource

from bokeh.layouts import column
from bokeh.plotting import figure
from bokeh.models import *
from bokeh.models.widgets import FileInput
import base64
import io

from eis_analysis2 import EIS_data

# Instantiate the EIS_data class for loading data
def load_data(data):
    return EIS_data(data)

# updater function called to load data with FileInput widget
# Must be decoded using base64
def load_file(attr, old, new):
    decoded = base64.b64decode(new)
    d = io.BytesIO(decoded)
    dat = load_data(d)
    dat_df = dat.df
    # Replace plot data with data from newly-loaded file
    source.data = dict(freq=dat_df[dat_df.columns[0]], reZ=dat_df[dat_df.columns[1]], imZ=dat_df[dat_df.columns[2]])
    #phase,mag = bode_calc(reZ,imZ)

    print(dat_df)
    print("EIS Data Uploaded Successfully")


# Create Column Data Source that will be used by the plot
source = ColumnDataSource(data=dict(freq=[], reZ=[], imZ=[]))

##Make the nyquist plot
nyq_plot = figure(title="Nyquist Plot",
                          x_axis_label='Re(Z) Ohm',
                          y_axis_label='-Im(Z) Ohm',
                          plot_width=600, 
                           plot_height=600)
nyq_plot.circle(x="reZ", y="imZ",source=source,size=7, color='navy', name='Data')


f = FileInput()
f.on_change('value', load_file)

layout = column(f, nyq_plot)
curdoc().add_root(layout)
...