
\[Pre\text-Depreciation \: Profitability \: Margin = \frac{Totale \: Revenue- (Total \: Expenses - Depreciation \: Expenses)}{Total \: Revenue} \]
Pre-depreciation profit is an income measure used to determine profit before incorporating non-cash expenses on a balance sheet. Pre-depreciation profit is calculated because it provides a cleaner number that can help determine a organization’s ability to service debt. Much like free cash flow, pre-depreciation profit is a measure of a organization’s actual cash flow. Non-expense items lower an organization’s reported earnings, so a pre-depreciation profit would show a higher profit in comparison to profits calculated after depreciation.
High values in this metric are generally desirable since they indicate that an organization is not losing a lot of its revenues to expenses, though the amount of expense exempted from this metric due to depreciation (which for community development corporations can represent a large portion of their expenses), makes it less of an indicator of true profitability and more an indicator of an organization’s cash flow. Values close to zero are normal, and negative numbers indicate the organization is functioning at a deficit.
Note: This data is available only for organizations that file full 990s. [Organizations with revenues <$200,000 and total assets <$500,000 have the option to not file a full 990 and file an EZ instead.]
Numerator: Total Revenue - (Total Functional
Expenses - Depreciation Expenses)
Denominator: Total Revenue
On 990: Part VIII, Line 12A -SOI PC EXTRACTS: totrevenue
On EZ: Part I, line 9 -SOI PC EXTRACTS: totrevnue
# TEMPORARY VARIABLES
pred_profits <- ( core$totrevenue-(core$totfuncexpns-core$deprcatndepletn))
revenue <- ( core$totrevenue)
# can't divide by zero
revenue[ revenue == 0 ] <- NA
# SAVE RESULTS
core$pred_profitmargin <- pred_profits / revenue
# summary( core$pred_profitmargin )Check high and low values to see what makes sense.
x.05 <- quantile( core$pred_profitmargin, 0.05, na.rm=T )
x.95 <- quantile( core$pred_profitmargin, 0.95, na.rm=T )
ggplot( core, aes(x = pred_profitmargin ) ) +
geom_density( alpha = 0.5) +
xlim( x.05, x.95 ) 
Winsorization: All extreme values have been capped by replacing any values below the 5% distribution and above the 95% distribution with the 5% and 95% values. Consequently the end tails to all density charts may be slightly higher than reality but the visuals will be scaled for better viewing (not skewed by outliers).
core2 <- core
# proportion of values that are negative
mean( core2$pred_profitmargin < 0, na.rm=T )
## [1] 0.28193
core2$pred_profitmargin[ core2$pred_profitmargin < 0 ] <- 0
# proption of values above 200%
mean( core2$pred_profitmargin > 50, na.rm=T )
## [1] 0.0003784295
core2$pred_profitmargin[ core2$pred_profitmargin > 50 ] <- 50
x.05 <- quantile( core$pred_profitmargin, 0.05, na.rm=T )
x.95 <- quantile( core$pred_profitmargin, 0.95, na.rm=T )
core2 <- core
# proportion of values that are negative
# mean( core2$der < 0, na.rm=T )
# proption of values above 1%
# mean( core2$der > 5, na.rm=T )
# WINSORIZATION AT 5th and 95th PERCENTILES
core2$pred_profitmargin[ core2$pred_profitmargin < x.05 ] <- x.05
core2$pred_profitmargin[ core2$pred_profitmargin > x.95 ] <- x.95Tax data is available for full 990 filers, so this metric does not describe any organizations with Gross receipts < $200,000 and Total assets < $500,000. Some organizations with receipts or assets below those thresholds may have filed a full 990, but these would be exceptions.
The data have been capped to those with values between 5% and 95% of the normal distribution to cut off outliers and exempt organizations with zero profitability (though negative values are allowed still).
Convert all monetary variables to thousands of dollars. Scale margin (multiply it by 100) for readability
core2 %>%
mutate( pred_profitmargin = pred_profitmargin * 100,
totrevenue = totrevenue / 1000,
totfuncexpns = totfuncexpns / 1000,
lndbldgsequipend = lndbldgsequipend / 1000,
totassetsend = totassetsend / 1000,
totliabend = totliabend / 1000,
totnetassetend = totnetassetend / 1000 ) %>%
select( STATE, NTEE1, NTMAJ12,
pred_profitmargin,
AGE,
totrevenue, totfuncexpns,
lndbldgsequipend, totassetsend,
totnetassetend, totliabend ) %>%
stargazer( type = s.type,
digits=0,
summary.stat = c("min","p25","median",
"mean","p75","max", "sd"),
covariate.labels = c("Pre-Depreciation Profitability Margin (x100)", "Age",
"Revenue ($1k)", "Expenses($1k)",
"Buildings ($1k)", "Total Assets ($1k)",
"Net Assets ($1k)", "Liabiliies ($1k)"))| Statistic | Min | Pctl(25) | Median | Mean | Pctl(75) | Max | St. Dev. |
| Pre-Depreciation Profitability Margin (x100) | -56 | -1 | 6 | 9 | 22 | 69 | 28 |
| Age | 3 | 22 | 30 | 32 | 41 | 95 | 15 |
| Revenue (1k) | -5,377 | 259 | 909 | 4,522 | 3,672 | 408,932 | 14,286 |
| Expenses(1k) | 0 | 263 | 840 | 4,192 | 3,328 | 382,667 | 13,466 |
| Buildings (1k) | -4 | 79 | 824 | 3,504 | 2,868 | 513,509 | 13,210 |
| Total Assets (1k) | -7,552 | 778 | 2,446 | 9,262 | 7,477 | 672,021 | 27,039 |
| Net Assets (1k) | -178,870 | 156 | 1,094 | 4,553 | 4,079 | 531,068 | 15,470 |
| Liabiliies (1k) | -2,707 | 115 | 816 | 4,709 | 3,133 | 705,623 | 18,722 |
What proportion of orgs have pre-depreciation profitability margins equal to zero?
prop.zero <- mean( core2$pred_profitmargin == 0, na.rm=T )In the sample, 0 percent of the organizations have pre-depreciation profitability margins equal to zero. These organizations are dropped from subsequent graphs to keep the visualizations clean. The interpretation of the graphics should be the distributions of pre-depreciation profitability margins for organizations that have positive or negative values.
###
### ADD QUANTILES
###
### function create_quantiles() defined in r-functions.R
core2$exp.q <- create_quantiles( var=core2$totfuncexpns, n.groups=5 )
core2$rev.q <- create_quantiles( var=core2$totrevenue, n.groups=5 )
core2$asset.q <- create_quantiles( var=core2$totnetassetend, n.groups=5 )
core2$age.q <- create_quantiles( var=core2$AGE, n.groups=5 )
core2$land.q <- create_quantiles( var=core2$lndbldgsequipend, n.groups=5 )min.x <- min( core2$pred_profitmargin, na.rm=T )
max.x <- max( core2$pred_profitmargin, na.rm=T )
ggplot( core2, aes(x = pred_profitmargin )) +
geom_density( alpha = 0.5 ) +
xlim( min.x, max.x ) +
xlab( variable.label ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() )
core3 <- core2 %>% filter( ! is.na(NTEE1) )
table( core3$NTEE1) %>% sort(decreasing=TRUE) %>% kable()| Var1 | Freq |
|---|---|
| Housing | 2837 |
| Community Development | 1585 |
| Human Services | 1102 |
t <- table( factor(core3$NTEE1) )
df <- data.frame( x=Inf, y=Inf,
N=paste0( "N=", as.character(t) ),
NTEE1=names(t) )
ggplot( core3, aes( x=pred_profitmargin ) ) +
geom_density( alpha = 0.5) +
# xlim( -0.1, 1 ) +
labs( title="Nonprofit Subsectors" ) +
xlab( variable.label ) +
facet_wrap( ~ NTEE1, nrow=1 ) +
theme_minimal( base_size = 15 ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank(),
strip.text = element_text( face="bold") ) + # size=20
geom_text( data=df,
aes(x, y, label=N ),
hjust=2, vjust=3,
color="gray60", size=6 )
table( core2$Region) %>% kable()| Var1 | Freq |
|---|---|
| Midwest | 1444 |
| Northeast | 1368 |
| South | 1610 |
| West | 1088 |
t <- table( factor(core2$Region) )
df <- data.frame( x=Inf, y=Inf,
N=paste0( "N=", as.character(t) ),
Region=names(t) )
core2 %>%
filter( ! is.na(Region) ) %>%
ggplot( aes(pred_profitmargin) ) +
geom_density( alpha = 0.5 ) +
xlab( "Census Regions" ) +
ylab( variable.label ) +
facet_wrap( ~ Region, nrow=3 ) +
theme_minimal( base_size = 22 ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() ) +
geom_text( data=df,
aes(x, y, label=N ),
hjust=2, vjust=3,
color="gray60", size=6 )
table( core2$Division ) %>% kable()| Var1 | Freq |
|---|---|
| East North Central | 1038 |
| East South Central | 289 |
| Middle Atlantic | 904 |
| Mountain | 303 |
| New England | 464 |
| Pacific | 785 |
| South Atlantic | 900 |
| West North Central | 406 |
| West South Central | 421 |
t <- table( factor(core2$Division) )
df <- data.frame( x=Inf, y=Inf,
N=paste0( "N=", as.character(t) ),
Division=names(t) )
core2 %>%
filter( ! is.na(Division) ) %>%
ggplot( aes(pred_profitmargin) ) +
geom_density( alpha = 0.5 ) +
xlab( "Census Sub-Regions (10)" ) +
ylab( variable.label ) +
facet_wrap( ~ Division, nrow=3 ) +
theme_minimal( base_size = 22 ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() ) +
geom_text( data=df,
aes(x, y, label=N ),
hjust=2, vjust=3,
color="gray60", size=6 ) 
ggplot( core2, aes(x = totfuncexpns )) +
geom_density( alpha = 0.5 ) +
xlim( quantile(core2$totfuncexpns, c(0.02,0.98), na.rm=T ) )
core2$totfuncexpns[ core2$totfuncexpns < 1 ] <- 1
# core2$totfuncexpns[ is.na(core2$totfuncexpns) ] <- 1
if( nrow(core2) > 10000 )
{
core3 <- sample_n( core2, 10000 )
} else
{
core3 <- core2
}
jplot( log10(core3$totfuncexpns), core3$pred_profitmargin,
xlab="Nonprofit Size (logged Expenses)",
ylab=variable.label,
xaxt="n", xlim=c(3,10) )
axis( side=1,
at=c(3,4,5,6,7,8,9,10),
labels=c("1k","10k","100k","1m","10m","100m","1b","10b") )
core2 %>%
filter( ! is.na(exp.q) ) %>%
ggplot( aes(pred_profitmargin) ) +
geom_density( alpha = 0.5) +
labs( title="Nonprofit Size (logged expenses)" ) +
xlab( variable.label ) +
facet_wrap( ~ exp.q, nrow=3 ) +
theme_minimal( base_size = 22 ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() )
ggplot( core2, aes(x = totrevenue )) +
geom_density( alpha = 0.5 ) +
xlim( quantile(core2$totrevenue, c(0.02,0.98), na.rm=T ) ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() )
core2$totrevenue[ core2$totrevenue < 1 ] <- 1
if( nrow(core2) > 10000 )
{
core3 <- sample_n( core2, 10000 )
} else
{
core3 <- core2
}
jplot( log10(core3$totrevenue), core3$pred_profitmargin,
xlab="Nonprofit Size (logged Revenue)",
ylab=variable.label,
xaxt="n", xlim=c(3,10) )
axis( side=1,
at=c(3,4,5,6,7,8,9,10),
labels=c("1k","10k","100k","1m","10m","100m","1b","10b") )
core2 %>%
filter( ! is.na(rev.q) ) %>%
ggplot( aes(pred_profitmargin) ) +
geom_density( alpha = 0.5 ) +
labs( title="Nonprofit Size (logged revenues)" ) +
xlab( variable.label ) +
facet_wrap( ~ rev.q, nrow=3 ) +
theme_minimal( base_size = 22 ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() )
ggplot( core2, aes(x = totnetassetend )) +
geom_density( alpha = 0.5) +
xlim( quantile(core2$totnetassetend, c(0.02,0.98), na.rm=T ) ) +
xlab( "Net Assets" ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() )
core2$totnetassetend[ core2$totnetassetend < 1 ] <- NA
if( nrow(core2) > 10000 )
{
core3 <- sample_n( core2, 10000 )
} else
{
core3 <- core2
}
jplot( log10(core3$totnetassetend), core3$pred_profitmargin,
xlab="Nonprofit Size (logged Net Assets)",
ylab=variable.label,
xaxt="n", xlim=c(3,10) )
axis( side=1,
at=c(3,4,5,6,7,8,9,10),
labels=c("1k","10k","100k","1m","10m","100m","1b","10b") )
core2$totnetassetend[ core2$totnetassetend < 1 ] <- NA
core2$asset.q <- create_quantiles( var=core2$totnetassetend, n.groups=5 )
core2 %>%
filter( ! is.na(asset.q) ) %>%
ggplot( aes(pred_profitmargin) ) +
geom_density( alpha = 0.5 ) +
labs( title="Nonprofit Size (logged net assets, if assets > 0)" ) +
xlab( variable.label ) +
ylab( "" ) +
facet_wrap( ~ asset.q, nrow=3 ) +
theme_minimal( base_size = 22 ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() )
Total Assets for Comparison
core2$totassetsend[ core2$totassetsend < 1 ] <- NA
core2$tot.asset.q <- create_quantiles( var=core2$totassetsend, n.groups=5 )
if( nrow(core2) > 10000 )
{
core3 <- sample_n( core2, 10000 )
} else
{
core3 <- core2
}
jplot( log10(core3$totassetsend), core3$pred_profitmargin,
xlab="Nonprofit Size (logged Total Assets)",
ylab=variable.label,
xaxt="n", xlim=c(3,10) )
axis( side=1,
at=c(3,4,5,6,7,8,9,10),
labels=c("1k","10k","100k","1m","10m","100m","1b","10b") )
ggplot( core2, aes(x = totassetsend )) +
geom_density( alpha = 0.5) +
xlim( quantile(core2$totassetsend, c(0.02,0.98), na.rm=T ) ) +
xlab( "Net Assets" ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() )
core2 %>%
filter( ! is.na(tot.asset.q) ) %>%
ggplot( aes(pred_profitmargin) ) +
geom_density( alpha = 0.5 ) +
xlab( "Nonprofit Size (logged total assets, if assets > 0)" ) +
ylab( variable.label ) +
facet_wrap( ~ tot.asset.q, nrow=3 ) +
theme_minimal( base_size = 22 ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() )
ggplot( core2, aes(x = AGE )) +
geom_density( alpha = 0.5 ) 
core2$AGE[ core2$AGE < 1 ] <- NA
if( nrow(core2) > 10000 )
{
core3 <- sample_n( core2, 10000 )
} else
{
core3 <- core2
}
jplot( core3$AGE, core3$pred_profitmargin,
xlab="Nonprofit Age",
ylab=variable.label ) 
core2 %>%
filter( ! is.na(age.q) ) %>%
ggplot( aes(pred_profitmargin) ) +
geom_density( alpha = 0.5 ) +
labs( title="Nonprofit Age" ) +
xlab( variable.label ) +
ylab( "" ) +
facet_wrap( ~ age.q, nrow=3 ) +
theme_minimal( base_size = 22 ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() )
ggplot( core2, aes(x = lndbldgsequipend )) +
geom_density( alpha = 0.5 ) 
core2$lndbldgsequipend[ core2$lndbldgsequipend < 1 ] <- NA
if( nrow(core2) > 10000 )
{
core3 <- sample_n( core2, 10000 )
} else
{
core3 <- core2
jplot( log10(core3$lndbldgsequipend), core3$pred_profitmargin,
xlab="Land and Building Value (logged)",
ylab=variable.label,
xaxt="n", xlim=c(3,10) )
axis( side=1,
at=c(3,4,5,6,7,8,9,10),
labels=c("1k","10k","100k","1m","10m","100m","1b","10b") )
}
core2 %>%
filter( ! is.na(land.q) ) %>%
ggplot( aes(pred_profitmargin) ) +
geom_density( alpha = 0.5 ) +
labs( title="Land and Building Value" ) +
xlab( variable.label ) +
ylab( "" ) +
facet_wrap( ~ land.q, nrow=3 ) +
theme_minimal( base_size = 22 ) +
theme( axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank() )
core.pred_profitmargin <- select( core, ein, tax_pd, pred_profitmargin )
saveRDS( core.pred_profitmargin, "03-data-ratios/m-08-pre-d-profit-margin.rds" )
write.csv( core.pred_profitmargin, "03-data-ratios/m-08-pre-d-profit-margin.csv" )