Skip to content

Latest commit

 

History

History
328 lines (232 loc) · 9.53 KB

example_costs.org

File metadata and controls

328 lines (232 loc) · 9.53 KB

Costs

Preface

There are a few modules you may need to install (but avoid this if you can; you may need to restart your kernel afterwards):

#!pip install -r requirements.txt

Data

We’ll get data from two places. First, basic data, including a food conversion table and recommended daily intakes table can be found in a google spreadsheet.

Here are addresses of google sheets for different dataframes for the case of Uganda:

InputFiles = {'Expenditures':('1yVLriVpo7KGUXvR3hq_n53XpXlD5NmLaH1oOMZyV0gQ','Expenditures (2019-20)'),
              'Prices':('1yVLriVpo7KGUXvR3hq_n53XpXlD5NmLaH1oOMZyV0gQ','Prices'),
              'HH Characteristics':('1yVLriVpo7KGUXvR3hq_n53XpXlD5NmLaH1oOMZyV0gQ','HH Characteristics'),
              'FCT':('1yVLriVpo7KGUXvR3hq_n53XpXlD5NmLaH1oOMZyV0gQ','FCT'),
              'RDI':('1yVLriVpo7KGUXvR3hq_n53XpXlD5NmLaH1oOMZyV0gQ','RDI'),}

Prices, FCT, RDI

from eep153_tools.sheets import read_sheets
import numpy as np
import pandas as pd

def get_clean_sheet(key,sheet=None):

    df = read_sheets(key,sheet=sheet)
    df.columns = [c.strip() for c in df.columns.tolist()]

    df = df.loc[:,~df.columns.duplicated(keep='first')]

    df = df.drop([col for col in df.columns if col.startswith('Unnamed')], axis=1)

    df = df.loc[~df.index.duplicated(), :]

    return df

# Get prices
p = get_clean_sheet(InputFiles['Prices'][0],
                    sheet=InputFiles['Prices'][1])

if 'm' not in p.columns:  # Supply "market" indicator if missing
    p['m'] = 1

p = p.set_index(['t','m'])
p.columns.name = 'j'

p = p.apply(lambda x: pd.to_numeric(x,errors='coerce'))
p = p.replace(0,np.nan)

fct = get_clean_sheet(InputFiles['FCT'][0],
                    sheet=InputFiles['FCT'][1])

fct = fct.set_index('j')
fct.columns.name = 'n'

fct = fct.apply(lambda x: pd.to_numeric(x,errors='coerce'))

################## RDI, if available (consider using US) #####################
rdi = get_clean_sheet(InputFiles['RDI'][0],
                    sheet=InputFiles['RDI'][1])
rdi = rdi.set_index('n')
rdi.columns.name = 'k'

Pre-estimated Demand Systems

An instance r of cfe.Regression can be made persistent with r.to_pickle('my_result.pickle'), which saves the instance “on disk”, and can be loaded using cfe.regression.read_pickle. We use this method below to load data and demand system previously estimated for Uganda:

import cfe.regression as rgsn

r = rgsn.read_pickle('uganda_2019-20.pickle')  # Assumes you've already set this up e.g., in Project 3

Ceteris Paribus

We begin by setting up some benchmarks for prices and budgets, so the things we don’t want to change we can hold fixed.

Reference Prices

Choose reference prices. Here we’ll choose a particular year, and average prices across markets. If you wanted to focus on particular market you’d do this differently.

# Reference prices chosen from a particular time; average across place.
# These are prices per kilogram:
pbar = p.xs('2019-20',level='t').mean()
pbar = pbar[r.beta.index] # Only use prices for goods we can estimate

Budgets

Get food budget for all households, then find median budget:

import numpy as np

xhat = r.predicted_expenditures()

# Total food expenditures per household
xbar = xhat.groupby(['i','t','m']).sum()

# Reference budget
x0 = xbar.quantile(0.5)  # Household at 0.5 quantile is median

Changing one price, holding others fixed

Finally, define a function to change a single price in the vector $p$:

def my_prices(p0,p=pbar,j='Millet'):
    """
    Change price of jth good to p0, holding other prices fixed.
    """
    p = p.copy()
    p.loc[j] = p0
    return p

Mapping to Nutrition

Mapping to Nutrients

FCT

We’ve seen how to map prices and budgets into vectors of consumption quantities using cfe.Regression.demands. Next we want to think about how to map these into bundles of nutrients. The information needed for the mapping comes from a “Food Conversion Table” (or database, such as the USDA Food Data Central). We’ve already grabbed an FCT, let’s take a look:

fct

Food Quantities

Get quantities of food by dividing expenditures by prices:

qhat = (xhat.unstack('j')/pbar).dropna(how='all')

# Drop missing columns
qhat = qhat.loc[:,qhat.count()>0]

qhat

Derived Nutrient Demands

We need the index of the Food Conversion Table (FCT) to match up with the index of the vector of quantities demanded. To manage this we make use of the align method for pd.DataFrames:

# Create a new FCT and vector of consumption that only share rows in common:
fct0,c0 = fct.align(qhat.T,axis=0,join='inner')
print(fct0.index)

Now, since rows of fct0 and c0 match, we can obtain nutritional outcomes from the inner (or dot, or matrix) product of the transposed fct0 and c0:

# The @ operator means matrix multiply
N = fct0.T@c0

N  #NB: Uganda quantities are for previous 7 days

Of course, since we can compute the nutritional content of a vector of consumption goods c0, we can also use our demand functions to compute nutrition as a function of prices and budget.

def nutrient_demand(x,p):
    c = r.demands(x,p)
    fct0,c0 = fct.align(c,axis=0,join='inner')
    N = fct0.T@c0

    N = N.loc[~N.index.duplicated()]

    return N

With this nutrient_demand function in hand, we can see how nutrient outcomes vary with budget, given prices:

import numpy as np
import matplotlib.pyplot as plt

X = np.linspace(x0/5,x0*5,100)

UseNutrients = ['Protein','Energy','Iron','Calcium','Vitamin C']

df = pd.concat({myx:np.log(nutrient_demand(myx,pbar))[UseNutrients] for myx in X},axis=1).T
ax = df.plot()

ax.set_xlabel('log budget')
ax.set_ylabel('log nutrient')

Now how does nutrition vary with prices?

USE_GOOD = 'Oranges'

scale = np.geomspace(.01,10,50)

ndf = pd.DataFrame({s:np.log(nutrient_demand(x0/2,my_prices(pbar[USE_GOOD]*s,j=USE_GOOD)))[UseNutrients] for s in scale}).T

ax = ndf.plot()

ax.set_xlabel('log price')
ax.set_ylabel('log nutrient')

Simple Policy Experiments: Changing Prices

If price of a good increases/decreases, what’s the cost to the household? Ask a related question: If a price $p_1$ increases, how much income do we need to give to the household to make it just as well off as it was at the old prices?

Summarize this as the compensating variation associated with the price change.

./compensating_variation.png

Compensating Variation can also be measured as the (change in the) area under the Hicksian (or compensated) demand curve:

./compensating_variation_newexpenditure.png.

Marshallian vs. Hicksian Demand Curves

Let’s look at Marshallian & Hicksian demands—one way of thinking about the Hicksian (compensated) curves is that they eliminate the income effect associated with changing prices.

import matplotlib.pyplot as plt
%matplotlib inline

my_j = 'Millet'  # Interesting Ugandan staple

P = np.geomspace(.01,10,50)*pbar[my_j]

# Utility of median household, given prices
U0 = r.indirect_utility(x0,pbar)

plt.plot([r.demands(x0,my_prices(p0,j=my_j))[my_j] for p0 in P],P)
plt.plot([r.demands(U0,my_prices(p0,j=my_j),type="Hicksian")[my_j] for p0 in P],P)
plt.ylabel('Price')
plt.xlabel(my_j)
plt.legend(("Marshallian","Hicksian"))

Welfare measures

def compensating_variation(U0,p0,p1):
    x0 = r.expenditure(U0,p0)
    x1 = r.expenditure(U0,p1)

    return x1-x0

def revenue(U0,p0,p1,type='Marshallian'):
    """(Un)Compensated revenue from taxes changing vector of prices from p0 to p1.

    Note that this is only for *demand* side (i.e., if supply perfectly elastic).
    """
    
    dp = p1 - p0 # Change in prices

    c = r.demands(U0,p1,type=type)

    dp,c = dp.align(c,join='inner')

    return dp.T@c


def deadweight_loss(U0,p0,p1):
    """
    Deadweight loss of tax/subsidy scheme creating wedge in prices from p0 to p1.

    Note that this is only for *demand* side (i.e., if supply perfectly elastic).
    """
    cv = compensating_variation(U0,p0,p1)

    return cv - revenue(U0,p0,p1,type='Hicksian') 
    

Price Changes, Revenue, and Compensating Variation

Examine effects of price changes on revenue (if price change due to a tax or subsidy) and compensating variation.

fig, ax1 = plt.subplots()

ax1.plot(P,[compensating_variation(U0,pbar,my_prices(p0,j=my_j)) for p0 in P])
ax1.set_xlabel(f"Price of {my_j}")
ax1.set_ylabel("Compensating Variation")

ax1.plot(P,[revenue(U0,pbar,my_prices(p0,j=my_j),type='Hicksian') for p0 in P],'k')
ax1.legend(('Compensating Variation','Revenue'))
ax1.axhline(0)
ax1.axvline(pbar.loc[my_j])

Deadweight Loss

Differences between revenue and compensating variation is deadweight-loss:

fig, ax1 = plt.subplots()

ax1.plot(P,[deadweight_loss(U0,pbar,my_prices(p0,j=my_j)) for p0 in P])
ax1.set_xlabel("Price of %s" % my_j)
ax1.set_ylabel("Deadweight Loss")