On Profitability Measures of Businesses

Import libraries

In [1]:
import numpy as np
import datetime
import pandas as pd
import warnings
pd.set_option('display.max_rows', 500)
import seaborn as sns
sns.set(color_codes=True)

warnings.filterwarnings('ignore')

Import PrattStats Data

In [2]:
data=pd.read_csv('prattsdatabase.csv',encoding='latin-1')   

data['IntanglibleIntensityPPA']=data.TotalIntangiblesPPA/data.TotalAssetsPPA
data['NonCompete'][data['NonCompete']=='Yes']=1.0
data['NonCompete'][data['NonCompete']=='No']=0.0

The baseline measure of Business Rate of Return

The measure of rate of returns is called "NetIncomeOverSalePrice". It is the netincome (after depreciation, interest, taxes) from business in the last full year before the sale divided by market value of invested capital or MVIC. MVIC is the total consideration paid to the seller and includes any cash, notes and/or securities that were used as a form of payment plus any interest-bearing liabilities assumed by the buyer. The MVIC price includes the noncompete value and the assumption of interest-bearing liabilities and excludes (1) the real estate value and (2) any earnouts (because they have not yet been earned, and they may not be earned) and (3) the employment/consulting agreement values.

In [3]:
data['NetIncomeOverSalePrice']=(data.EarningsBeforeTaxes/data.MVICPrice)*100
In [4]:
naics_labels=['Agriculture, Forestry, Fishing and Hunting',\
        'Mining, Quarrying, and Oil and Gas Extraction',\
        'Utilities',\
        'Construction',\
        'NA',\
        'Manufacturing',\
        'NA',\
        'Wholesale Trade',\
        'Retail Trade',\
        'NA',\
        'Transportation and Warehousing',\
        'NA',\
        'Information',\
        'Finance and Insurance',\
        'Real Estate and Rental and Leasing',\
        'Professional, Scientific, and Technical Services',\
        'Management of Companies and Enterprises',\
        'Administrative and Support and Waste Management and Remediation Services'\
        'NA',\
        'Educational Services',\
        'Health Care and Social Assistance',\
        'Arts, Entertainment, and Recreation',\
        'Accommodation and Food Service',\
        'Other Services']

len(naics_labels)

naics_code=[11,21,22,23,24,31,34,42,44,46,48,50,51,52,53,54,55,56,61,62,71,72,81,92]
n=23


naics_labels=['Ag. Mining and Utilities',\
        'Consts and Mfg',\
        'Trade and warehousing',\
        'Information and FIRE',\
        'Professional services',\
        'Nonprof services']

naics_code=[11,23,42,51,54,57,92]

data['TargetStructureAlt']=data['TargetStructure'].values
sel=data['TargetStructure'].isin(['Partnership', 'Limited Partnership', 'LLP'])
data.loc[sel,'TargetStructureAlt']='Partnership'
sel=data['TargetStructure'].isin(['Consolidated', 'UK Corporation', 'PC','Non-Profit','Ltd','Limited Corporation'])
data.loc[sel,'TargetStructureAlt']='Others'
sel=data['TargetStructure'].isin(['C corporation'])
data.loc[sel,'TargetStructureAlt']='C Corporation'

Rates of Returns

Sample selection

Taking away outliers top and bottom 0.1%

In [5]:
upper_cutoff=0.999
lower_cutoff=0.001
cutoffH=data.MVICPrice.quantile(upper_cutoff)
cutoffL=data.MVICPrice.quantile(lower_cutoff)
cutoffHNI=data.EarningsBeforeTaxes.quantile(upper_cutoff)
cutoffLNI=data.EarningsBeforeTaxes.quantile(lower_cutoff)
cutoffTAH=data.TotalAssetsPPA.quantile(upper_cutoff)
cutoffTAL=data.TotalAssetsPPA.quantile(lower_cutoff)
selection=(data.MVICPrice.notnull())&(data.MVICPrice<cutoffH)&((data.MVICPrice>0))&((data.MVICPrice>cutoffL)) &(data.EarningsBeforeTaxes<cutoffHNI)&(data.EarningsBeforeTaxes>cutoffLNI)&(data.TotalAssetsPPA<cutoffTAH)&(data.TotalAssetsPPA>cutoffTAL)
sel=data['TargetStructureAlt'].isin(['C Corporation','S Corporation', 'Sole Proprietorship', 'Partnership'])
selection=selection&sel
selPassThru=data['TargetStructureAlt'].isin(['S Corporation', 'Sole Proprietorship', 'Partnership'])

data['TargetStructurePassThru']=data['TargetStructure'].values
data.loc[selPassThru,'TargetStructurePassThru']=True
data.loc[~selPassThru,'TargetStructurePassThru']=False


data['TargetStructureBus']=data['TargetStructure'].values
data.loc[sel,'TargetStructureBus']='all_bus'


#data['TargetStructure']='bus'
#data['TargetStructureAlt']='bus'

Define a helper function

In [6]:
def f(x):
    var_name='NetIncomeOverSalePrice'
    d = {}
    d['count']=x[var_name].count()
    d['eq-wgt mean'] = x[var_name].mean()
    d['value-wgt mean']=100*x.EarningsBeforeTaxes.sum()/x.MVICPrice.sum()
    d['std dev'] = x[var_name].std()
    d['p10'] = x[var_name].quantile(.1)
    d['p25'] = x[var_name].quantile(.25)

    d['p50'] = x[var_name].quantile(.5)
    d['p75'] = x[var_name].quantile(.75)
    d['p90'] = x[var_name].quantile(.90)
    return pd.Series(d, index=['count','eq-wgt mean', 'value-wgt mean','std dev', 'p10', 'p25', 'p50','p75','p90'])

Summary statistics for Net Income/ Sale Price

all firms

In [7]:
data[selection][['EarningsBeforeTaxes','NetIncomeOverSalePrice','MVICPrice']].groupby(data.TargetStructureBus).apply(f)
Out[7]:
count eq-wgt mean value-wgt mean std dev p10 p25 p50 p75 p90
TargetStructureBus
all_bus 8986.0 27.704589 1.14464 74.950375 -9.643819 3.645106 21.453128 46.962782 79.19524

by pass-thru status

In [8]:
data[selection][['EarningsBeforeTaxes','NetIncomeOverSalePrice','MVICPrice']].groupby(data.TargetStructurePassThru).apply(f)
Out[8]:
count eq-wgt mean value-wgt mean std dev p10 p25 p50 p75 p90
TargetStructurePassThru
False 1988.0 6.994810 0.332740 83.463948 -24.587270 -2.116869 6.217021 28.443826 57.580734
True 6998.0 33.587847 7.021726 71.266340 -5.242685 7.315148 25.374058 51.031452 84.457674
In [9]:
data[selection][['EarningsBeforeTaxes','NetIncomeOverSalePrice','MVICPrice']].groupby(data.TargetStructureAlt).apply(f)

#data[selection][['EarningsBeforeTaxes','NetIncomeOverSalePrice','MVICPrice','NonCompete']].groupby(data.TargetStructureAlt).apply(f).to_csv('PrattReturns.csv')
Out[9]:
count eq-wgt mean value-wgt mean std dev p10 p25 p50 p75 p90
TargetStructureAlt
C Corporation 1988.0 6.994810 0.332740 83.463948 -24.587270 -2.116869 6.217021 28.443826 57.580734
Partnership 205.0 27.925293 5.785049 56.658561 -19.306121 2.733737 20.436910 48.822604 90.344211
S Corporation 5632.0 31.668688 6.859736 74.637629 -5.916070 6.506642 23.489651 48.234434 79.711738
Sole Proprietorship 1161.0 43.897518 31.905380 53.838569 0.000000 13.925773 37.762611 63.762973 105.000000

nontech/nondistressed

In [10]:
data['Reason']='NoReason'
data.loc[data.Notes.str.contains('retirement',case=False)==1,'Reason']='retirement'
data.loc[data.Notes.str.contains('health',case='False')==1,'Reason']='health'
data.loc[data.Notes.str.contains('relocation',case='False')==1,'Reason']='relocation'
data.loc[data.Notes.str.contains('burnout',case='False')==1,'Reason']='burnout'
In [11]:
selection=(data.MVICPrice.notnull())&(data.MVICPrice<cutoffH)&((data.MVICPrice>0))&((data.MVICPrice>cutoffL)) &(data.EarningsBeforeTaxes<cutoffHNI)&(data.EarningsBeforeTaxes>cutoffLNI)&(data.TotalAssetsPPA<cutoffTAH)&(data.TotalAssetsPPA>cutoffTAL)
sel=data['TargetStructureAlt'].isin(['C Corporation','S Corporation', 'Sole Proprietorship', 'Partnership'])

selection=selection&sel

sel51=( (data.TargetNAICS1/(10**10))>=51 ) &( (data.TargetNAICS1/(10**10))<52 ) 
sel5415=( (data.TargetNAICS1/(10**10))>=54.15 ) &( (data.TargetNAICS1/(10**10))<54.16 )
sel5417=( (data.TargetNAICS1/(10**10))>=54.17 ) &( (data.TargetNAICS1/(10**10))<54.18 )

selnaics=(~sel51&~sel5415 & ~sel5417)

selreason=(data.Reason=='NoReason')
selection=selection&selnaics&selreason


naics_code=[11,23,42,50,51,52,53,54,54.15,54.16,54.17,54.18,55,56,57,92]
data[selection][['EarningsBeforeTaxes','NetIncomeOverSalePrice','MVICPrice']].groupby(data.TargetStructureBus).apply(f)
Out[11]:
count eq-wgt mean value-wgt mean std dev p10 p25 p50 p75 p90
TargetStructureBus
all_bus 8223.0 29.625558 2.019736 69.3952 -7.282154 4.626344 22.730286 48.32704 80.584188