# -*- coding: utf-8 -*-
"""
"""
import pandas as pd
from scipy.interpolate import interp1d
import numpy as np
import numpy.matlib
[docs]def import_scenarios(income_baseline, param, grid, path_scenarios,
options):
"""
Define linear interpolations for time-moving exogenous variables.
Parameters
----------
income_baseline : DataFrame
Table summarizing, for each income group in the data (12, including
people out of employment), the number of households living in each
endogenous housing type (3), their total number at baseline year (2011)
in retrospect (2001), as well as the distribution of their average
income (at baseline year)
param : dict
Dictionary of default parameters
grid : DataFrame
Table yielding, for each grid cell (24,014), its x and y
(centroid) coordinates, and its distance (in km) to the city centre
path_scenarios : str
Path towards raw scenarios used for time-moving exogenous variables
options : dict
Dictionary of default options
Returns
-------
spline_agricultural_price : interp1d
Linear interpolation for the agricultural land price (in rands)
over the years (baseline year set at 0)
spline_interest_rate : interp1d
Linear interpolation for the interest rate (in %) over the years
spline_population_income_distribution : interp1d
Linear interpolation for total population per income group in the data
(12) over the years (baseline year set at 0)
spline_inflation : interp1d
Linear interpolation for inflation rate (in base 100 relative to
baseline year) over the years (baseline year set at 0)
spline_income_distribution : interp1d
Linear interpolation for median annual income (in rands) per income
group in the data (12) over the years (baseline year set at 0)
spline_population : interp1d
Linear interpolation for total population over the years
(baseline year set at 0)
spline_income : interp1d
Linear interpolation for overall average (annual) income over the years
(baseline year set at 0), used to avoid money illusion in future
simulations when computing the housing supply
(see equilibrium.run_simulations)
spline_minimum_housing_supply : interp1d
Linear interpolation for minimum housing supply (in m²) over the years
(baseline year set at 0)
spline_fuel : interp1d
Linear interpolation for fuel price (in rands per km)
over the years (baseline year set at 0)
"""
# Import Scenarios
if options["inc_ineq_scenario"] == 2:
scenario_income_distribution = pd.read_csv(
path_scenarios + 'Scenario_inc_distrib_2.csv', sep=';')
elif options["inc_ineq_scenario"] == 1:
scenario_income_distribution = pd.read_csv(
path_scenarios + 'Scenario_inc_distrib_1.csv', sep=';')
elif options["inc_ineq_scenario"] == 3:
scenario_income_distribution = pd.read_csv(
path_scenarios + 'Scenario_inc_distrib_3.csv', sep=';')
if options["pop_growth_scenario"] == 4:
scenario_population = pd.read_csv(
path_scenarios + 'Scenario_pop_20201209.csv', sep=';')
elif options["pop_growth_scenario"] == 3:
scenario_population = pd.read_csv(
path_scenarios + 'Scenario_pop_3.csv', sep=';')
elif options["pop_growth_scenario"] == 2:
scenario_population = pd.read_csv(
path_scenarios + 'Scenario_pop_2.csv', sep=';')
elif options["pop_growth_scenario"] == 1:
scenario_population = pd.read_csv(
path_scenarios + 'Scenario_pop_1.csv', sep=';')
scenario_inflation = pd.read_csv(
path_scenarios + 'Scenario_inflation_1.csv', sep=';')
scenario_interest_rate = pd.read_csv(
path_scenarios + 'Scenario_interest_rate_1.csv', sep=';')
if options["fuel_price_scenario"] == 2:
scenario_price_fuel = pd.read_csv(
path_scenarios + 'Scenario_price_fuel_2.csv', sep=';')
elif options["fuel_price_scenario"] == 1:
scenario_price_fuel = pd.read_csv(
path_scenarios + 'Scenario_price_fuel_1.csv', sep=',')
elif options["fuel_price_scenario"] == 3:
scenario_price_fuel = pd.read_csv(
path_scenarios + 'Scenario_price_fuel_3.csv', sep=',')
# Spline for population by income group in raw data
spline_population_income_distribution = interp1d(
np.array([2001, 2011, 2040]) - param["baseline_year"],
np.transpose([income_baseline.Households_nb_2001,
income_baseline.Households_nb,
scenario_income_distribution.Households_nb_2040]),
'linear')
# Spline for inflation
spline_inflation = interp1d(
scenario_inflation.Year_infla[
~np.isnan(scenario_inflation.inflation_base_2010)
] - param["baseline_year"],
scenario_inflation.inflation_base_2010[
~np.isnan(scenario_inflation.inflation_base_2010)],
'linear')
# Spline for median income by income group in raw data
# We initialize the vector for years 1996, 2001, 2011, and 2040
income_distribution = np.array(
[income_baseline.INC_med, income_baseline.INC_med,
income_baseline.INC_med, scenario_income_distribution.INC_med_2040]
)
# After 2011, for each income group, we multiply the baseline by inflation
# growth rate over the period
(income_distribution[scenario_population.Year_pop > 2011, :]
) = (income_distribution[scenario_population.Year_pop > 2011, :]
* np.matlib.repmat(spline_inflation(scenario_population.Year_pop[
scenario_population.Year_pop > 2011] - param["baseline_year"])
/ spline_inflation(2011 - param["baseline_year"]),
1,
income_distribution.shape[1])
)
# Then we get the spline
spline_income_distribution = interp1d(
scenario_population.Year_pop[~np.isnan(scenario_population.Year_pop)]
- param["baseline_year"],
np.transpose(
income_distribution[~np.isnan(scenario_population.Year_pop), :]),
'linear')
# Spline for overall population
spline_population = interp1d(
scenario_population.Year_pop[~np.isnan(scenario_population.HH_total)]
- param["baseline_year"],
scenario_population.HH_total[~np.isnan(scenario_population.HH_total)],
'linear')
# Spline for real interest rate
spline_interest_rate = interp1d(
scenario_interest_rate.Year_interest_rate[
~np.isnan(scenario_interest_rate.real_interest_rate)]
- param["baseline_year"],
scenario_interest_rate.real_interest_rate[
~np.isnan(scenario_interest_rate.real_interest_rate)],
'linear')
# Spline for minimum housing_supply per pixel
minimum_housing_2011 = param["minimum_housing_supply"]
spline_minimum_housing_supply = interp1d(
np.array([2001, 2011, 2100])
- param["baseline_year"],
np.transpose(
[np.zeros(len(grid.dist)), minimum_housing_2011,
minimum_housing_2011])
)
# Spline for agricultural land price
# We get missing value for 2040 by accounting for inflation
agricultural_price_long_fut = (
param["agricultural_price_baseline"]
* spline_inflation(2040 - param["baseline_year"])
/ spline_inflation(2011 - param["baseline_year"])
)
# Then we get the spline
spline_agricultural_price = interp1d(
[2001 - param["baseline_year"],
2011 - param["baseline_year"],
2040 - param["baseline_year"]],
[param["agricultural_price_retrospect"],
param["agricultural_price_baseline"],
agricultural_price_long_fut],
'linear')
# Spline for fuel prices (in rands per km)
spline_fuel = interp1d(
scenario_price_fuel.Year_fuel[
~np.isnan(scenario_price_fuel.price_fuel)]
- param["baseline_year"],
scenario_price_fuel.price_fuel[
~np.isnan(scenario_price_fuel.price_fuel)]
/ 100,
'linear')
# Spline for overall average income
# We first get initial values
average_income_retrospect = np.sum(
income_baseline.Households_nb_2001 * income_baseline.INC_med
) / sum(income_baseline.Households_nb_2001)
average_income_baseline = np.sum(
income_baseline.Households_nb * income_baseline.INC_med
) / sum(income_baseline.Households_nb)
average_income_long_fut = np.sum(
scenario_income_distribution.Households_nb_2040
* scenario_income_distribution.INC_med_2040
) / sum(scenario_income_distribution.Households_nb_2040)
# Then we set the time frame according to inflation schedule
year_inc = scenario_inflation.Year_infla[
~np.isnan(scenario_inflation.inflation_base_2010)]
year_inc = year_inc[(year_inc > 2000) & (year_inc < 2041)]
# We get initial spline (not taking inflation into account)
inc_year_noinfla = interp1d(
np.array([2001, 2011, 2040]) - param["baseline_year"],
[average_income_retrospect, average_income_baseline,
average_income_long_fut],
'linear')
# We stock it into an array with the right number of periods
inc_ref = inc_year_noinfla(
year_inc - param["baseline_year"]
)
# We get a schedule for inflation growth rates with respect to baseline
noinfla_ref = np.ones(year_inc[(year_inc <= param["baseline_year"])].size)
infla_ref = spline_inflation(
year_inc[(year_inc > param["baseline_year"])] - param["baseline_year"]
) / spline_inflation(0)
infla_schedule = np.append(noinfla_ref, infla_ref)
# Then we correct output from initial spline with inflation growth rates
inc_year_infla = inc_ref * infla_schedule
# And we get the final spline
spline_income = interp1d(
year_inc - param["baseline_year"], inc_year_infla, 'linear')
return (spline_agricultural_price, spline_interest_rate,
spline_population_income_distribution, spline_inflation,
spline_income_distribution, spline_population,
spline_income, spline_minimum_housing_supply,
spline_fuel)
[docs]def compute_average_income(spline_population_income_distribution,
spline_income_distribution, param, t):
"""
Compute average income and population per income group for a given year.
This allows to update the relative distributions used to compute the
equilibrium in subsequent periods (see equilibrium.compute_equilibrium).
Parameters
----------
spline_population_income_distribution : interp1d
Linear interpolation for total population per income group in the data
(12) over the years (baseline year set at 0)
spline_income_distribution : interp1d
Linear interpolation for median annual income (in rands) per income
group in the data (12) over the years (baseline year set at 0)
param : dict
Dictionary of default parameters
t : int
Year for which we want to run the function
Returns
-------
avg_income_group : ndarray(float64)
Average median income for each income group in the model (4)
total_group : ndarray(float64)
Exogenous total number of households per income group (excluding people
out of employment, for 4 groups)
"""
total_bracket = spline_population_income_distribution(t)
avg_income_bracket = spline_income_distribution(t)
avg_income_group = np.zeros(4)
total_group = np.zeros(4)
for j in range(0, 4):
total_group[j] = sum(
total_bracket[param["income_distribution"] == j + 1])
avg_income_group[j] = sum(
avg_income_bracket[param["income_distribution"] == j + 1]
* total_bracket[param["income_distribution"] == j + 1]
) / total_group[j]
return avg_income_group, total_group
[docs]def interpolate_interest_rate(spline_interest_rate, t):
"""
Return real interest rate used in model, for a given year.
Parameters
----------
spline_interest_rate : interp1d
Linear interpolation for the interest rate (in %) over the years
t : int
Year for which we want to run the function
Returns
-------
float64
Real interest rate used in the model, and defined as the average over
past (3) years to convey the structural (as opposed to conjonctural)
component of the interest rate
"""
nb_years = 3
interest_rate_n_years = spline_interest_rate(np.arange(t - nb_years, t))
interest_rate_n_years[interest_rate_n_years < 0] = np.nan
return np.nanmean(interest_rate_n_years)/100
[docs]def evolution_housing_supply(housing_limit, param, t1, t0,
housing_supply_1, housing_supply_0):
"""
Yield dynamic housing supply with time inertia and capital depreciation.
We consider that formal private developers anticipate the unconstrained
equilibrium value of their housing supply in future periods. Only if it is
bigger than current values do we allow them to build more housing. In all
cases, housing capital depreciates. This function computes a new housing
supply including a time inertia parameter, that will be the (more
realistic) housing supply simulated for target year (all the other
equilibrium values will be updated correspondingly under this constraint).
Then, the function returns the difference between simulated future and
current values for housing supply.
Parameters
----------
housing_limit : Series
Maximum housing supply (in m² per km²) in each grid cell (24,014)
param : Dict
Dictionary of default parameters
t1 : float64
Target year (relative to baseline set at 0) for evolution of housing
supply
t0 : float64
Origin year (relative to baseline set at 0) for evolution of housing
supply
housing_supply_1 : ndarray(float64)
(Unconstrained) equilibrium housing supply per unit of available land
(in m² per km²) for target year, per grid cell (24,014)
housing_supply_0 : TYPE
Equilibrium housing supply per unit of available land
(in m² per km²) for origin year, per grid cell (24,014)
Returns
-------
Series
Difference between simulated future and current values for housing
supply per unit of available land (in m² per km²), per grid cell
(24,014).
"""
# New housing supply (accounting for inertia and depreciation with time)
# See technical documentation for math formula
if t1 - t0 > 0:
# Yields the difference in housing supply (if growing) weighted by
# time inertia, minus housing stock depreciation
diff_housing = ((housing_supply_1 - housing_supply_0)
* (housing_supply_1 > housing_supply_0)
* (t1 - t0) / param["time_invest_housing"]
- housing_supply_0 * (t1 - t0)
* param["depreciation_rate"])
# This allows to run backward simulations
else:
diff_housing = ((housing_supply_1 - housing_supply_0)
* (housing_supply_1 < housing_supply_0)
* (t1 - t0) / param["time_invest_housing"]
- housing_supply_0 * (t1 - t0)
* param["depreciation_rate"])
# We set the target constrained equilibrium housing supply target
housing_supply_target = housing_supply_0 + diff_housing
# We bound it upwards as housing height is limited by potential regulations
housing_supply_target = np.minimum(housing_supply_target, housing_limit)
# We also consider minimum housing supply in the future (useful for ad hoc
# corrections on Mitchells Plain)
minimum_housing_supply_interp = interp1d(
np.array([2001, 2011, 2100]) - param["baseline_year"],
np.transpose([np.zeros(len(param["minimum_housing_supply"])),
param["minimum_housing_supply"],
param["minimum_housing_supply"]])
)
minimum_housing_supply_interp = minimum_housing_supply_interp(t1)
# We also bound it downwards
housing_supply_target = np.maximum(
housing_supply_target, minimum_housing_supply_interp)
# Finally, we return diff_housing after taking corrections into account
return housing_supply_target - housing_supply_0