| Title: | Analyze Bunching in a Kink or Notch Setting |
|---|---|
| Description: | View and analyze data where bunching is expected. Estimate counter- factual distributions. For earnings data, estimate the compensated elasticity of earnings w.r.t. the net-of-tax rate. |
| Authors: | Itai Trilnick [aut, cre] |
| Maintainer: | Itai Trilnick <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.2.1 |
| Built: | 2026-05-21 07:09:52 UTC |
| Source: | https://github.com/trilnick/bunchr |
Given a kinked or notched budget set, this function gets a vector of earnings and analyzes bunching. The bunchr package has two main useful functions:
bunch( earnings, zstar, t1, t2, Tax = 0, cf_start = NA, cf_end = NA, exclude_before = NA, exclude_after = NA, force_after = FALSE, binw = 10, poly_size = 7, convergence = 0.01, max_iter = 100, correct = TRUE, select = TRUE, draw = TRUE, nboots = 0, seed = NA, progress = FALSE, title = "Bunching Visualization", varname = "Earnings" )bunch( earnings, zstar, t1, t2, Tax = 0, cf_start = NA, cf_end = NA, exclude_before = NA, exclude_after = NA, force_after = FALSE, binw = 10, poly_size = 7, convergence = 0.01, max_iter = 100, correct = TRUE, select = TRUE, draw = TRUE, nboots = 0, seed = NA, progress = FALSE, title = "Bunching Visualization", varname = "Earnings" )
earnings |
Vector of earnings, hopefully a very large one. |
zstar |
Place of kink (critical earning point). |
t1 |
Marginal tax rate before kink. |
t2 |
Marginal tax rate after kink. |
Tax |
"Penalty" tax for crossing zstar. |
cf_start |
Number of bins before the kink bin where counter-factual histogram should start. |
cf_end |
Number of bins after the kink bin where counter-factual histogram should start. |
exclude_before |
Number of excluded bins before the kink bin. |
exclude_after |
Number of excluded bins after the kink bin. |
force_after |
For notch analysis, should |
binw |
Bin width. |
poly_size |
Order of polynomial used to calculate counter-factual histogram. |
convergence |
Minimal rate of change of bunching estimate to stop iterations. |
max_iter |
Maximum number of iterations for bunching estimates. |
correct |
Should the counter-factual histogram be corrected to compensate for shifting left because of the notch? See details. |
select |
Should model selection be used to find counter-factual histogram? See details. |
draw |
Should a graph be drawn? |
nboots |
how many bootstraps should be run? |
seed |
specify seed for bootstraps (earnings sampling). |
progress |
Should a progress bar be desplayed? |
title |
Title for Plot output |
varname |
Name for running variable, to be desplayed in the plot |
bunch checks if the specification has a kink, i.e. if the Tax
parameter is greater than zero. If so, it applies notch_estimator.
Otherwise, it applies kink_estimator. Additionally, bunch
can bootstrap by sampling the earnings vector, returning a vector with
the estimated elasticities.
bunch returns a list comprising of the parameters returned by
kink_estimator and notch_estimator. If bootstraps were asked for,
bootstrapped values are added to the list. Drawing of histograms is
suppressed when running the bootsraps.
kink_estimator, notch_estimator
# analyzing a kink ability_vec <- 4000 * rbeta(100000, 2, 5) earning_vec <- sapply(ability_vec, earning_fun, 0.2, 0, 0.2, 0, 1000) # bunch_viewer(earning_vec, 1000, 20, 20, 1, 1, binw = 20) estim <- bunch(earning_vec, 1000, 0, 0.2, Tax = 0, 20, 20, 1, 1, binw = 20, draw=TRUE, nboots = 0, seed = 16) estim$e # analyzing a notch earning_vec <- sapply(ability_vec, earning_fun, 0.2, 0.2, 0.2, 500, 1000) bunch_viewer(earning_vec, 1000, 10, 40, 2, 22, binw = 50) estim <- bunch(earning_vec, 1000, 0.2, 0.2, Tax = 500, 10, 40, 2, 22, binw = 50, draw = FALSE, nboots = 0, seed = 16) estim$e# analyzing a kink ability_vec <- 4000 * rbeta(100000, 2, 5) earning_vec <- sapply(ability_vec, earning_fun, 0.2, 0, 0.2, 0, 1000) # bunch_viewer(earning_vec, 1000, 20, 20, 1, 1, binw = 20) estim <- bunch(earning_vec, 1000, 0, 0.2, Tax = 0, 20, 20, 1, 1, binw = 20, draw=TRUE, nboots = 0, seed = 16) estim$e # analyzing a notch earning_vec <- sapply(ability_vec, earning_fun, 0.2, 0.2, 0.2, 500, 1000) bunch_viewer(earning_vec, 1000, 10, 40, 2, 22, binw = 50) estim <- bunch(earning_vec, 1000, 0.2, 0.2, Tax = 500, 10, 40, 2, 22, binw = 50, draw = FALSE, nboots = 0, seed = 16) estim$e
This function is meant to aid find excluded bins and analysis area for a bunching study. It displays a histogram with borders. Optionally, you can get the actual histogram back. This is convenient, as the kink/notch point is set as the center of a bin.
bunch_viewer( earnings, zstar = NA, cf_start = 10, cf_end = 50, exclude_before = 2, exclude_after = 20, binw = NA, trimy = TRUE, report = FALSE, title = "Count Histogram", varname = "Running Variable" )bunch_viewer( earnings, zstar = NA, cf_start = 10, cf_end = 50, exclude_before = 2, exclude_after = 20, binw = NA, trimy = TRUE, report = FALSE, title = "Count Histogram", varname = "Running Variable" )
earnings |
Vector of earnings, hopefully a very large one |
zstar |
Place of notch/kink (critical earning point) |
cf_start |
Number of bins before the kink bin where counter-factual histogram should start. |
cf_end |
Number of bins after the kink bin where counter-factual histogram should start. |
exclude_before |
Number of excluded bins before the kink bin. |
exclude_after |
Number of excluded bins after the kink bin. |
binw |
Bin width. |
trimy |
Logical. Should the y-axis be trimmed to better show off-bunching histogram? |
report |
Should the function return the actual histogram? |
title |
Title for Plot output |
varname |
Name for running variable, to be desplayed in the plot |
A plot, the actual histogram if report is set to TRUE.
ability_vec <- 4000 * rbeta(100000, 2, 5) earning_vec <- sapply(ability_vec, earning_fun, 0.2, 0.1, 0.2, 0, 1000) bunch_viewer(earning_vec, 1000, 20, 40, 2, 2, 20, trimy = TRUE, report = FALSE)ability_vec <- 4000 * rbeta(100000, 2, 5) earning_vec <- sapply(ability_vec, earning_fun, 0.2, 0.1, 0.2, 0, 1000) bunch_viewer(earning_vec, 1000, 20, 40, 2, 2, 20, trimy = TRUE, report = FALSE)
bunchApp is an interactive simulator for bunching analysis. It is meant
to serve as a tool for understanding bunching analysis in general, and the use of
bunchr for data analysis. This app is opened on a separate window.
bunchApp()bunchApp()
This function merely runs the app. It accepts no parameters.
The machinery behind the simulation: bunch
This simulator is also offered online at https://trilnick.shinyapps.io/bunchapp/.
The bunchr package is meant to help analyze bunching. Given a vector
of earnings (or any other numeric vector), it creates a counter-factual
count histogram and calculates the compensated elasticity of earnings w.r.t.
the net-of-tax rate.
bunchr has three main functions:
bunchis the main function running the actual analysis.
bunch_viewerserves as an aid to the second by visualizing some of the user-specified options without running an analysis. Use it to see what the histogram of your earnings vector looks like when setting specific bin size, where the counter-factual analysis should be done, and the bounds of the excluded area. You can also save the histogram bins and counts.
bunchAppis an interactive simulator. Use it to explore bunching simulation and estimation of earning elasticity.
Maintainer: Itai Trilnick [email protected]
For an agent with quasi-linear iso-elastic utility, find the utility maximizing earning level.
earning_fun(n, elas, t1, t2, Tax, zstar)earning_fun(n, elas, t1, t2, Tax, zstar)
n |
Ability of person (earnings with zero tax) |
elas |
elasticity of earnings w.r.t. net-of-tax rate |
t1 |
Tax rate before notch/kink |
t2 |
Tax rate after notch/kink |
Tax |
height of notch (zero for pure kink) |
zstar |
place of notch/kink (critical earning point) |
earn_funciton is intended to simulate earnings of agents
under a kink or notch.
Optimal earning level.
earning_fun(1200,0.2,0.1,0.3,100,1000)earning_fun(1200,0.2,0.1,0.3,100,1000)
Given an elasticity, a budget set, and the earnings of the marginal buncher, calculate the utility at notch point and at marginal buncher's earning, and return the absolute difference. Equating these two utilities helps find the elasticity of the marginal buncher. See equations (3) and (4) at Kelven and Waseem (2013)
elas_equalizer(elas, t1, t2, Tax, zstar, delta_zed, binw)elas_equalizer(elas, t1, t2, Tax, zstar, delta_zed, binw)
elas |
elasticity of earnings w.r.t. net-of-tax rate |
t1 |
Tax rate before notch/kink |
t2 |
Tax rate after notch/kink |
Tax |
Height of notch (zero for pure kink) |
zstar |
Place of notch/kink (critical earning point) |
delta_zed |
The notch size in bin units |
binw |
Bin width |
Absolute value of utility at minus utility at
kink/notch point.
Kleven, H. and Waseem, Mazhar (2013) Using notches to uncover optimization frictions and structural elasticities: Theory and evidence from Pakistan, The Quarterly Journal of Economics 128(2)
elas_equalizer(0.2, 0.1, 0.2, 100, 1000, 20, 10) # The elasticity value to minimize this is ~0.0716: elas_equalizer(0.0716, 0.1, 0.2, 100, 1000, 20, 10)elas_equalizer(0.2, 0.1, 0.2, 100, 1000, 20, 10) # The elasticity value to minimize this is ~0.0716: elas_equalizer(0.0716, 0.1, 0.2, 100, 1000, 20, 10)
Given a kinked budget set, this function gets a vector of earnings and
analyzes bunching. This function could be run independently, but best used
through the bunch function.
kink_estimator( earnings, zstar, t1, t2, cf_start = NA, cf_end = NA, exclude_before = 2, exclude_after = 2, binw = 10, poly_size = 7, convergence = 0.01, max_iter = 100, correct = TRUE, select = TRUE, draw = TRUE, title = "Bunching Visualization", varname = "Earnings" )kink_estimator( earnings, zstar, t1, t2, cf_start = NA, cf_end = NA, exclude_before = 2, exclude_after = 2, binw = 10, poly_size = 7, convergence = 0.01, max_iter = 100, correct = TRUE, select = TRUE, draw = TRUE, title = "Bunching Visualization", varname = "Earnings" )
earnings |
Vector of earnings, hopefully a very large one. |
zstar |
Place of kink (critical earning point). |
t1 |
Marginal tax rate before kink. |
t2 |
Marginal tax rate after kink. |
cf_start |
Number of bins before the kink bin where counter-factual histogram should start. |
cf_end |
Number of bins after the kink bin where counter-factual histogram should start. |
exclude_before |
Number of excluded bins before the kink bin. |
exclude_after |
Number of excluded bins after the kink bin. |
binw |
Bin width. |
poly_size |
Order of polynomial used to calculate counter-factual histogram. |
convergence |
Minimal rate of change of bunching estimate to stop iterations. |
max_iter |
Maximum number of iterations for bunching estimates. |
correct |
Should the counter-factual histogram be corrected to compensate for shifting left because of the notch? See details. |
select |
Should model selection be used to find counter-factual histogram? See details. |
draw |
Should a graph be drawn? |
title |
Title for plot output |
varname |
Name for running variable, to be desplayed in the plot |
A histogram is created from the earnings vector, with the kink point zstar as the center of one of the bins.
Correction of the counter-factual is required, as the kink-induced bunching will shift the whole distribution on the right side of the kink to the left. This option follows Chetty et al (2009) in correcting for this.
Model selection works using the step function from the stats package.
It runs backwards from the full polynomial model, trying to find the best
explanatory model using the Akaike information criterion.
kink_estimator returns a list of the following variables:
eEstimated elasticity
BnThe sum of total estimated extra bunching in the excluded bins
bThe rate of extra bunching in the excluded area, divided by the length of area in $
dataA data frame with bin mids, counts, counter-factual counts, and excluded dummy
Chetty, R., Friedman, J., Olsen, T., Pistaferri, L. (2011) Adjustment Costs, Firm Responses, and Micro vs. Macro Labor Supply Elasticities: Evidence from Danish Tax Records, Quarterly Journal of Economics, 126(2).
ability_vec <- 4000 * rbeta(100000, 2, 5) earning_vec <- sapply(ability_vec, earning_fun, 0.2, 0, 0.2, 0, 1000) # bunch_viewer(earning_vec, 1000, 40, 40, 1, 1, binw = 10) kink_estimator(earning_vec, 1000, 0, 0.2, 40, 40, 1, 1, binw = 10, draw = FALSE)$eability_vec <- 4000 * rbeta(100000, 2, 5) earning_vec <- sapply(ability_vec, earning_fun, 0.2, 0, 0.2, 0, 1000) # bunch_viewer(earning_vec, 1000, 40, 40, 1, 1, binw = 10) kink_estimator(earning_vec, 1000, 0, 0.2, 40, 40, 1, 1, binw = 10, draw = FALSE)$e
Given a notched budget set, this function gets a vector of earnings and
analyzes bunching. This function could be run independently, but best used
through the bunch function.
notch_estimator( earnings, zstar, t1, t2, Tax = 0, cf_start = NA, cf_end = NA, exclude_before = NA, exclude_after = NA, force_after = FALSE, binw = 10, poly_size = 7, convergence = 0.01, max_iter = 100, select = TRUE, draw = TRUE, title = "Bunching Visualization", varname = "Earnings" )notch_estimator( earnings, zstar, t1, t2, Tax = 0, cf_start = NA, cf_end = NA, exclude_before = NA, exclude_after = NA, force_after = FALSE, binw = 10, poly_size = 7, convergence = 0.01, max_iter = 100, select = TRUE, draw = TRUE, title = "Bunching Visualization", varname = "Earnings" )
earnings |
Vector of earnings, hopefully a very large one |
zstar |
Place of notch (critical earning point) |
t1 |
Tax rate before notch |
t2 |
Tax rate after notch |
Tax |
Lump sum penalty for crossing zstar. |
cf_start |
Number of bins before the notch bin where counter-factual histogram should start. |
cf_end |
Number of bins after the notch bin where counter-factual histogram should start. |
exclude_before |
Number of excluded bins before the notch bin. |
exclude_after |
Number of excluded bins after the notch bin. |
force_after |
Should |
binw |
Bin width. |
poly_size |
Order of polynomial used to calculate counter-factual histogram. |
convergence |
Minimal rate of change of bunching estimate to stop iterations. |
max_iter |
Maximum number of iterations for bunching estimates. |
select |
Should model selection be used to find counter-factual histogram? See details. |
draw |
Should a graph be drawn? |
title |
Title for plot output |
varname |
Name for running variable, to be displayed in the plot |
By default, notch_estimator will try to find the end of the notch,
i.e. a histogram bin where the incentive to bunch wears off. To do this,
a counter factual distribution is interpolated using the bins inside the
counter-factual area but outside of the excluded area. This is assumed to be
the histogram we would have without the tax at the notch point. At the
observed bunching area, the bins should be much greater than their
corresponding counter-factual bins. Later on, the bunching should create
the notch area where the observed bins are lower than the counter-factual.
factual bin However, both observed and counter-factual histograms should
have the same total mass. The end of the notch is determined as the bin
where the running sum of differences between observed and counter-factual
bins reaches zero. notch_estimator goes through an iterative
process, setting the notch end bin and re-interpolating the counter-factual
in the notch area using all the bins outside of the notch, trying to find
a stable right-side boundary.
A user might want to force a visibly detectable end of notch, rather than
let notch_estimator calculate one. Use force_after=TRUE with
caution: this sets the notch size as exclude_after minus
exclude_before
in terms of bins. The notch size is used to calculate elasticity, and
forcing the wrong notch size might bias the intensive margin elasticity
estimate. In other settings, e.g. a labor market with extensive margins
(entry and exit from labor force), forcing the notch size might be helpful.
For "impure" notches, where the marginal tax rate after the notch is different than the one before it, this function disregards the shifting of post-notch distribution to the right, as suggested by Kleven (2016). Assumption is that the notch effect is much stronger anyway.
The select option implements a step model selection. It runs
backwards from the full polynomial model (specified by poly_size),
trying to find the best explanatory polynomial model for counter-factual
histogram.
notch_estimator returns a list of the following variables:
eEstimated elasticity
BnThe sum of total estimated extra bunching in the area starting at cf_start and through the notch bin (zstar)
notch_sizeDistance between notch bin and bin where the estimated influence of the notch ends, delta_zed
dataA data frame with bin mids, counts, counter-factual counts, and excluded dummy
Kleven, H J (2016). Bunching, Annual Review of Economics, 8(1).
ability_vec <- 4000 * rbeta(100000, 2, 5) earning_vec <- sapply(ability_vec, earning_fun, 0.2, 0.2, 0.2, 500, 1000) bunch_viewer(earning_vec, 1000, 15, 30, 2, 21, binw = 50) notch_estimator(earning_vec, 1000, 0.2, 0.2, 500, 15, 30, 2, 21, binw = 50, draw = FALSE)$eability_vec <- 4000 * rbeta(100000, 2, 5) earning_vec <- sapply(ability_vec, earning_fun, 0.2, 0.2, 0.2, 500, 1000) bunch_viewer(earning_vec, 1000, 15, 30, 2, 21, binw = 50) notch_estimator(earning_vec, 1000, 0.2, 0.2, 500, 15, 30, 2, 21, binw = 50, draw = FALSE)$e
util_calc(z, n, elas, t1, t2, Tax, zstar)util_calc(z, n, elas, t1, t2, Tax, zstar)
z |
Earnings |
n |
Ability of person (earnings with zero tax) |
elas |
elasticity of earnings w.r.t. net-of-tax rate |
t1 |
Tax rate before notch/kink |
t2 |
Tax rate after notch/kink |
Tax |
height of notch (zero for pure kink) |
zstar |
place of notch/kink (critical earning point) |
The utility of earning sum z given other parameters.
util_calc(900, 950, 0.2, 0.1, 0.2, 100, 1000)util_calc(900, 950, 0.2, 0.1, 0.2, 100, 1000)
Ability (n) and elasticity (e) determine an agent's earnings and utility. This function determines the tangency point of the agent's utility with the budget line and returns the distance between the utility of earning at that point and the utility of earning at the notch/kink point. This function is mostly used to find the marginal buncher.
util_equalizer(n, elas, t1, t2, Tax, zstar)util_equalizer(n, elas, t1, t2, Tax, zstar)
n |
Ability of person (earnings with zero tax) |
elas |
elasticity of earnings w.r.t. net-of-tax rate |
t1 |
Tax rate before notch/kink |
t2 |
Tax rate after notch/kink |
Tax |
height of notch (zero for pure kink) |
zstar |
place of notch/kink (critical earning point) |
Absolute value of utility at tangency minus utility at kink/notch point.
util_equalizer(1200,0.2,0.1,0.3,100,1000)util_equalizer(1200,0.2,0.1,0.3,100,1000)