问题
I have a script to plot the prices of some share that the user wants to look at : he can choose the shares via a Dropdown button and Bokeh will draw the curve accordingly. (I am working in jupyter notebook) :
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
output_notebook()
my code is the following :
from bokeh.models import  Callback, ColumnDataSource, Select,CustomJS
from bokeh.plotting import figure, show, gridplot
from bokeh.models.widgets.layouts import VBox
import pandas as pd
shares = ['AAPL', 'MSFT', 'IBM', 'All']
AAPL = pd.read_csv("http://ichart.yahoo.com/table.csv?s=AAPL&a=0&b=1&c=2000&d=0&e=1&f=2015",parse_dates=['Date'])
MSFT = pd.read_csv("http://ichart.yahoo.com/table.csv?s=MSFT&a=0&b=1&c=2000&d=0&e=1&f=2015",parse_dates=['Date'])
IBM = pd.read_csv("http://ichart.yahoo.com/table.csv?s=IBM&a=0&b=1&c=2000&d=0&e=1&f=2015",parse_dates=['Date'])
max_price = max(AAPL['Adj Close'].max(), MSFT['Adj Close'].max(), IBM['Adj Close'].max()) + 10
min_date = min(AAPL['Date'].min(), MSFT['Date'].min(), IBM['Date'].min())
max_date = max(AAPL['Date'].max(), MSFT['Date'].max(), IBM['Date'].max())
myplot = figure(title="Share price", x_axis_type="datetime", x_range=[min_date,max_date],y_range=[0,max_price],
        background_fill='#FFF5EE', plot_width=900, plot_height = 400, outline_line_color= None)
source_AAPL = ColumnDataSource(data=dict(x=AAPL['Date'], y = AAPL['Adj Close'], ytemp = AAPL['Adj Close']))
source_MSFT = ColumnDataSource(data=dict(x=MSFT['Date'], y = MSFT['Adj Close'], ytemp = MSFT['Adj Close']))
source_IBM  = ColumnDataSource(data=dict(x=IBM['Date'],  y = IBM['Adj Close'],  ytemp = IBM['Adj Close']))
myplot.line(x ='x', y ='y', color='#A6CEE3', source = source_AAPL, name='AAPL')
myplot.line(x ='x', y ='y', color='#33A02C', source = source_MSFT, name='IBM')
myplot.line(x ='x', y ='y', color='#FB9A99', source = source_IBM, name='MSFT') 
Callback_Shares = CustomJS(args={'source_AAPL': source_AAPL,'source_MSFT': source_MSFT,'source_IBM': source_IBM}, code="""
    var f = cb_obj.get('value');
    var data_AAPL = source_AAPL.get('data');
    var data_MSFT = source_MSFT.get('data');     
    var data_IBM = source_IBM.get('data');
    if (f == 'AAPL') {
        data_MSFT['y'] = [0 for i in range(len(data_MSFT['x']))];
        data_IBM['y'] = [0 for i in range(len(data_IBM['x']))];
        data_AAPL['y'] = data_AAPL['ytemp'] ;
        source_AAPL.trigger('change');
        source_MSFT.trigger('change');
        source_IBM.trigger('change');
        }
    if (f == 'MSFT') {
        data_AAPL['y'] = [0 for i in range(len(data_AAPL['x']))];
        data_IBM['y'] = [0 for i in range(len(data_IBM['x']))];
        data_MSFT['y'] = data_MSFT['ytemp'] ;
        source_AAPL.trigger('change');
        source_MSFT.trigger('change');
        source_IBM.trigger('change');
        }
    if (f == 'IBM') {
        data_AAPL['y'] = [0 for i in range(len(data_AAPL['x']))];
        data_MSFT['y'] = [0 for i in range(len(data_MSFT['x']))];
        data_IBM['y'] = data_IBM['ytemp'] ;
        source_AAPL.trigger('change');
        source_MSFT.trigger('change');
        source_IBM.trigger('change');
        }
    if (f == 'All') {
        data_AAPL['y'] = data_AAPL['ytemp'];
        data_MSFT['y'] = data_MSFT['ytemp'];
        data_IBM['y'] = data_IBM['ytemp'];
        source_AAPL.trigger('change');
        source_MSFT.trigger('change');
        source_IBM.trigger('change');
        }"""
)
dropdown = Select(title="Shares:", value=shares[3], options=shares, callback = Callback_Shares)
myfigure =  VBox(dropdown, gridplot([[myplot]]))
show(myfigure)
My problem is the figure always shows the 3 curves and does not take into account the choice of the DropDown...
回答1:
The other answer is unfortunately not an optimal one. As a project maintainer I feel obligated to show the project in the best light. Here is a much simpler complete example that functions the same and works with Bokeh 0.12.4:
from bokeh.models import CustomJS, ColumnDataSource, Select
from bokeh.plotting import figure, output_file, show
from bokeh.layouts import column
import pandas as pd
url = "http://ichart.yahoo.com/table.csv?s=%s&a=0&b=1&c=2000&d=0&e=1&f=2015"
AAPL = pd.read_csv(url % "AAPL", parse_dates=['Date'])
MSFT = pd.read_csv(url % "MSFT", parse_dates=['Date'])
IBM  = pd.read_csv(url % "IBM",  parse_dates=['Date'])
max_price = max(AAPL['Close'].max(), MSFT['Close'].max(), IBM['Close'].max())
source = ColumnDataSource({
    'xAAPL' : AAPL['Date'], 'yAAPL' : AAPL['Close'], 'yAAPLp' : AAPL['Close'],
    'xMSFT' : MSFT['Date'], 'yMSFT' : MSFT['Close'], 'yMSFTp' : MSFT['Close'],
    'xIBM'  :  IBM['Date'], 'yIBM'  :  IBM['Close'], 'yIBMp'  :  IBM['Close']
})
p = figure(width=500, height=250, x_axis_type="datetime", y_range=[0, max_price+10])
r_aapl = p.line('xAAPL', 'yAAPL', source=source, color='navy',  alpha=0.5)
r_msft = p.line('xMSFT', 'yMSFT', source=source, color='red',   alpha=0.5)
r_ibm  = p.line('xIBM',  'yIBM',  source=source, color='green', alpha=0.5)
callback = CustomJS(args=dict(r_aapl=r_aapl, r_msft=r_msft, r_ibm=r_ibm), code="""
    f = cb_obj.value;
    r_aapl.visible = false;
    r_msft.visible = false;
    r_ibm.visible = false;
    if      (f == "AAPL") { r_aapl.visible = true; }
    else if (f == "MSFT") { r_msft.visible = true; }
    else if (f == "IBM")  { r_ibm.visible = true; }
    else {
        r_aapl.visible = true;
        r_msft.visible = true;
        r_ibm.visible = true;
    }
""")
shares = ['AAPL', 'MSFT', 'IBM', 'All']
multi_select = Select(title="Select Shares:", value=shares[3], options=shares, callback=callback)
output_file("datetime.html")
show(column(multi_select, p))
I should also add, "interactive legends" that allow glyphs to be hidden or muted by clicking on the legend, will be added as as standard feature in 0.12.5.
回答2:
Rather than updating actual data sources, there are much easier ways to control visibility. You can set a glyph's alpha to zero, or color to None, or probably most directly, set its renderer's visible property to False as seen in this example. The relevant part of that example, simplified some, is this code:
r0 = p.line(x, y0, color="red")
r1 = p.line(x, y1, color="green")
r2 = p.line(x, y2, color="blue")
checkbox = CheckboxGroup(labels=["Line 0", "Line 1", "Line 2"],
                         active=[0, 1, 2], width=100)
checkbox.callback = CustomJS.from_coffeescript(
    args=dict(r0=r0, r1=r1, r2=r2, checkbox=checkbox), 
    code="""
r0.visible = 0 in checkbox.active;
r1.visible = 1 in checkbox.active;
r2.visible = 2 in checkbox.active;
""")
    回答3:
I found the following solution after quite some work :
from bokeh.models import CustomJS, ColumnDataSource, Select
from bokeh.plotting import figure, output_file, show
from bokeh.models.layouts import VBox
import pandas as pd
AAPL = pd.read_csv("http://ichart.yahoo.com/table.csv?s=AAPL&a=0&b=1&c=2000&d=0&e=1&f=2015",parse_dates=['Date'])
MSFT = pd.read_csv("http://ichart.yahoo.com/table.csv?s=MSFT&a=0&b=1&c=2000&d=0&e=1&f=2015",parse_dates=['Date'])
IBM = pd.read_csv("http://ichart.yahoo.com/table.csv?s=IBM&a=0&b=1&c=2000&d=0&e=1&f=2015",parse_dates=['Date'])
max_price = max(AAPL['Close'].max(), MSFT['Close'].max(), IBM['Close'].max()) + 10
min_date = min(AAPL['Date'].min(), MSFT['Date'].min(), IBM['Date'].min())
max_date = max(AAPL['Date'].max(), MSFT['Date'].max(), IBM['Date'].max())
output_file("datetime.html")
source = ColumnDataSource({'xAAPL': AAPL['Date'], 'xMSFT': MSFT['Date'], 'xIBM': IBM['Date'], 
                       'yAAPL': AAPL['Close'], 'yAAPLp': AAPL['Close'], \
                       'yMSFT': MSFT['Close'], 'yMSFTp': MSFT['Close'], \
                       'yIBM': IBM['Close'],   'yIBMp': IBM['Close'] })
p = figure(width=500, height=250, x_axis_type="datetime", x_range=[min_date,max_date],y_range=[0,max_price])
p.line('xAAPL', 'yAAPL', source=source, color='navy', alpha=0.5)
p.line('xMSFT', 'yMSFT', source=source, color='red', alpha=0.5)
p.line('xIBM',  'yIBM',  source=source, color='green', alpha=0.5)
callback = CustomJS(args=dict(source=source), code="""
    var data = source.get('data');
    var f = cb_obj.get('value')
    yAAPL = data['yAAPL']
    yMSFT = data['yMSFT']
    yIBM  = data['yIBM']
    yAAPLp = data['yAAPLp']
    yMSFTp = data['yMSFTp']
    yIBMp  = data['yIBMp']
    if (f == "AAPL") {
        for (i = 0; i < yAAPL.length; i++) {yAAPL[i] = yAAPLp[i]}            
        for (i = 0; i < yMSFT.length; i++) {yMSFT[i] = 'nan'}
        for (i = 0; i < yIBM.length; i++)  {yIBM[i] = 'nan'}
    } 
    else if (f == "MSFT") {
        for (i = 0; i < yAAPL.length; i++) {yAAPL[i] = 'nan'}            
        for (i = 0; i < yMSFT.length; i++) {yMSFT[i] = yMSFTp[i]}
        for (i = 0; i < yIBM.length; i++)  {yIBM[i] = 'nan'}
    } 
    else if (f == "IBM") {
        for (i = 0; i < yAAPL.length; i++) {yAAPL[i] = 'nan'}            
        for (i = 0; i < yMSFT.length; i++) {yMSFT[i] = 'nan'}
        for (i = 0; i < yIBM.length; i++)  {yIBM[i] = yIBMp[i]}
    }
    else {
        for (i = 0; i < yAAPL.length; i++) {yAAPL[i] = yAAPLp[i]}            
        for (i = 0; i < yMSFT.length; i++) {yMSFT[i] = yMSFTp[i]}
        for (i = 0; i < yIBM.length; i++)  {yIBM[i] = yIBMp[i]}
    }
    source.trigger('change');
""")
shares = ['AAPL', 'MSFT', 'IBM', 'All']
multi_select = Select(title="Select Shares:", value=shares[3],     options=shares, callback=callback)
layout = VBox(multi_select, p)
show(layout)
    来源:https://stackoverflow.com/questions/41307136/dropdown-not-working-with-bokeh