In [1]:
# Required libraries for bokeh_plot.
import numpy as np
import pandas as pd
from bokeh.plotting import *
from bokeh.objects import Range1d
from bokeh.session import *
from StringIO import StringIO
from math import log, sqrt
In [2]:
"""
Function
--------
bokeh_plot(csv)

Plot Bokeh graph of probabilities for each team in tournament for each stage of tournament.

Parameters
----------
csv : CSV file or data in StringIO format 
  Example of properly formatted csv data is shown in the cell below.
  
Bokeh runs its visualization through a server-side Java kernal, which makes debugging/writing code using the library in python
EXTREMELY tedious. This code cannot be re-run twice without restarting the ipynb kernal/reloading the page/running the cell empty
to clear everything, etc., so make sure that your code is right the first time you put it in.

This function takes a StringIO-format dataset. The table MUST have five columns 
with the following headers: Team, First Round, Conference Semifinals, Conference Finals, The Finals, Conference. 
The table MUST a total of 17 rows, including the header. 

Team: name of NBA team. 
First Round: float between 0 and 1 representing probability of winning first round.
Conference Semifinals: float between 0 and 1 representing probability of winning Semi's.
Conference Finals: float between 0 and 1 representing probability of winning Conference Finals.
The Finals: float between 0 and 1 representing probability of winning Playoff Finals.
Conference: "East" or "West".

Returns
-------
None
"""
def bokeh_plot(csv):
    
    # Create new notebook session.
    NotebookSession(plot=None)
    
    # Define colors for different tourney rounds.
    round_color = {
        "First Round"   : "#0d3362",
        "Conference Semifinals" : "#c64737",
        "Conference Finals"     : "black",
        "The Finals"     : "#ffd700",
    }
    
    # Bifurcated color scheme for representing conferences.
    conference_color = {
        "East" : "#aeaeb8",
        "West" : "#e69584",
    }
    
    # Read data into DataFrame.
    df = pd.read_csv(StringIO(csv))

    # Define graph dimensions
    width = 800
    height = 800
    inner_radius = 90
    outer_radius = 300 - 10
    minr = 0
    maxr = 100
    
    # Define ratio between graph measurements and input values.
    a = ((outer_radius - inner_radius) / (maxr-minr))
    
    # Define angles.
    big_angle = 2.0 * np.pi / (len(df) + 1)
    small_angle = big_angle / 9
    
    # Initialize arrays for strings.
    x = np.zeros(len(df))
    y = np.zeros(len(df))
    
    # Function to transform measurements from input to graph.
    def rad(mic):
        return a*mic*100+inner_radius
    
    # Set output and hold.
    output_notebook()
    hold()
    
    # Map lines.
    line(x+1, y+1, alpha=0, width=width, height=height, title="", tools="", x_axis_type=None, y_axis_type=None)
    
    # Draw initial graph, background, etc.
    plot = curplot()
    plot.x_range = Range1d(start=-420, end=420)
    plot.y_range = Range1d(start=-420, end=420)
    plot.min_border = 0
    plot.background_fill = "#FFFFFF"
    plot.border_fill = "#f0e1d2"
    plot.outline_line_color = None 
    
    # annular wedges
    angles = np.pi/2 - big_angle/2 - df.index*big_angle
    colors = [conference_color[comp] for comp in df.Conference]
    annular_wedge(
        x, y, inner_radius, outer_radius, -big_angle+angles, angles, color=colors,
    )
    
    # small wedges
    annular_wedge(
        x, y, inner_radius, rad(df["First Round"]), -big_angle+angles + 7*small_angle, -big_angle+angles+8*small_angle, color=round_color["First Round"],
    )
    annular_wedge(
        x, y, inner_radius, rad(df["Conference Semifinals"]), -big_angle+angles + 5*small_angle, -big_angle+angles+6*small_angle, color=round_color["Conference Semifinals"],
    )
    annular_wedge(
        x, y, inner_radius, rad(df["Conference Finals"]), -big_angle+angles + 3*small_angle, -big_angle+angles+4*small_angle, color=round_color["Conference Finals"],
    )
    annular_wedge(
        x, y, inner_radius, rad(df["The Finals"]), -big_angle+angles + 1*small_angle, -big_angle+angles+2*small_angle, color=round_color["The Finals"],
    )
    
    # circular axes and lables
    labels = np.arange(0, 110,20)
    radii = a * labels + inner_radius
    circle(x, y, radius=radii, fill_color=None, line_color="white")
    text(x[1:], radii[1:], [str(r) for r in labels[1:]], angle=0, text_font_size="8pt", text_align="center", text_baseline="middle")
    
    # radial axes
    annular_wedge(
        x, y, inner_radius-10, outer_radius+10, -big_angle+angles, -big_angle+angles, color="black",
    )
    
    # team labels
    xr = (radii[-1]+10)*np.cos(np.array(-big_angle/2 + angles))
    yr = (radii[-1]+10)*np.sin(np.array(-big_angle/2 + angles))
    label_angle=np.array(-big_angle/2+angles)
    label_angle[label_angle < -np.pi/2] += np.pi # easier to read labels on the left side
    text(xr, yr, df.Team, angle=label_angle, text_font_size="9pt", text_align="center", text_baseline="middle")
    
    # OK, these hand drawn legends are pretty clunky, will be improved in future release
    circle([-40, -40], [-370, -390], color=conference_color.values(), radius=5)
    text([-30, -30], [-370, -390], text=[x for x in conference_color.keys()], angle=0, text_font_size="7pt", text_align="left", text_baseline="middle")
    
    rect([-40, -40, -40,-40], [18, 0, -18,-36], width=30, height=13, color=round_color.values())
    text([-15, -15, -15,-15], [18, 0, -18,-36], text=round_color.keys(), angle=0, text_font_size="7pt", text_align="left", text_baseline="middle")
    
    xgrid().grid_line_color = None
    ygrid().grid_line_color = None
    
    show()
In [3]:
# Random sample data to test graph with.
import csv
s = []
with open('final_bracket1.csv', 'rb') as f:
    reader = csv.reader(f, delimiter=',')
    for row in reader:
        s.append(','.join(row))
s[0] = "Team,First Round,Conference Semifinals,Conference Finals,The Finals,Conference"
string = '\n'.join(s)
string1 = str(string)
In [4]:
# Run graph.
bokeh_plot(string1)

Configuring embedded BokehJS mode.

Plots
In [4]:
s = []
with open('final_bracket2.csv', 'rb') as f:
    reader = csv.reader(f, delimiter=',')
    for row in reader:
        s.append(','.join(row))
s[0] = "Team,First Round,Conference Semifinals,Conference Finals,The Finals,Conference"
string = '\n'.join(s)
string2 = str(string)
bokeh_plot(string2)

Configuring embedded BokehJS mode.

Plots