3.3.8. Interactive plots: plotly

I want to show you how far we can push our ongoing exploration of leverage and firm value. The code uses plotly’s subpackage plotly-express which is ridiculously easy to use, for how cool these plots are.

Putting the lessons to work

As an exercise, you can critique the figures on this page. What about them could be improved? (I certainly think there are aspects to improve!)

Hit the “+” sign to show the code.

First, I plot industry-level average leverage and average market to book. This animation makes it easy to see trends:

  • The cloud of points gets wider > increasing dispersion in firm leverage

  • The cloud of points moves up > valuations have been increasing

  • No obvious slope > the relationship between firm leverage and valuation is unclear from this graph1

#!pip install plotly
%matplotlib inline
import pandas as pd
import numpy as np
import plotly.express as px # pip install plotly.. the animation below is from plotly module
from io import BytesIO
from zipfile import ZipFile
from urllib.request import urlopen

url = 'https://github.com/LeDataSciFi/data/blob/main/Firm%20Year%20Datasets%20(Compustat)/CCM_cleaned_for_class.zip?raw=true'

#firms = pd.read_stata(url)   <-- would work, but GH said "too big" and forced me to zip it, 
# so here is the work around to download it:

with urlopen(url) as request:
    data = BytesIO(request.read())

with ZipFile(data) as archive:
    with archive.open(archive.namelist()[0]) as stata:
        firms = pd.read_stata(stata)

# firms = pd.read_stata('https://github.com/LeDataSciFi/data/blob/main/Firm%20Year%20Datasets%20(Compustat)/CCM_cleaned_for_class.zip?raw=true')
firms.name = "Firms"
# https://jupyterbook.org/guide/05_faq.html#How-can-I-include-interactive-Plotly-figures?

# the lines before and after the fig help make sure this is viewable on the website 
# but shouldn't be necessary just for notebook viewing... but I'm not sure about github viewing

from IPython.display import display
from IPython.core.display import HTML
from plotly.offline import init_notebook_mode, plot
init_notebook_mode(connected=True)

fig =   (
        firms
            .query('(fyear < 2014) & (mb < 5) & (td_a >= 0) & (td_a < 1.5) ')         # some sensible limits
            .groupby(['state','gsector','fyear'])
            .agg({'td_a':'mean','mb':'mean','at':'sum','lpermno':'count'
                 }) # we need the # of firms per industry-state for an extra filter
                    # and I wanted the total assets summed so bigger industries get bigger circles
            .rename(columns={'td_a':'Avg Book Leverage', 'mb':'Avg Market to Book','lpermno':'Num_Firms'})     
            .query('Num_Firms > 20 ')    # disgard small industry-states
            .reset_index() # get fyear as a variable for plotting function
            .sort_values(['fyear'])
            .pipe( 
                 (px.scatter,'data_frame'), 
                  y='Avg Market to Book', x='Avg Book Leverage', animation_frame="fyear", 
                  range_x=[0,.5], range_y=[0,2], hover_data=["state","gsector"],
                  title = "State-By-Industry Avg Leverage and Avg Firm Value"
            )
        )
    
plot(fig, filename = 'ind-state mb vs lev.html')
display(HTML('ind-state mb vs lev.html'))

Next, let’s see if those trends are different when we look at firms (not industry aggregations) and, to make things easier to see, let’s hone in on two states and two industries.

  • At firm level, many firms have no leverage!

  • Movement of firms on these plots is far more volatile than industry level movement above

  • Fewer firms have upward movements in valuations, suggesting the industry valuation bumps are being driven by a relative few number of “superstar firms”

  • The cloud of points gets wider > increasing dispersion in firm leverage

fig =   (
            firms
                .query('(fyear < 2014) & (mb < 5) & (td_a >= 0) & (td_a < 1.5) ')         # some sensible limits
                .query('state in ["CA","NY"] & gsector in ["40","45"]')  # sample restriction
                .rename(columns={'td_a':'Book Leverage'})    
                .reset_index() # get fyear as a variable for plotting function
                .sort_values(['fyear'])    
                .pipe( 
                     (px.scatter,'data_frame'), 
                      y='mb',x='Book Leverage',animation_frame="fyear",
                      range_x=[0,1.5], range_y=[0,5], 
                      facet_row="gsector", facet_col="state",
                      hover_data=["state","gsector"],
                      title = "Leverage and Firm Value"
                )
        )
plot(fig, filename = 'mb vs lev for each state-ind.html')
display(HTML('mb vs lev for each state-ind.html'))

Finally, this is a replication of a famous Hans Rosling TED talk figure using the well-known gapminder data:

fig = px.scatter(px.data.gapminder(), x="gdpPercap", y="lifeExp",
                    size="pop", color="continent",animation_frame="year",
                     range_y=[30,85],              
                    hover_name="country", log_x=True, size_max=60)
plot(fig, filename = 'hans.html')
display(HTML('hans.html'))

1

Much more can be said about this, as the relationship between firm leverage and valuation has been the subject of more than 70 years of scholarship, starting with Modigliani and Miller’s influential 1958 paper in the American Economic Review.