Russell Burdt Tech Blog

Explore tech stock prices in 2018
by Russell Burdt


A picture is not worth a thousand words [reference]. Likewise, a data visualization is not worth a thousand words. A data visualization is worth only the words that are supported by information contained in the visualization. In this blog post, interactive data visualization is used to deliver information about tech stock prices in 2018 (but only a limited amount).

Complete price information for 19 tech stocks for each day of 2018 is contained in the data visualization below—interact and find it.


Interpretation


The upper component of the data visualization contains information about the daily close price of the list of tech stocks represented as y-axis labels. These stocks do not represent a specific index, they are just those I have some familiarity with from working in a technology industry. Each row of data represents the normalized daily close price for one stock during 2018, that is, where all values throughout the year are normalized to the value on Jan 1st 2018. Normalizing and representing the data in colors enables a meaningful relative comparison and a holistic view of this set of tech stocks throughout all of 2018.

An interactive cursor creates a horizontal lineout of the daily close price throughout 2018 for the tech stock in line with the cursor. For example, look at the rough year EBAY had, the volatile year of AAPL, and the relatively good year of ADBE. Any ideas for next year?

As a contrast to the information enabled by this interactive data visualization, consider the article below from The Washington Post published Jan 1st 2019.

Stocks are down after a volatile year, but that’s not the whole picture https://t.co/BJXs4SEElG

— The Washington Post (@washingtonpost) January 1, 2019


Get stock data in Python for free with the tiingo library


The tiingo library provides access to the Tiingo API, which is a commercial product providing a Starter (free) access level for users with an API key. Complete Python code (except for the API key you can get for free) to download historical AAPL stock price data into a pandas DataFrame object is below. All data in the interactive visualization above was downloaded with the Python tiingo library at a rate well below the limits of the Starter plan.

    import pandas as pd
    from io import StringIO
    from tiingo import TiingoClient

    API_KEY = '' # your API KEY here
    client = TiingoClient({'api_key': API_KEY})

    df = pd.read_csv(StringIO(client.get_ticker_price('AAPL',
        fmt='csv',
        startDate='01-01-2019',
        endDate='01-15-2019')))
    

Create and use a diverging asymmetric colorbar in bokeh


A unique component of the Python bokeh data visualization above is the diverging and asymmetric colorbar. The matplotlib colormap reference and bokeh colormap reference both document diverging colormaps that alway diverge at exactly the midpoint (symmetrically). For example, the matplotlib RdGy colormap (diverging and symmetric) is below.


Another blog post and a Stack Overflow post describe how to create and use a diverging colorbar in matplotlib that diverges somewhere other than the midpoint (asymmetrically). This blog post describes then how to create and use a diverging asymmetric colorbar in bokeh, two examples of which are below. In both cases they reproduce the RdGy colormap above but diverge away from the midpoint.



A Python function to create a diverging and asymmetric colormap in bokeh is below. The colormap is built from two sequential matplotlib colormaps — these appear as cm.Reds and cm.Greys in the function. The idea is to line up the two independent colormaps, aligning the ends and scaling everything correctly, and then use the matplotlib colormap implementation to create an object that bokeh understands and can use with a 3D visualization. Note that the function with all default inputs recreates the matplotlib RdGy colormap.

    import numpy as np
    from matplotlib import cm
    from bokeh.models import LinearColorMapper

    def bokeh_diverging_asymmetric_color_mapper(low=0, high=1, center=0.5,
            ncolors=256, cmaps=[cm.Reds, cm.Greys]):
        """
        create and return a bokeh LinearColorMapper object representing a
        diverging and asymmetric colormap
        * inputs 'low' and 'high' represent same as in LinearColorMapper
        * 'center' is the midpoint of the colormap (low < center < high)
        * 'ncolors' defines the resolution of the colormap
        * the colormap is built from the 2 matplotlib colormaps in 'cmaps'
        """
        assert low < center < high
        assert len(cmaps) == 2
        nmaps = [(center - low) / (high - low), (high - center) / (high - low)]
        nmaps = np.round(ncolors * np.array(nmaps)).astype(np.int)
        fracs = [[1 * x / nmap for x in range(nmap)] for nmap in nmaps]
        palette = [cm.colors.rgb2hex(cmaps[0](x)) for x in fracs[0]][::-1] + \
                  [cm.colors.rgb2hex(cmaps[1](x)) for x in fracs[1]]
        return LinearColorMapper(palette=palette, low=low, high=high)
    

At this point the diverging asymmetric colormap object can be used directly in a bokeh 3D data visualization — Python code for all of that is below. The 3D data is created in numpy and represents a rectagular grid of random numbers in the default range of the random function (0 to 1). These data are then represented in a bokeh figure as rectangle objects. In order to apply the colormap, the fill_color of the rectange objects and the bokeh.models.Colorbar object both need to both be mapped to the output of the bokeh_diverging_asymmetric_color_mapper function (in this case configured to create a red-grey colormap with midpoint at 0.6).

    import pandas as pd
    from bokeh.plotting import figure, output_file, show
    from bokeh.models import ColorBar

    # create 3D data in a DataFrame
    x, y = np.arange(60), np.arange(30)
    xx, yy = np.meshgrid(x, y)
    zz = np.random.random(xx.shape)
    df = pd.DataFrame(
        {'x': np.ravel(xx), 'y': np.ravel(yy), 'z': np.ravel(zz)})

    # create a bokeh diverging asymmetric color mapper object
    mapper = bokeh_diverging_asymmetric_color_mapper(low=0, high=1, center=0.6)

    # create a bokeh figure, add Rectangle glyphs mapped to the 3D data
    fig = figure(width=600, height=300, tools='')
    fig.rect(x='x', y='y', width=1, height=1, source=df, line_color=None,
        fill_color={'field': 'z', 'transform': mapper})

    # create and add a colorbar object, then show
    cbar = ColorBar(color_mapper=mapper, location=(0, 0), label_standoff=10)
    cbar.major_label_text_font_size = '12pt'
    fig.add_layout(cbar, place='right')
    show(fig)
    

The graphical output of the code above is this 3D data visualization.


The same example above except with different settings for the diverging asymmetric colormap function creates a different 3D data visualization.


    mapper = bokeh_diverging_asymmetric_color_mapper(low=0, high=1, center=0.3,
        cmaps=[cm.Purples, cm.Blues])
    

© Russell Burdt 2020.