Tony Crocker Ski Season Progress Reports visualized with Bokeh

In [1]:
#this clears all variables
#%reset
In [2]:
import pandas as pd
from selenium import webdriver
import os
import math

from bokeh.plotting import figure, output_file, show

from bokeh.models import ColumnDataSource
from bokeh.models import HoverTool
from bokeh.models import WheelZoomTool
from bokeh.models import PanTool
from bokeh.models import ResetTool
from bokeh.models import BoxZoomTool


from bokeh.core.properties import value
from bokeh.models import LinearAxis, Range1d

import nbconvert
import nbformat

from bokeh.io import output_notebook
output_notebook()

import warnings
warnings.filterwarnings('ignore')

# this sets up notebook export to .html
import IPython.core.display as di

# This line will hide code by default when the notebook is exported as HTML
di.display_html('<script>jQuery(function() {if (jQuery("body.notebook_app").length == 0)' + 
                '{ jQuery(".input_area").toggle(); jQuery(".prompt").toggle();}});</script>', raw=True)

# This line will add a button to toggle visibility of code blocks, for use with the HTML export version
#di.display_html('''<button onclick="jQuery('.input_area').toggle(); jQuery('.prompt').toggle();">Toggle code</button>''', raw=True)
Loading BokehJS ...
In [3]:
class crockerToolTips():

    def __init__(self, line_policy, names, tooltips, point_policy, toggleable):
            self.line_policy = line_policy
            self.names = names
            self.tooltips = tooltips
            self.point_policy = point_policy
            self.toggleable = toggleable

            
class crockerLastUpdate():

    def __init__(self, last_update):
            self.last_update = last_update
In [4]:
def scrape_tc(table_number):
    
    try:
        driver = webdriver.Chrome(r'C:\chromedriver_win32\chromedriver.exe') 
        browser = webdriver.Chrome()

        driver.get('https://bestsnow.net/seas19.htm')
        content = driver.find_element_by_xpath('/html/body/table[' + str(table_number) + ']')
        
        tc_last_update = driver.find_element_by_xpath('/html/body/h3')
        tc_last_update_to_text = tc_last_update.text
        
        tc_last_update_string = crockerLastUpdate(last_update=tc_last_update_to_text)
           
    except:
        browser.quit()
        driver.quit()

    resort_dict = {}
    content_list = []

    for c in (content.text).splitlines()[4:]:
        content_list.append(c)

    content_list = [content_list[x:x+4] for x in range(0, len(content_list), 4)]

    resort_dict = {}

    for c in content_list:
        resort_dict[c[0]] = c[1:]

    resort_df = pd.DataFrame(resort_dict)
    
    return(resort_df, tc_last_update_string)
    browser.quit()
    driver.quit()
In [5]:
def create_dfs(resort_df):

    resort_season_snow_df = resort_df.iloc[:1, :]

    resort_percent_normal_df = resort_df.iloc[1:2, :]
    resort_percent_normal_df = resort_percent_normal_df.apply(lambda x: x.str[-4:].str.lstrip('-'))

    resort_percent_open_df = resort_df.iloc[2:3, :]
    resort_percent_open_df = resort_percent_open_df.apply(lambda x: x.str[-4:].str.lstrip('-'))

    resort_list = resort_season_snow_df.columns
    resort_season_snow_df[resort_list] = resort_season_snow_df[resort_list].apply(pd.to_numeric, errors='coerce', axis=1)
    resort_season_snow_df.fillna(0)

    resort_percent_normal_df = resort_percent_normal_df.apply(lambda x: x.str.replace('%', ''))  
    resort_percent_normal_df[resort_list] = resort_percent_normal_df[resort_list].apply(pd.to_numeric, errors='coerce', axis=1)
    resort_percent_normal_df.fillna(0)

    resort_percent_open_df = resort_percent_open_df.apply(lambda x: x.str.replace('%', ''))  
    resort_percent_open_df[resort_list] = resort_percent_open_df[resort_list].apply(pd.to_numeric, errors='coerce', axis=1)
    resort_percent_open_df.fillna(0, inplace=True)

    return resort_season_snow_df, resort_percent_normal_df, resort_percent_open_df, resort_list
In [6]:
def make_tooltip_objs(resort_list):

    tooltip_season_snow_objs = [crockerToolTips(line_policy='nearest', 
                         names=[i+'_season_snow_1'],
                         tooltips=[(i + ' season snow', "@{" + i + "}{0.0[0]}")],
                         point_policy='follow_mouse', toggleable=False) for i in resort_list]

    tooltip_percent_normal_objs = [crockerToolTips(line_policy='nearest', 
                         names=[i+'_percent_normal_1'],
                         tooltips=[(i + ' percent of normal', "@{" + i + "}{0.0[0]}")],
                         point_policy='follow_mouse', toggleable=False) for i in resort_list]

    tooltip_percent_open_objs = [crockerToolTips(line_policy='nearest', 
                         names=[i+'_percent_open_1'],
                         tooltips=[(i + ' percent open', "@{" + i + "}{0.0[0]}")],
                         point_policy='follow_mouse', toggleable=False) for i in resort_list]

    return tooltip_season_snow_objs, tooltip_percent_normal_objs, tooltip_percent_open_objs
In [7]:
def do_bokeh_bar_chart(resort_season_snow_df, resort_percent_normal_df, resort_percent_open_df, tt_objs, tc_last_update_string):

    season_snow_source = ColumnDataSource(data=resort_season_snow_df)
    percent_normal_source = ColumnDataSource(data=resort_percent_normal_df)
    percent_open_source = ColumnDataSource(data=resort_percent_open_df)

    season_snow_hover_list = []

    for i in tt_objs[0]:
        i = HoverTool(
                          line_policy=i.line_policy, 
                          names=i.names,
                          tooltips=i.tooltips,
                          point_policy=i.point_policy, toggleable=i.toggleable)
        season_snow_hover_list.append(i)


    percent_normal_hover_list = []
    for i in tt_objs[1]:
        i = HoverTool(
                          line_policy=i.line_policy, 
                          names=i.names,
                          tooltips=i.tooltips,
                          point_policy=i.point_policy, toggleable=i.toggleable)
        percent_normal_hover_list.append(i)


    percent_open_hover_list = []
    for i in tt_objs[2]:
        i = HoverTool(
                          line_policy=i.line_policy, 
                          names=i.names,
                          tooltips=i.tooltips,
                          point_policy=i.point_policy, toggleable=i.toggleable)
        percent_open_hover_list.append(i)

    title_dic = {'Squaw 8,000': 'California',
                'Whistler': 'Pacific Northwest',
                'Lake Louise': 'Canadian Rockies and Interior B.C.',
                'Grand Targhee': 'U. S. Northern Rockies',
                'Alta': 'Utah',
                'Vail': 'Northern and Central Colorado',
                'Telluride': 'Southern and Western Colorado',
                'Killington': 'Northeast'} 
    
    
    for k, v in title_dic.items():
        if k in resort_season_snow_df.columns.tolist():
            title = v
        
    v = figure(x_range=(0, len(resort_list) + 1),
                               y_range=(0, resort_season_snow_df.values.max() + 50),
                                     plot_height=500,
                                     plot_width=900,
                                     title=title + ' Snow Data ' + tc_last_update_string.last_update,
                                     tools=['pan', 'box_zoom',
                                            'wheel_zoom',
                                            'reset'] + season_snow_hover_list + percent_normal_hover_list + percent_open_hover_list)

    count = 0
    for i in resort_list:
        if resort_season_snow_df.values[0][count] != 0:
            v.vbar(x=(count + 1)-0.2, top=resort_season_snow_df.values[0][count], width=0.2, color="lightblue", legend=value("season snow"),
               name=season_snow_hover_list[count].names[0], source=season_snow_source)
        count += 1

    v.xaxis.ticker = [x for x in range(0, len(resort_list)+1)]

    xaxis_lab_override_dic = {}
    xaxis_lab_override_dic[0] = ''
    count = 1
    for x in resort_list:
        xaxis_lab_override_dic[count] = x
        count += 1

    v.xaxis.major_label_overrides = xaxis_lab_override_dic
    v.xaxis.major_label_orientation = math.pi/3
    v.xaxis.axis_label = "resort"
    v.yaxis.axis_label = "season snow (inches)"
    #

    v.legend.click_policy = 'hide'
    v.legend.location = 'top_right'


    v.extra_y_ranges = {"foo": Range1d(start=0, end=200)}
    v.add_layout(LinearAxis(y_range_name="foo", axis_label="percent"), 'right')


    # add percent of normal bars

    count = 0
    for i in resort_list:
        if resort_percent_normal_df.values[0][count] != 0:
            v.vbar(x=(count+ 1), top= resort_percent_normal_df.values[0][count], width=0.2, color="royalblue", legend=value("percent of normal"),
               name=percent_normal_hover_list[count].names[0], source=percent_normal_source, y_range_name="foo")
        count += 1


    count = 0
    for i in resort_list:
        if resort_percent_open_df.values[0][count] != 0:
            v.vbar(x=(count+ 1)+0.2, top=resort_percent_open_df.values[0][count], width=0.2, color="lightgreen", legend=value("percent open"),
               name=percent_open_hover_list[count].names[0], source=percent_open_source, y_range_name="foo")
        count += 1

    show(v)
In [8]:
if __name__ == "__main__":
    for i in range(1, 10):
        try:
            resort_df, tc_last_update_string = scrape_tc(i)
            resort_season_snow_df, resort_percent_normal_df, resort_percent_open_df, resort_list = create_dfs(resort_df)
            tt_objs = make_tooltip_objs(resort_list)
            do_bokeh_bar_chart(resort_season_snow_df, resort_percent_normal_df, resort_percent_open_df, tt_objs, tc_last_update_string)
        except:
            print('done')
            break
            
            
done
In [ ]:
os.system('jupyter nbconvert --to html bokeh_snow_tracker_181213.ipynb')
In [ ]:
 
In [ ]: