2017-08-31 15 views
0

アスリートのスイミングイベントをグラフ化するために、Djangoアプリケーションでボケプロットを作成しようとしています。私は期間(時間泳ぎ)と日付(泳ぎの日)をプロットしています。アイデアは1つのプロットを持ち、SelectBoxウィジェットを使ってグラフに表示されるイベントを選択できるようにすることです。問題は、CallbackJS関数でデータのソースを変更するとグラフが更新されず空白になることです。CallbackJSのBokehチェンジソースデータ

データが

class Event(models.Model): 
    swimmer = models.ForeignKey(Swimmer, on_delete=models.SET_NULL, null=True, blank=True) 
    team = models.ForeignKey(Team, on_delete=models.CASCADE, null=True, blank=True) 
    name = models.CharField(max_length=50, null=True, blank=True) 
    gender = models.CharField(max_length=1, choices=GENDER_CHOICE, null=True, blank=True) 
    event = models.CharField(max_length=10, choices=EVENT_CHOICE) 
    time = models.DurationField() 
    place = models.IntegerField(null=True, blank=True) 
    date = models.DateField(null=True) 

まずフォームのEventオブジェクトから来て、私は(現在は17)の各イベントを反復処理し、日付(datetime.date)と時間(日時のリストを作ります。 timedelta)フィールド。変更されていないフィールドはそれぞれxとyに使用され、値はホバーツールに使用されるようにわずかに編集されます(ほとんどが文字列に型キャストされます)。特定のイベントのデータがない場合は、のエントリはNoneに設定されます。

例データ:行がページのロード時に表示されるように

data_source = { 
    'x_50_free': [date(2017,9,7), date(2017,9,8)] 
    'y_50_free': [timedelta(seconds=22.96), timedelta(seconds=22.32)] 
    'date_50_free': ['9/7/2017', '9/8/2017'] 
    'time_50_free': ['00:22.96', '00:22.32'] 
    'x_100_free': [date(2017,9,7)] 
    'y_100_free': [timedelta(seconds=49.86)] 
    'date_100_free': ['9/7/2017'] 
    'time_100_free': ['00:49.86'] 
} 

その後、私は最初のラインをプロットします。

source = ColumnDataSource(data=dict(
    x=data_source['x_'+first_event], 
    y=data_source['y_'+first_event], 
    date=data_source['date_'+first_event], 
    time=data_source['time_'+first_event] 
)) 
plot.line('x', 'y', source=source) 

私は私が理解から、私のコールバック関数

callback = CustomJS(args=dict(source=source), code=""" 
    data = %s; 
    f = cb_obj.value; 

    if (f == "50 Freestyle") { 
     source.data['x'] = data.x_50_free; 
     source.data['y'] = data.y_50_free; 
     source.data['date'] = data.date_50_free; 
     source.data['time'] = data.time_50_free; 
    } else if (f == "100 Freestyle") { 
     source.data['x'] = data.x_100_free; 
     source.data['y'] = data.y_100_free; 
     source.data['date'] = data.date_100_free; 
     source.data['time'] = data.time_100_free; 
    } 

    ... 

    } else if (f == "400 IM") { 
     source.data['x'] = data.x_400_im; 
     source.data['y'] = data.y_400_im; 
     source.data['date'] = data.date_400_im; 
     source.data['time'] = data.time_400_im; 
    } 

    source.change.emit(); 
""" % json.dumps(data_source, cls=DatetimeEncoder)) # encoder to handle datetimes for x-axis 

にソースデータを更新し、source.change.emit()はColumnDataSourceを更新するために使用されます。これは、source.data[]をコンソールに記録して、Selectウィジェットオプションに応じて更新を確認できますが、プロット自体は更新されず、空白になります。ソースデータの変化を反映するようにプロットを更新するにはどうすればよいですか?

+0

あなたは何をやっているの完全な(しかし、最小限の)例を投稿していただけますか?特にモックデータとコールバックの定義が含まれています。 – mc51

+0

@ mc51はいちょうどやった!もう情報が必要な場合は教えてください。私はとても近づいているように感じます。それはプロット自体が更新されていないという問題です。 –

答えて

0

あなたの例は、最小でも完全でもありません。すぐに完全な例を実行することができます。最小限のものは、あなたの問題に関係のない側面を除外します。したがって、私はすべてを理解しているとは確信していませんが、私はそれを適応しようとしました。

from bokeh.io import show, output_file, output_notebook 
from bokeh.models import ColumnDataSource 
from bokeh.plotting import figure 
from bokeh.layouts import column, widgetbox 
from bokeh.models import CustomJS, Select 

output_file("swim_plot.html") 

first_event = "_50_free" 
second_event = "_100_free" 

data_source = { 
    'x': [date(2017,9,3), date(2017,9,4)], 
    'y': [0, 0], 
    'x_50_free': [date(2017,9,7), date(2017,9,8)], 
    'y_50_free': [22.23, 24.34], 
    'x_100_free': [date(2017,9,12), date(2017,9,14)], 
    'y_100_free': [23.22, 25,12] 
}  
source = ColumnDataSource(data=data_source) 

callback = CustomJS(args=dict(source=source), code=""" 
    data = source.data; 
    f = cb_obj.value; 
    if (f == "_50_free") { 
     data['x'] = data.x_50_free; 
     data['y'] = data.y_50_free; 
    } else if (f == "_100_free") { 
     data['x'] = data.x_100_free; 
     data['y'] = data.y_100_free; 
    } 
    source.change.emit(); 
""") 

select = Select(title="Option:", value="default", options=["default", 
       first_event, second_event]) 
select.js_on_change('value', callback) 

plot = figure(plot_width=400, plot_height=400, x_axis_type='datetime') 
plot.line('x', 'y', source=source)  
show(column(widgetbox(select),plot)) 

私があなたのコードに見られる主な問題は、ColumnDataSourceの定義です。すべてのデータをdata_dictに追加する一方、その一部だけをソースに追加します。したがって、JSコールバックで欠落しているデータにアクセスしようとすると失敗します。

+0

貧しい例については申し訳ありませんが、全体的には少し時間がかかっていますが、何が関係するのかはっきりしませんでしたが、今後取り組むべきことをすべて含める必要はないようです。私が助けるかもしれないことを私が今提供できるものはありますか? –

+0

あなたの答えは、 'x_50_free'、 'y_50_free'などとは別に 'x'と 'y'を定義する理由は何ですか?私がソースを定義した理由は、ColumnDataSourceに 'x'、 'y'、 'date'、 'time'のキーを設定し、後で値を変更できることでした。後でアクセスするには、すべてのデータをColumnDataSourceに追加する必要がありますか?私が前に言ったように、Selectが変更され、データが変更されたが、プロットが更新されていないときにコンソールにsource.dataをログすることができたので、元々プロットの更新をトリガーし、適切に変更する。 –

+0

はい、データをColumnDataSourceに追加する必要があります。それは私が最後の文章で意味していたものです。 – mc51