Skip to content

Commit d603bdd

Browse files
authored
Merge pull request #45 from martinholmer/revert-pr-37
Postpone resolution of issue 36
2 parents df35a9d + 58c5a89 commit d603bdd

2 files changed

Lines changed: 73 additions & 51 deletions

File tree

behresp/behavior.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,6 @@ def response(calc_1, calc_2, behavior, dump=False):
8585
implications", Journal of Public Economics 84:1-32 (2002) [see
8686
equation 2 on page 10].
8787
88-
Note: the substitution effect here is calculated from a taxable income
89-
base that excludes long-term capital gains, since long-term capital
90-
gains behavior may be modeled separately using the capital-gains
91-
elasticity. You should consider this when applying estimates of the
92-
elasticity of taxable income from the literature.
93-
9488
Note: the nature of the capital-gains elasticity used here is similar
9589
to that used in Joint Committee on Taxation, "New Evidence on the
9690
Tax Elasticity of Capital Gains: A Joint Working Paper of the Staff
@@ -200,10 +194,8 @@ def _mtr12(calc__1, calc__2, mtr_of='e00200p', tax_type='combined'):
200194
mtr1 = np.where(wage_mtr1 > mtr_cap, mtr_cap, wage_mtr1)
201195
mtr2 = np.where(wage_mtr2 > mtr_cap, mtr_cap, wage_mtr2)
202196
pch = ((1. - mtr2) / (1. - mtr1)) - 1.
203-
# Note: c04800 is filing unit's taxable income and
204-
# p23250 is filing units' long-term capital gains
205-
taxinc_less_ltcg = calc1.array('c04800') - calc1.array('p23250')
206-
sub = (pvalue['BE_sub'] * pch * taxinc_less_ltcg)
197+
# Note: c04800 is filing unit's taxable income
198+
sub = pvalue['BE_sub'] * pch * calc1.array('c04800')
207199
# calculate magnitude of income effect
208200
if pvalue['BE_inc'] == 0.0:
209201
inc = np.zeros(calc1.array_len)

behresp/tests/test_behavior.py

Lines changed: 71 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from io import StringIO
99
import numpy as np
1010
import pandas as pd
11+
import pytest
1112
import taxcalc as tc
1213
from behresp import PARAM_INFO, response, quantity_response
1314

@@ -133,24 +134,68 @@ def test_alternative_behavior_parameters(cps_subsample):
133134
del df2
134135

135136

136-
def test_sub_effect_independence():
137+
def test_quantity_response():
138+
"""
139+
Test quantity_response function.
140+
"""
141+
quantity = np.array([1.0] * 10)
142+
res = quantity_response(quantity,
143+
price_elasticity=0,
144+
aftertax_price1=None,
145+
aftertax_price2=None,
146+
income_elasticity=0,
147+
aftertax_income1=None,
148+
aftertax_income2=None)
149+
assert np.allclose(res, np.zeros(quantity.shape))
150+
one = np.ones(quantity.shape)
151+
res = quantity_response(quantity,
152+
price_elasticity=-0.2,
153+
aftertax_price1=one,
154+
aftertax_price2=one,
155+
income_elasticity=0.1,
156+
aftertax_income1=one,
157+
aftertax_income2=(one + one))
158+
assert not np.allclose(res, np.zeros(quantity.shape))
159+
160+
161+
@pytest.mark.skip
162+
@pytest.mark.parametrize("stcg",
163+
[-3600,
164+
-2400,
165+
-1200,
166+
0,
167+
1200,
168+
2400,
169+
3600,
170+
4800])
171+
def test_sub_effect_independence(stcg):
137172
"""
138173
Ensure that LTCG amount does not affect magnitude of substitution effect.
139174
"""
175+
# pylint: disable=too-many-locals
140176
# specify reform that raises top-bracket marginal tax rate
141177
refyear = 2020
142178
reform = {refyear: {'_II_rt7': [0.70]}}
143179
# specify a substitution effect behavioral response
144180
beh_json = """{
145181
"BE_sub": {"2013": 0.25}
146182
}"""
147-
# specify input consisting of two filing units with high earnings, but
148-
# with one having large long-term capital gains and the other having
149-
# no long-term capital gains
150-
input_csv = (u'RECID,MARS,e00200,e00200p,p23250\n'
151-
u'1,1,1000000,1000000,500000\n'
152-
u'2,1,1000000,1000000,0\n')
153-
recs = tc.Records(data=pd.read_csv(StringIO(input_csv)),
183+
# specify several high-earning filing units
184+
num_recs = 9
185+
input_csv = (u'RECID,MARS,e00200,e00200p,p22250,p23250\n'
186+
u'1,2,1000000,1000000,stcg, 0\n'
187+
u'2,2,1000000,1000000,stcg, 4800\n'
188+
u'3,2,1000000,1000000,stcg, 3600\n'
189+
u'4,2,1000000,1000000,stcg, 2400\n'
190+
u'5,2,1000000,1000000,stcg, 1200\n'
191+
u'6,2,1000000,1000000,stcg, 0\n'
192+
u'7,2,1000000,1000000,stcg,-1200\n'
193+
u'8,2,1000000,1000000,stcg,-2400\n'
194+
u'9,2,1000000,1000000,stcg,-3600\n')
195+
inputcsv = input_csv.replace('stcg', str(stcg))
196+
input_dataframe = pd.read_csv(StringIO(inputcsv))
197+
assert len(input_dataframe.index) == num_recs
198+
recs = tc.Records(data=input_dataframe,
154199
start_year=refyear,
155200
gfactors=None, weights=None)
156201
beh_dict = tc.Calculator.read_json_assumptions(beh_json)
@@ -164,39 +209,24 @@ def test_sub_effect_independence():
164209
df1, df2 = response(calc1, calc2, beh_dict)
165210
del calc1
166211
del calc2
167-
# compute change in taxable income for each of the two filing units
168-
chg_funit1 = df2['c04800'][0] - df1['c04800'][0] # funit with RECID=1
169-
chg_funit2 = df2['c04800'][1] - df1['c04800'][1] # funit with RECID=2
212+
# compute change in taxable income for each of the filing units
213+
chg_funit = dict()
214+
for rid in range(1, num_recs + 1):
215+
idx = rid - 1
216+
chg_funit[rid] = df2['c04800'][idx] - df1['c04800'][idx]
170217
del df1
171218
del df2
172219
# confirm reform reduces taxable income when assuming substitution effect
173-
assert chg_funit1 < 0
174-
assert chg_funit2 < 0
175-
# confirm change in taxable income is same for the two filing units
176-
assert np.allclose(chg_funit1, chg_funit2)
177-
del chg_funit1
178-
del chg_funit2
179-
180-
181-
def test_quantity_response():
182-
"""
183-
Test quantity_response function.
184-
"""
185-
quantity = np.array([1.0] * 10)
186-
res = quantity_response(quantity,
187-
price_elasticity=0,
188-
aftertax_price1=None,
189-
aftertax_price2=None,
190-
income_elasticity=0,
191-
aftertax_income1=None,
192-
aftertax_income2=None)
193-
assert np.allclose(res, np.zeros(quantity.shape))
194-
one = np.ones(quantity.shape)
195-
res = quantity_response(quantity,
196-
price_elasticity=-0.2,
197-
aftertax_price1=one,
198-
aftertax_price2=one,
199-
income_elasticity=0.1,
200-
aftertax_income1=one,
201-
aftertax_income2=(one + one))
202-
assert not np.allclose(res, np.zeros(quantity.shape))
220+
emsg = ''
221+
for rid in range(1, num_recs + 1):
222+
if not chg_funit[rid] < 0:
223+
txt = '\nFAIL: stcg={} : chg[{}]={:.2f} is not negative'
224+
emsg += txt.format(stcg, rid, chg_funit[rid])
225+
# confirm change in taxable income is same for all filing units
226+
for rid in range(2, num_recs + 1):
227+
if not np.allclose(chg_funit[rid], chg_funit[1]):
228+
txt = '\nFAIL: stcg={} : chg[{}]={:.2f} != chg[1]={:.2f}'
229+
emsg += txt.format(stcg, rid, chg_funit[rid], chg_funit[1])
230+
del chg_funit
231+
if emsg:
232+
raise ValueError(emsg)

0 commit comments

Comments
 (0)