#!/usr/bin/env python
import random
import os
import re
import pdb
import pandas as pd
import numpy as np
from scipy import stats
from src.WorkerTable import Worker as Worker
from policy_modules.exchange import Exchange as Exchange
from policy_modules.cost_sharing_subsidy import CostSharing as CostSharing
from policy_modules.uninsured_penalty import UninsuredPenalty as UninsuredPenalty
from policy_modules.mcaid_elig import McaidEligibility as McaidEligibility
from policy_modules.fpl import Fpl as Fpl
from policy_modules.fam_tax import FedTax as FedTax
from utility_modules.math_functions import expand_grid
from utility_modules.multi_merge import multi_merge
from utility_modules.function_dict import populate_fun_dict
from utility_modules.function_dict import plan_ids
[docs]class Hieu():
def __init__(self, hieu_table, ind_parms = None):
"""
__init__: initializes member variables
remove irrelevant persons and index using hieu_id, person_id
"""
# hieu_table = hieu_table.loc[hieu_table['person_id'].notnull()]
# hieu_table.set_index(['hieu_id', 'person_type'], inplace=True)
self.__hieu_table = hieu_table
self.__pop_hieu = hieu_table
#initializing individual behavior related parameters
"""
Getters
"""
[docs] def get_hieu_table(self):
return self.__hieu_table.copy()
[docs] def get_choices(self):
return self.__choices.copy()
[docs] def get_pop_hieu(self):
return self.__pop_hieu.copy()
[docs] def get_long_calib_table(self):
return self.__long_calib_table.copy()
"""
Mutators
"""
[docs] def set_hieu_table(self, new_data):
self.__hieu_table = new_data
[docs] def set_choices(self, new_data):
self.__choices = new_data
[docs] def set_pop_hieu(self, new_data):
#assert new_data.columns.shape[0] == 55, "the populated hieu does not have 55 columns"
self.__pop_hieu = new_data
[docs] def set_long_calib_table(self, new_data):
self.__long_calib_table = new_data
[docs] def make_pop_df(self, firm, Curr_esi, family_table, worker_table):
"""
Input:
1. firm id
2. current esi
3. family table
Output:
1. worker table
2. pop_df: a small version of hieu_table with family income and family_size
choice_types = ['adult_1', 'adult_2', 'child_1', 'child_2', \
'adult_child_1', 'adult_child_2', 'adult_child_3', 'adult_child_4']
"""
#subset worker_table to just workers who have current firm_id
#if (firm == 5000686):
wrk_df = worker_table[worker_table['firm_id'] == firm]
#subset hieu_table to just rows with hieu_id in wrk_df's hieu_id
hieu_df = self.get_hieu_table()
pop_df0 = self.get_pop_hieu()
pop_df = hieu_df.loc[hieu_df.hieu_id.isin(wrk_df.hieu_id)] # subsetting
pop_df = pd.merge(pop_df, pop_df0[['hieu_id', 'person_type', 'mcaid_elig_exante', 'predicted_exante_elig', \
'new_mcaid_elig', 'mcaid_elig']], on=['hieu_id', 'person_type'], how='left')
pop_df = pd.merge(pop_df, family_table[['family_id','fam_income', 'family_size', 'tax_filing_status']], on="family_id", how= "left")
pop_df_columns = pop_df.columns.values
fake_hieu_shape = pd.DataFrame(data=np.nan,index=range(0, 8), columns=pop_df_columns)
fake_hieu_shape.hieu_id = 4000000
for x in range(0, 8):
fake_hieu_shape.loc[[x], 'person_type'] = x
fake_hieu_shape.family_id=0
pop_df = pd.concat([pop_df, fake_hieu_shape], axis=0, ignore_index=True)
pop_df.family_id.astype('int64')
#pop_df.person_id.astype('int64')
pop_df.drop_duplicates(subset=['hieu_id', 'person_type'], keep=False, inplace=True)
##determine part time full time status
wrk_df['part_time'] = 0
wrk_df.loc[wrk_df.hrs_week<30, 'part_time'] = 1
return (pop_df, wrk_df)
[docs] def income_adjuster(self, premium, income, income_adjuster):
# return perceived premium for each combined choice at hieu level
if (income <= 100):
return 1
else:
return min(income_adjuster[1], max(income_adjuster[2], ((premium/income)/income_adjuster[3])**income_adjuster[4]))
[docs] def individual_behavior(self, Curr_esi, Esi_0, xc_prem, individual_mandate, family_table, worker_table, firm_table, income_adjuster):
# initialize borg objects
self.Exchange = Exchange()
self.CostSharing = CostSharing()
self.UninsuredPenalty = UninsuredPenalty()
self.Mcaid = McaidEligibility()
self.Fpl = Fpl()
self.FedTax = FedTax()
""" items need to be accounted for
1. mcaid eligibility
2. mcaid premium
3. exchange subsidy eligibility
4. exchange premiumfamily_table
5. updated out-of-pocket based on cost-sharing
6. esi premium
7. proxy premium
8. individual penalty amount
9. calibration parameters
"""
# pop_df contains updated family income by merging with self.Family (line 156)
(pop_df, wrk_df) = self.make_pop_df(firm_table.curr_firm, Curr_esi, family_table, worker_table)
#expand choices by esi plans
temp_firm = firm_table[['firm_id', 'curr_choice']]
temp_firm.is_copy = False
# fam_income exists from this point on...
expnd_choices = self.esi_expand(self.get_choices(), worker_table, pop_df, temp_firm, Curr_esi)
#pop_df['n_person'] = pop_df['n_person'].fillna(0)
n_person = pop_df.pivot('hieu_id', 'person_type', 'n_person')
#n_person.fillna(0, inplace=True)
n_person = n_person.fillna(0.0).astype(int)
n_person.columns = ['n_person_' + str(i) for i in range(8)]
n_person = n_person.reset_index()
#n_person.fillna(0, inplace=True)
expnd_choices = pd.merge(expnd_choices, n_person[['hieu_id','n_person_2', 'n_person_3']], on='hieu_id', how='left')
people = ['adult_1','adult_2','child_1','child_2','adult_child_1','adult_child_2','adult_child_3','adult_child_4']
esi_fac = float(self.utility_parms[0])
xc_fac = pd.Series(data=[float(self.utility_parms[1]), float(self.utility_parms[2])])
# xc_fac[0] to be used for xc_sub = 0, and xc_fac[1] for xc_sub = 1
mc_fac = float(self.utility_parms[3])
cal_fac = float(self.utility_parms[4])
pen_fac = float(self.utility_parms[5])
### compute all necessary variables for long data
# 1. mcaid eligibility
pop_df['fpl'] = self.Fpl.calculate_fpl(pop_df)
pop_df = self.Mcaid.determine_elig(pop_df)
# including both rigorous medicaid eligibility and predicted eligibili
if self.use_pred_mcaid == True:
pop_df['mcaid_all'] = pop_df['mcaid_elig'] | pop_df['predicted_elig'] | pop_df['predicted_exante_elig']
else:
pop_df['mcaid_all'] = pop_df['mcaid_elig'] | pop_df['predicted_exante_elig']
# 2. mc_premium
pop_df_xc_elig = self.Exchange.subsidy_elig(pop_df, worker_table, Curr_esi, self.affordability_type)
pop_df['mc_premium']= self.Mcaid.calculate_premium(pop_df_xc_elig, self.mcaid_shadow_premium)
# 3. subsidy elig
pop_df['subsidy_elig'] = pop_df_xc_elig['subsidy_elig']
pop_df['adultkid_subsidy_elig']= pop_df_xc_elig['adultkid_subsidy_elig']
# compute columns to flatten multi-index
mcaid_cols = ['mcaid_%d' % i for i in range(8)]
new_columns = ['adultkid_xc_sub_'+ str(i) for i in range(8)]+['mcaid_prem_' + str(i) for i in range(8)]+mcaid_cols + ['new_' + col for col in mcaid_cols] + ['pred_' + col for col in mcaid_cols] + ['xc_sub_'+ str(i) for i in range(8)]
# pivot once to turn wide data (with new variables) to long
new_variables = pd.pivot_table(pop_df, index='hieu_id', columns='person_type', values=['adultkid_subsidy_elig','mc_premium','mcaid_all', 'new_mcaid_elig', 'predicted_elig', 'subsidy_elig'])
new_variables.mcaid_all=new_variables.mcaid_all.fillna(False).astype(bool)
new_variables = new_variables.fillna(0.0).astype(int)
new_variables.mcaid_all= new_variables.mcaid_all.astype(bool)
# flatten multi-index
new_variables.columns = new_columns
expnd_choices = pd.merge(expnd_choices, new_variables, left_on='hieu_id', right_index=True, how='left')
# i. if they are subsidy eligible, delete their medicaid choice
# ii. if any row has a choice type = 9 and a respective mcaid_ = 0, then cut that row; this could happen due to wage change
if (self.dual == 0):
for i, person in enumerate(people):
expnd_choices.drop(expnd_choices.loc[(expnd_choices['xc_sub_' + str(i)] == 1) & (expnd_choices[person] == 9)].index.values, inplace=True)
expnd_choices.drop(expnd_choices.loc[(expnd_choices[person] == 9) & (expnd_choices[mcaid_cols[i]] == 0)].index.values, inplace=True)
else:
for i, person in enumerate(people):
expnd_choices['dual_' + str(i)] = (expnd_choices['xc_sub_'+str(i)] == 1) & (expnd_choices['mcaid_'+str(i)] == 1)
expnd_choices.drop(expnd_choices.loc[(expnd_choices[person] == 9) & (expnd_choices[mcaid_cols[i]] == 0)].index.values, inplace=True)
# only those with medicaid choice should have premium
for index, person in enumerate(people):
expnd_choices['mcaid_prem_' + str(index)] = expnd_choices['mcaid_prem_' + str(index)] * (expnd_choices[person]==9)
# 4. populate exchange premiums, returns columns: [person + '_xc_prem']
try:
curr_av_value = Curr_esi[Curr_esi['firm_id']==firm_table.curr_firm]['av_value'].iloc[0]
except:
curr_av_value = 0.7
expnd_choices = self.Exchange.xc_premium_amount(pop_df_xc_elig, worker_table, xc_prem, Curr_esi, expnd_choices, self.affordability_type, curr_av_value)
# 5. exchange cost sharing and exchange oop update
expnd_choices = self.CostSharing.oop_update(expnd_choices, pop_df_xc_elig)
oop_cols = ['oop' + str(i) for i in range(8)]
# 6 and 7. esi premiums, as well as proxy premiums
expnd_choices = self.esi_premium(expnd_choices, Curr_esi, Esi_0, family_table, firm_table, worker_table)
esi_prem_cols = ['esi_premium_' + str(i) for i in range(8)]
wage_cols = ['wage_delta_' + str(i) for i in range(8)]
for index, person in enumerate(people):
expnd_choices['esi_prem_wage_' + str(index)] = expnd_choices['esi_premium_' + str(index)] + expnd_choices['wage_delta_' + str(index)]
expnd_choices['esi_contr'] = expnd_choices[esi_prem_cols].sum(axis=1)
# 8. applying bronze tier ratio to calibration parameters
bronze_7_1 = self.bronze_ratio[0]*2 # with subsidy
bronze_7_0 = self.bronze_ratio[1]*2 # without subsidy
for i, person in enumerate(people):
expnd_choices['cal' + str(i)][(expnd_choices[person]==7) & (expnd_choices['subsidy_elig_' + str(i)] ==1) ] = \
expnd_choices['cal' + str(i)][(expnd_choices[person]==7) & (expnd_choices['subsidy_elig_' + str(i)] ==1) ]*bronze_7_1
expnd_choices['cal' + str(i)][(expnd_choices[person]==7) & (expnd_choices['subsidy_elig_' + str(i)] ==0 )] = \
expnd_choices['cal' + str(i)][(expnd_choices[person]==7) & (expnd_choices['subsidy_elig_' + str(i)] ==0 )]*bronze_7_0
## 9. calibration amount
new_cali_cols = ['new_cal'+ str(i) for i in range(8)] # new calibration parameters
cali_cols = ['cal'+ str(i) for i in range(8)]
if set(new_cali_cols).issubset(expnd_choices.columns) and self.use_cal_flag == 1:
for i in range(8):
expnd_choices['cal'+str(i)] = expnd_choices['cal'+str(i)] + expnd_choices['new_cal'+str(i)]
# 10. incorporate penalty
expnd_choices['total_premiums'] = expnd_choices['esi_contr'] + expnd_choices['exchange_prem']
pop_df = self.UninsuredPenalty.cheapest_coverage(expnd_choices, pop_df_xc_elig)
expnd_choices = self.UninsuredPenalty.penalty(pop_df, expnd_choices, individual_mandate, self.flat_penalties, pen_fac)
expnd_choices[['penalty_' + str(i) for i in range(8)]] = expnd_choices[['penalty_' + str(i) for i in range(8)]].fillna(0)
expnd_choices[['penalty_orig_' + str(i) for i in range(8)]] = expnd_choices[['penalty_orig_' + str(i) for i in range(8)]].fillna(0)
# penalty only apply to uninsured choice
for i, person in enumerate(people):
expnd_choices['penalty_' + str(i)] = (expnd_choices[person]==1)*expnd_choices['penalty_' + str(i)]
expnd_choices['penalty_orig_' + str(i)] = (expnd_choices[person] == 1) * expnd_choices['penalty_orig_' + str(i)]
# depending which expenditure models to use, to be passed to premium loop
if (self.ab_choice == 1 or self.ab_choice ==2):
exp_cols = ['exp'+ str(i) for i in range(8)]
expnd_choices['total_exp'] = expnd_choices[exp_cols].sum(axis=1)
else:
exp_cols = ['exp'+ str(i)+'_adj' for i in range(8)]
expnd_choices['total_exp'] = expnd_choices[exp_cols].sum(axis=1)
oop_cols = ['oop'+ str(i) for i in range(8)]
# 11 applying expenditure and oop ratio to expenditures relative to silver and bronze
for i, person in enumerate(people):
expnd_choices['exp' + str(i)][expnd_choices[person]==6] = expnd_choices['exp' + str(i)][expnd_choices[person]==6]*self.exp_ratio[0]/1.2
expnd_choices['exp' + str(i)][expnd_choices[person]==7] = expnd_choices['exp' + str(i)][expnd_choices[person]==7]*self.exp_ratio[1]
expnd_choices['exp' + str(i)][expnd_choices[person]==8] = expnd_choices['exp' + str(i)][expnd_choices[person]==8]*self.exp_ratio[2]/.9
expnd_choices['oop' + str(i)][expnd_choices[person]==6] = expnd_choices['oop' + str(i)][expnd_choices[person]==6]*self.oop_ratio[0]/.75
expnd_choices['oop' + str(i)][expnd_choices[person]==7] = expnd_choices['oop' + str(i)][expnd_choices[person]==7]*self.oop_ratio[1]
expnd_choices['oop' + str(i)][expnd_choices[person]==8] = expnd_choices['oop' + str(i)][expnd_choices[person]==8]*self.oop_ratio[2]/1.125
# 12. creating columns for expenditure multiplier, [xc, Oct 10, 2017]
for i, person in enumerate(people):
expnd_choices['exp_mult' + str(i)] = expnd_choices[[person]].isin([2,3,4,5])*self.exp_multiplier[0] + \
expnd_choices[[person]].isin([6,7])*self.exp_multiplier[1] + \
expnd_choices[[person]].isin([10])*self.exp_multiplier[2] + \
expnd_choices[[person]].isin([9])*self.exp_multiplier[3] + \
expnd_choices[[person]].isin([1])*self.exp_multiplier[4]
# set up which set of expenditure model to use
if (self.ab_choice ==3 or self.ab_choice ==4):
exp_suffix = "_adj"
else:
exp_suffix = ""
if (income_adjuster[0] == 0):
for i in range(8):
index = str(i)
(expnd_choices['exp' + index+exp_suffix]).fillna(value=0, inplace=True)
(expnd_choices['cal' + index]*cal_fac).fillna(value=0, inplace=True)
(expnd_choices['oop' + index+exp_suffix]).fillna(value=0, inplace=True)
(expnd_choices['esi_prem_wage_' + index]).fillna(value=0, inplace=True)
(expnd_choices['xc_prem_' + index]).fillna(value=0, inplace=True)
(expnd_choices['mcaid_prem_' + index]).fillna(value=0, inplace=True)
(expnd_choices['penalty_' + index]).fillna(value=0, inplace=True)
(expnd_choices['var' + index]*self.utility_parameter[0]/2).fillna(value=0, inplace=True)
expnd_choices['utility_' + index] = expnd_choices['exp' + index+exp_suffix]*self.utility_parameter[1] + \
(expnd_choices['cal' + index]*cal_fac) - \
(expnd_choices['oop' + index+exp_suffix]) - \
(expnd_choices['esi_prem_wage_' + index]*esi_fac) - \
(expnd_choices['xc_prem_' + index]* \
(xc_fac[1]*(expnd_choices['subsidy_elig_' + index]==1).astype(int) + \
xc_fac[0]*(expnd_choices['subsidy_elig_' + index]==0).astype(int))) - \
(expnd_choices['mcaid_prem_' + index]*mc_fac) - \
(expnd_choices['penalty_' + index]) - \
(expnd_choices['var' + index]*self.utility_parameter[0]/2)
util_cols = ['utility_'+ str(i) for i in range(8)]
expnd_choices['utility'] = expnd_choices[util_cols].sum(axis=1)
else:
# taking out premium related terms
for i in range(8):
index = str(i)
(expnd_choices['exp' + index+exp_suffix]).fillna(value=0, inplace=True)
(expnd_choices['cal' + index]*cal_fac).fillna(value=0, inplace=True)
(expnd_choices['oop' + index+exp_suffix]).fillna(value=0, inplace=True)
(expnd_choices['wage_delta_' + index]).fillna(value=0, inplace=True)
(expnd_choices['penalty_' + index]).fillna(value=0, inplace=True)
(expnd_choices['var' + index]*self.utility_parameter[0]/2).fillna(value=0, inplace=True)
expnd_choices['utility_p1' + index] = expnd_choices['exp' + index+exp_suffix]*self.utility_parameter[1] + \
(expnd_choices['cal' + index]*cal_fac) - \
(expnd_choices['oop' + index+exp_suffix]) - \
(expnd_choices['wage_delta_' + index]) - \
(expnd_choices['penalty_' + index]) - \
(expnd_choices['var' + index]*self.utility_parameter[0]/2)
util_p1_cols = ['utility_p1'+ str(i) for i in range(8)]
expnd_choices['utility_p1'] = expnd_choices[util_p1_cols].sum(axis=1)
# premium part
mcaid_prem_columns = ['mcaid_prem_' + str(i) for i in range(8)]
expnd_choices['mcaid_prem'] = expnd_choices[mcaid_prem_columns].sum(axis=1)
expnd_choices['total_premiums'] = expnd_choices['total_premiums'] + expnd_choices['mcaid_prem']
expnd_choices['perceived_premium'] = expnd_choices['total_premiums']*expnd_choices.apply(lambda row: \
self.income_adjuster(row['total_premiums'], row['fam_income'], income_adjuster), axis=1)
expnd_choices['utility'] = expnd_choices['utility_p1'] - expnd_choices['perceived_premium']
# calculating max utility and making a choice
final_choice_df = self.choose_choice(expnd_choices)
plan_details = self.plan_details(final_choice_df, firm_table.curr_firm, Curr_esi, people, self.ab_choice)
# build long data
firm_curr_choice = Curr_esi[['firm_id','choice']]
firm_curr_choice.rename(columns={'choice':'curr_choice'}, inplace=True)
if self.calib_flag != 0:
self.build_long_calib_table(expnd_choices, pop_df_xc_elig, firm_curr_choice, self.calib_flag, self.debug)
return (final_choice_df, plan_details)
[docs] def build_long_calib_table(self, expnd_choices, pop_df, firm_df, calib_flag, debug):
"""
in:
expnd_choices - wide data with individual choices or utility parameters
calib_flag - 1 is default choice and 2 should be all choices!
effect:
updates self.long_calib_table with unique utility parameters per unique choice and individual
"""
# manifest constants (DECLARE LIST OF COLUMNS YOU WANT TO ADD UP HERE, IN SAME FASHION)
utility_cols = ['utility_' + str(i) for i in range(8)]
exp_cols = ['exp' + str(i) for i in range(8)]
oop_cols = ['oop' + str(i) for i in range(8)]
esi_prem_wage_cols = ['esi_prem_wage_' + str(i) for i in range(8)]
esi_premium_cols = ['esi_premium_'+ str(i) for i in range(8)]
esi_wage_cols = ['wage_delta_'+ str(i) for i in range(8)]
subsidy_cols = ['subsidy_elig_'+ str(i) for i in range(8)]
xc_prem_cols = ['xc_prem_' + str(i) for i in range(8)]
age_rated_prem_cols = ['age_rated_premium_' + str(i) for i in range(8)]
mcaid_elig_cols = ['mcaid_' + str(i) for i in range(8)]
mcaid_prem_cols = ['mcaid_prem_' + str(i) for i in range(8)]
penalty_cols = ['penalty_' + str(i) for i in range(8)]
var_cols = ['var' + str(i) for i in range(8)]
sub_amount_cols = ['sub_amount_'+ str(i) for i in range(8)]
people = ['adult_1','adult_2','child_1','child_2','adult_child_1','adult_child_2','adult_child_3','adult_child_4']
person_id = ['p' + str(i) + '_id' for i in range(8)]
cali_cols = ['cal' + str(i) for i in range(8)] # new calibration parameters
firm_id_cols = ['p0_firm_id','p1_firm_id','p1_firm_id','p2_firm_id','p4_firm_id','p5_firm_id','p6_firm_id','p7_firm_id']
try:
long_table_temp = self.get_long_calib_table()
except:
long_table_temp = pd.DataFrame()
# build long data by going though individuals
for i in range(8):
# initialize base dataframe structure
individual_expnd_choices = expnd_choices.drop_duplicates(['hieu_id', people[i]])
# select columns we need (ADD TO HERE IF YOU WANT MORE COLUMNS)
individual_expnd_choices = individual_expnd_choices[['hieu_id', 'region', people[i], person_id[i], firm_id_cols[i], \
utility_cols[i], cali_cols[i], exp_cols[i], oop_cols[i], esi_prem_wage_cols[i], \
esi_premium_cols[i], esi_wage_cols[i], subsidy_cols[i], sub_amount_cols[i], age_rated_prem_cols[i], \
xc_prem_cols[i], mcaid_prem_cols[i], mcaid_elig_cols[i], penalty_cols[i], var_cols[i]]]
# rename the appropriate columns (IF YOU ADD, MAKE SURE YOU RENAME)
# this is all at individual level, not aggregated to hieu level
individual_expnd_choices.rename(
index=str,
columns={
people[i]: 'choice', \
utility_cols[i]: 'utility', \
firm_id_cols[i]: 'firm_id', \
cali_cols[i]: 'calib', \
person_id[i]: 'person_id', \
exp_cols[i]: 'exp', \
oop_cols[i]: 'oop', \
esi_prem_wage_cols[i]: 'esi_prem_wage_change', \
esi_premium_cols[i]: 'esi_prem', \
esi_wage_cols[i]: 'esi_wage', \
subsidy_cols[i]: 'subsidy_elig', \
age_rated_prem_cols[i]: 'age_rated_premium', \
sub_amount_cols[i]: 'sub_amount', \
xc_prem_cols[i]: 'xc_prem', \
mcaid_prem_cols[i]: 'mcaid_prem', \
mcaid_elig_cols[i]: 'mcaid_elig', \
penalty_cols[i]: 'penalty', \
var_cols[i]: 'var_oop'
},
inplace=True
)
# remove invalid
individual_expnd_choices = individual_expnd_choices[individual_expnd_choices['choice'] != 0]
individual_expnd_choices['person_type'] = people[i]
# merging hieu_df to get more variables
hieu_temp = pop_df[['person_id', 'p_weight', 'policy_holder', 'fpl', 'doc_status','affordability','fam_income', 'family_size', 'family_cap', 'age','coverage']]
individual_expnd_choices = pd.merge(individual_expnd_choices, hieu_temp, on = 'person_id', how = 'left')
individual_expnd_choices = pd.merge(individual_expnd_choices, firm_df, on = 'firm_id', how = 'left')
if (long_table_temp.empty == False):
temp_df = long_table_temp[~np.in1d(long_table_temp.person_id, individual_expnd_choices.person_id)]
temp_df = temp_df.reindex()
long_table = pd.concat([temp_df, individual_expnd_choices])
long_table_temp = long_table
long_table.dropna(subset=['person_id'], inplace=True)
else:
long_table_temp = individual_expnd_choices
self.set_long_calib_table(long_table)
### fixing it, so it returns the right values to the right cells, [xc 1/2/2017]
[docs] def esi_premium(self, choices, esi, esi0, family_table, firm_table, worker_table):
func_dict = populate_fun_dict()
cols = ['ins_p0', 'ins_p1', 'ins_p4', 'ins_p5', 'ins_p6', 'ins_p7']
for i in range(8):
choices['esi_premium_'+ str(i)] = 0
choices['wage_delta_'+ str(i)] = 0
nrows = choices.shape[0]
esi_contribution = np.zeros((nrows, 6))
wage_change = np.zeros((nrows, 6))
mykeys = pd.unique(choices[cols].values.ravel())
mykeys = mykeys[np.isfinite(mykeys)]
for ins in mykeys:
if int(ins) != 0:
loc = np.where(choices[cols] == ins)
temp = plan_ids(choices, loc)
if (np.all(np.isnan(temp)) ==False):
x = func_dict[ins](choices, loc, esi, esi0, self.get_hieu_table(), family_table, firm_table, self.FedTax)
# populating the esi_contribution, a for loop right now [xc]s
for index in range(x[0].shape[0]):
esi_contribution[loc[0][index], loc[1][index]] = x[0][index]
wage_change[loc[0][index], loc[1][index]] = x[1][index]
person_types = [0,1,4,5,6,7]
for index, p_type in enumerate(person_types):
choices['esi_premium_%d' % p_type] = esi_contribution[:, index]
choices['wage_delta_%d' % p_type] = wage_change[:, index]
#change wage_delta to 0 if it makes that person go below minimum_wage
for i in person_types:
choices=pd.merge(choices,worker_table[['person_id','wages','minimum_wage']],how='left',left_on=('p%d_id' %i) ,right_on='person_id')
choices['new_wage']= choices.wages-choices['wage_delta_%d' %i]
choices[choices['new_wage']<choices['minimum_wage']]['wage_delta_%d' %i]=0
choices.drop(['person_id','wages','new_wage','minimum_wage'],axis=1,inplace=True)
return choices
[docs] def set_utility(self, ind_parms):
if ind_parms !=None:
self.utility_parms = ind_parms['utility_params']
self.exp_multiplier = ind_parms['exp_multiplier']
self.flat_penalties = ind_parms['flat_penalties']
self.dual= ind_parms['dual']
self.debug= ind_parms['debug']
self.mcaid_shadow_premium= ind_parms['mcaid_shadow_premium']
self.use_cal_flag= ind_parms['use_cal_flag']
self.ab_choice= ind_parms['ab_choice']
self.calib_flag= ind_parms['calib_flag']
self.cal_multipliers= ind_parms['cal_multipliers']
self.utility_parameter= ind_parms['utility_parameter']
self.long_debug_run= ind_parms['long_debug_run']
self.bronze_ratio = ind_parms['bronze_ratio']
self.exp_ratio = ind_parms['exp_xc_multiplier']
self.oop_ratio = ind_parms['oop_xc_multiplier']
self.affordability_type=ind_parms['affordability_type']
self.use_pred_mcaid = ind_parms['use_pred_mcaid']
[docs] @classmethod
def esi_expand(cls, choice_hieu, wrk_df, pop_df, temp_firm, Curr_esi):
"""
choice_types are
choice_types = ['adult_1', 'adult_2', 'child_1', 'child_2','adult_child_1', 'adult_child_2','adult_child_3', 'adult_child_4']
corresponding to indices 0 1 2 3 4 5 6 7
Curr_esi.columns.values=['index' 'av_value' 'ch' 'choice' 'contr_p_ch' 'contr_p_fam' 'contr_p_self'
'contr_p_sp' 'firm_id' 'oop_ch' 'oop_ch_actual' 'oop_fam' 'oop_fam_actual'
'oop_self' 'oop_self_actual' 'oop_sp' 'oop_sp_actual' 'plan_id'
'premium_ch' 'premium_ch_actual' 'premium_fam' 'premium_fam_actual'
'premium_self' 'premium_self_actual' 'premium_sp' 'premium_sp_actual' 'pt'
'sp']
the goal is to link plan_id with each policy holder, when the person is eligible for the plan
"""
# need to index the data by hieu_id and choice_types columns [xc]
choice_hieu = choice_hieu.reset_index(drop=True)
hieu_idx = choice_hieu['hieu_id'].unique()
#merge in choice type to wrk_df
wrk_df = pd.merge(wrk_df, temp_firm, on= "firm_id", how = 'left')
wrk_df['person_id']=wrk_df['person_id'].astype(float)
# steps needed here to see who is eligible for the plan offered [xc]
choice_hieu = pd.merge(choice_hieu, Curr_esi[['firm_id', 'plan_id', 'pt']], left_on='p0_firm_id', right_on='firm_id', how='left')
choice_hieu.rename( columns={'plan_id': 'p0_plan_id', 'firm_id_x': 'p0_firm_id', 'pt':'pt_0'}, inplace=True)
choice_hieu.drop('firm_id', axis=1, inplace=True)
indices = [1,4,5,6,7]
for i in indices:
choice_hieu = pd.merge(choice_hieu, Curr_esi[['firm_id', 'plan_id','pt']], left_on='p%d_firm_id' % i, right_on='firm_id', how='left')
choice_hieu.rename( columns={'plan_id': 'p%d_plan_id' % i, 'firm_id_x': 'p%d_firm_id' % i, 'pt':'pt_%d' % i}, inplace=True)
choice_hieu.drop('firm_id', axis=1, inplace=True)
pid = ['p0_id', 'p1_id', 'p2_id', 'p3_id', 'p4_id', 'p5_id', 'p6_id', 'p7_id']
mod_wrk_df = wrk_df[['person_id','full_time']].copy()
#add esielig and full_time to choice_hieu table
for i in pid:
mod_wrk_df.columns.values[1] = 'full_time_' + str(i)[1]
choice_hieu = pd.merge(choice_hieu, mod_wrk_df, left_on=i, right_on = 'person_id', how = 'left')
del choice_hieu['person_id_x']
del choice_hieu['person_id_y']
#add in_firm_weight to choice_hieu table
mod_wrk_df = wrk_df[['person_id','in_firm_weight']].copy()
for i in pid:
mod_wrk_df.columns.values[1] = 'in_firm_weight_' + str(i)[1]
choice_hieu = pd.merge(choice_hieu, mod_wrk_df, left_on=i, right_on = 'person_id', how = 'left')
choice_hieu['in_firm_weight_' + str(i)[1]] = choice_hieu['in_firm_weight_'+ str(i)[1]].fillna(0)
del choice_hieu['person_id_x']
del choice_hieu['person_id_y']
mod_wrk_df = wrk_df[['person_id', 'esi_not_elig']].copy()
for i in pid:
mod_wrk_df.columns.values[1] = 'esi_not_elig_' + str(i)[1]
choice_hieu = pd.merge(choice_hieu, mod_wrk_df, left_on=i, right_on='person_id', how='left')
choice_hieu['esi_not_elig_' + str(i)[1]] = choice_hieu['esi_not_elig_' + str(i)[1]].fillna(0)
del choice_hieu['person_id_x']
del choice_hieu['person_id_y']
mod_pop_df=pop_df[['person_id','tax_dependent']].copy()
mod_pop_df.dropna(subset=['person_id'], inplace=True)
for i in pid:
mod_pop_df.columns.values[1] = 'tax_dependent_' + str(i)[1]
choice_hieu = choice_hieu.merge(mod_pop_df, left_on=i, right_on='person_id', how='left')
choice_hieu['tax_dependent_' + str(i)[1]] = choice_hieu['tax_dependent_' + str(i)[1]].fillna(2)
choice_hieu.drop(['person_id'],inplace=True,axis=1)
mod_pop_df = pop_df[['person_id', 'ind_income']].copy()
mod_pop_df.dropna(subset=['person_id'], inplace=True)
for i in pid:
mod_pop_df.columns.values[1] = 'ind_income_' + str(i)[1]
choice_hieu = pd.merge(choice_hieu, mod_pop_df, left_on=i, right_on='person_id', how='left')
choice_hieu['ind_income_' + str(i)[1]] = choice_hieu['ind_income_' + str(i)[1]].fillna(0)
choice_hieu.drop(['person_id'], inplace=True, axis=1)
#cut rows where esielig_ = 0
#for i in esi_elig:
# choice_hieu = choice_hieu.drop(choice_hieu.index[np.where(choice_hieu[i]==0)[0]])
indices = [0,1,4,5,6,7]
people = ['adult_1','adult_2','child_1','child_2','adult_child_1','adult_child_2','adult_child_3','adult_child_4']
#cut rows where a person is working part time but their plan doesn't cover part time workers
for i in indices:
choice_hieu = choice_hieu.drop(choice_hieu.index[np.where((choice_hieu['full_time_%d' % i]==0) & (choice_hieu['pt_%d' % i]==0) & (choice_hieu[people[i]]==2))[0]])
# drop worker whose working hour is lower than threshold
choice_hieu = choice_hieu.drop(choice_hieu.index[np.where((choice_hieu['esi_not_elig_%d' % i] == 1) & (choice_hieu[people[i]] == 2))[0]])
# trimming
choice_hieu = choice_hieu[ ~((choice_hieu[people[i]] ==2) & (choice_hieu['p%d_plan_id' % i].isnull()))]
choice_hieu['p2_plan_id'] = (choice_hieu.child_1 == 4)*choice_hieu.p0_plan_id.fillna(0) + (choice_hieu.child_1 == 5)*choice_hieu.p1_plan_id.fillna(0)
choice_hieu['p3_plan_id'] = (choice_hieu.child_2 == 4)*choice_hieu.p0_plan_id.fillna(0) + (choice_hieu.child_2 == 5)*choice_hieu.p1_plan_id.fillna(0)
choice_hieu['p2_firm_id'] = (choice_hieu.child_1 == 4)*choice_hieu.p0_firm_id + (choice_hieu.child_1 == 5)*choice_hieu.p1_firm_id
choice_hieu['p3_firm_id'] = (choice_hieu.child_2 == 4)*choice_hieu.p0_firm_id + (choice_hieu.child_2 == 5)*choice_hieu.p1_firm_id
#choice_hieu.reset_index(inplace=True)
choice_hieu['hieu_idx'] = pd.merge(choice_hieu[['hieu_id']], pop_df['hieu_id'].drop_duplicates().reset_index(), how = 'left')['index']
choice_hieu = pd.merge(choice_hieu, pop_df[['hieu_id','fam_income']].drop_duplicates().reset_index(), how = 'left', on ='hieu_id')
return (choice_hieu)
[docs] @staticmethod
def choose_choice(x):
# pick a row with maximum utility for each hieu, and choose the first one if duplicates occur [xc]
idx = x.groupby(['hieu_id'])['utility'].transform(max) == x['utility']
y = x[idx]
y = y.groupby('hieu_id').first()
final_choice_df = y.reset_index()
final_choice_df['x_index'] = final_choice_df.index
return (final_choice_df)
[docs] @staticmethod
def plan_details(final_choice_df, firm, Curr_esi, choice_types, ab_choice):
"""
input: choice_table after utility
current firm of interest
return:
1. number of people by plan_id and by plan_type (self, sp, ch and fam)
2. number of policy holders by plan_id and by plan_type (self, sp, ch and fam)
3. total expenditure by plan_id and by plan_type (self, sp, ch and fam)
4. AV value of the plans
"""
fchoice = Curr_esi[['firm_id', 'plan_id']][Curr_esi.firm_id == firm]
chosen_plans = fchoice['plan_id'].values.tolist() # allows more than one plan
# outtable has the same number of rows as the number of plan_ids
outtable = fchoice
outtable.reset_index(drop=True, inplace=True)
# step 1: identifying plan_id(s) of the focal firm
pcols = ['p%d_plan_id' % i for i in range(8)]
choice_i = final_choice_df[choice_types]
policy_type_col = final_choice_df[['ins_p0', 'ins_p1','ins_p4','ins_p5','ins_p6','ins_p7']]
# use un-adjusted expenditure in premium loop or adjusted depending on the option
if ab_choice ==1:
exp_cols = final_choice_df[['exp0', 'exp1', 'exp2', 'exp3', 'exp4', 'exp5', 'exp6', 'exp7']]
oop_cols = final_choice_df[['oop0', 'oop1', 'oop2', 'oop3', 'oop4', 'oop5', 'oop6', 'oop7']]
else:
exp_cols = final_choice_df[['exp0_adj', 'exp1_adj', 'exp2_adj', 'exp3_adj', 'exp4_adj', 'exp5_adj', 'exp6_adj', 'exp7_adj']]
oop_cols = final_choice_df[['oop0_adj', 'oop1_adj', 'oop2_adj', 'oop3_adj', 'oop4_adj', 'oop5_adj', 'oop6_adj', 'oop7_adj']]
adk_choice = choice_i.values[:, [4,5,6,7]]
wgt_cols = ['in_firm_weight_' + str(i) for i in range(8)]
weights = final_choice_df[wgt_cols].values
# step 2: number of policy holders and type of policy holders
for index, plan in enumerate(chosen_plans):
temp = final_choice_df[pcols].isin([plan]).values
adk_sp_plns = reduce(np.multiply,[temp[:,[4,5,6,7]], weights[:,[4,5,6,7]], (adk_choice==2)]).sum()
ad1 = final_choice_df[['p0_plan_id']].isin([plan]).values
ad_1_s_plns = ad1*final_choice_df[['in_firm_weight_0']].values*final_choice_df[['ins_p0']].isin([1,5,9]).values
ad_1_sp_plns = ad1*final_choice_df[['in_firm_weight_0']].values*final_choice_df[['ins_p0']].isin([2,6,10]).values
ad_1_ch_plns = ad1*final_choice_df[['in_firm_weight_0']].values*final_choice_df[['ins_p0']].isin([3,7,11]).values
ad_1_fam_plns = ad1*final_choice_df[['in_firm_weight_0']].values*final_choice_df[['ins_p0']].isin([4,8,12]).values
ad2 = final_choice_df[['p1_plan_id']].isin([plan]).values
ad_2_s_plns = ad2*final_choice_df[['in_firm_weight_1']].values*final_choice_df[['ins_p1']].isin([1,5,9]).values
ad_2_sp_plns = ad2*final_choice_df[['in_firm_weight_1']].values*final_choice_df[['ins_p1']].isin([2,6,10]).values
ad_2_ch_plns = ad2*final_choice_df[['in_firm_weight_1']].values*final_choice_df[['ins_p1']].isin([3,7,11]).values
ad_2_fam_plns = ad2*final_choice_df[['in_firm_weight_1']].values*final_choice_df[['ins_p1']].isin([4,8,12]).values
outtable.loc[index, 's_holder'] = adk_sp_plns.sum() + ad_1_s_plns.sum() + ad_2_s_plns.sum()
outtable.loc[index, 'sp_holder'] = ad_1_sp_plns.sum() + ad_2_sp_plns.sum()
outtable.loc[index, 'ch_holder'] = ad_1_ch_plns.sum() + ad_2_ch_plns.sum()
outtable.loc[index, 'fam_holder'] = ad_1_fam_plns.sum() + ad_2_fam_plns.sum()
on_plans = reduce(np.multiply, [temp, choice_i.isin([2,3,4,5])])
exp_plans = reduce(np.multiply,[on_plans, weights, exp_cols])
oop_plans = reduce(np.multiply,[on_plans, weights, oop_cols])
outtable.loc[index, 'num_plans'] = on_plans.sum().sum()
outtable.loc[index, 'total_exp'] = exp_plans.sum().sum()
outtable.loc[index, 'total_oop'] = oop_plans.sum().sum()
return outtable