Построение моделей на панельных данных в Python, часть 2: метод “разность разностей” (diff-in-diff).
Привет, Хабр!
Данная статья является продолжением статьи “Построение моделей на панельных данных в Python, часть 1: объединенный МНК, модель с фиксированными эффектами, модель со случайными эффектами”. Напомню задачу: необходимо оценить, как изменились цены в отелях на островах Сардинии и Корсики после введения закона Макрона на острове Корсика.
Рассчитываем эффект воздействия методом разность разностей (diff-in-diff). Перед формальным описанием модели перечислю основные факторы, которые могут влиять на цену отеля:
специфические особенности острова, в котором расположен отель (эффект острова);
особенности различных периодов времени, например, изменение экономической конъюнктуры (временной эффект);
эффект анонса закона Макрона (тот самый эффект, который мы пытаемся оценить).
Формально можем записать так: Yist =αs +μt +δ⋅Dist +εist, где индекс i - номер отеля, индекс s - остров (Корсика или Сардиния), индекс t - момент времени (период до анонса закона Макрона или период после него). Yist - цена отелей; переменная D равна 1 в отелях, которые находились на Корсике (и соответственно, на которые распространяется закон Макрона) и равно 0 во всех остальных случаях (если отели находились в Сардинии и на них закон Макрона не распространялся). αs - эффект острова, он имеет два значения: αcontrol, если наблюдение относится к контрольной группе, т.е. к Сардинии; αtreatment, если наблюдение относится к испытуемой группе, т.е. к Корсике. μt - временной эффект. Он равен μbefore до анонса закона Макрона и μafter после анонса закона, δ — эффект воздействия анонса закона Макрона. Это тот самый эффект, который требуется оценить; εist — случайные ошибки модели.
Подробные выкладки можно посмотреть в учебнике Картаева в главе 11[2].
Метод разности разностей непосредственно связан с оценкой моделей при помощи регрессий. Так как мы располагаем панельными данными об объектах из испытуемой и контрольной групп за два периода (до и после введения закона Макрона), оценка метода разности разностей может быть получена в результате применения следующей модели:
Yit = β0 + β1 ⋅ Xi + β2 ⋅ Zt + δ ⋅ Xi ⋅ Zt + εit, где Xi — бинарная переменная, которая равна единице, если i-й отель находится на Корсике, Zt - бинарная переменная, которая равна единице для всех наблюдений, относящихся ко второму периоду (периоду после анонса закона).
Для состоятельности оценки требуется экзогенность объясняющей переменной. Если регрессор оказывается эндогенным из-за пропуска других существенных факторов, влияющих на Y, то эта проблема может быть решена включением в уравнение контрольных переменных.
Важная предпосылка метода разность разностей - параллельные претренды. Именно эта предпосылка позволяет предполагать, что за два периода t=0 и t=1 динамика Y (в нашем случае цены на отелей) в тритмент-группе, если бы она не подверглась воздействию, была бы такой же, как и динамика Y в контрольной группе. Проверим соблюдение этой предпосылки на наших данных.
Претренды показателя “средняя цена за неделю” сохраняются до вступления закона в силу сохраняются(см. Рисунок 2).
import matplotlib.pyplot as plt
trends= df.groupby(['week_number', 'region'], as_index=False).agg({'eurP': 'mean'})
trends['region'] = trends['region'].apply(lambda x: 1 if x == 'Corsica' else 0)
fig, axes = plt.subplots(figsize=(9,6), dpi=80)
plt.plot(trends[trends['region'] == 1]['week_number'], trends[trends['region'] == 1]['eurP'], label = 'о. Корсика', color = 'dodgerblue')
plt.plot(trends[trends['region'] == 0]['week_number'], trends[trends['region'] == 0]['eurP'], label = 'о. Сардиния', color = 'deepskyblue')
plt.axvline(x = 28, color = 'lightsteelblue', label = "Закон обнародовали")
plt.axvline(x = 32, color = 'slategrey', label = "Закон вступил в силу")
plt.legend()
plt.xlabel('Номер недели', fontsize=12)
plt.ylabel('Цена за 1 номер за 1 день, евро', fontsize=12)
plt.title('Тренды средней цены на Корсике и в Сардинии', fontsize=14)
plt.show()
Рисунок 2: Тренды средней цены за неделю на Корсике и в Сардинии
Для построения моделей методом “разность разностей” добавим бинарные переменные.
def adding_dummies(df, col_names):
for column_name in col_names:
dummies =pd.DataFrame(pd.get_dummies(df[column_name],
prefix=column_name))
df=df.merge(dummies, left_index=True, right_index=True)
return(df)
df=adding_dummies(df, ['Star rating', 'Chain affiliation',
'hot_size', 'region'])
И предобработаем данные следующим образом: если в модель мы добавим переменную PostLaw_Treated_NoChain, которая равна PostLaw* treat* Chain affiliation_0, то коэффициент при ней будет показывать насколько изменилась цена в отелях, которые “не сетевые”, на Корсике (где закон Макрона был принят) по сравнению с ценами в отелях, которые “не сетевые”, но уже в Сардинии (где закон не был принят). Введение таких новых переменных позволит нам сделать вывод об изменении цен в разрезе разных категорий.
df=df.rename(columns={'google_src': 'Google_Searchers',
'town_avail': 'Hotel_availability'})
df['Postlaw_Treated']=df['postlaw']*df['treat']
df['PostLaw_Treated_NoChain']= df['postlaw']*df['treat']*df['Chain affiliation_0']
df['PostLaw_Treated_Chain']= df['postlaw']*df['treat']*df['Chain affiliation_1']
df['PostLaw_Treated_0stars']= df['postlaw']*df['treat']*df['Star rating_0']
df['PostLaw_Treated_1stars']= df['postlaw']*df['treat']*df['Star rating_1']
df['PostLaw_Treated_2stars']= df['postlaw']*df['treat']*df['Star rating_2']
df['PostLaw_Treated_3stars']= df['postlaw']*df['treat']*df['Star rating_3']
df['PostLaw_Treated_4stars']= df['postlaw']*df['treat']*df['Star rating_4']
df['PostLaw_Treated_5stars']= df['postlaw']*df['treat']*df['Star rating_5']
df['PostLaw_Treated_small_size']= df['postlaw']*df['treat']*df['hot_size_0']
df['PostLaw_Treated_medium_size']= df['postlaw']*df['treat']*df['hot_size_1']
df['PostLaw_Treated_large_size']= df['postlaw']*df['treat']*df['hot_size_2']
df['DaysCorsica']= df['bdays']*df['region_Corsica']
df['DaysSardinia']= df['bdays']*df['region_Sardinia']
df['const']=1
Строим модели. В модели добавляем контрольные переменные: количество дней с момента введения закона (DaysCorsica, DaysSardinia) для оценки долгосрочности эффекта, количество запросов в Google (Google_Searchers) и количество доступных отелей (Hotel_availability).
from linearmodels import IV2SLS
from collections import OrderedDict
from linearmodels.iv.results import compare
mod11v='lprice100 ~ Postlaw_Treated + DaysCorsica + DaysSardinia + Google_Searchers + Hotel_availability - 1'
reg11v = IV2SLS.from_formula(mod11v,df).fit(cov_type="robust")
mod22v='lprice100 ~ PostLaw_Treated_NoChain + PostLaw_Treated_Chain + DaysCorsica + DaysSardinia + Google_Searchers + Hotel_availability - 1'
reg22v = IV2SLS.from_formula(mod22v,df).fit(cov_type="robust")
mod33v='lprice100 ~ PostLaw_Treated_0stars + PostLaw_Treated_1stars + PostLaw_Treated_2stars + PostLaw_Treated_3stars + PostLaw_Treated_4stars + PostLaw_Treated_5stars + DaysCorsica + DaysSardinia + Google_Searchers + Hotel_availability -1'
reg33v = IV2SLS.from_formula(mod33v,df).fit(cov_type="robust")
mod44v='lprice100 ~ PostLaw_Treated_small_size + PostLaw_Treated_medium_size + PostLaw_Treated_large_size + DaysCorsica + DaysSardinia + Google_Searchers + Hotel_availability - 1'
reg44v = IV2SLS.from_formula(mod44v,df).fit(cov_type="robust")
res1v = OrderedDict()
res1v["D-in-D"] = reg11v
res1v["Triple_Chains"] = reg22v
res1v["Triple_Stars"] = reg33v
res1v["Triple_Size"] = reg44v
result = compare(res1v, stars=True, precision='std_errors')
print(result)
Исходя из построенных моделей, мы можем сделать следующие выводы:
введение закона Макрона повышает цену отелей, так как коэффициент при Postlaw_Treated > 0. Но увеличение цены происходит только в краткосрочном периоде. С увеличением количества дней, которые прошли с момента введения закона (с 6 августа 2015 года) цена на отели снижается: коэффициенты при DaysCorsica и DaysSardinia отрицательные. Причем на Сардинии цены снижаются быстрее и краткосрочный рост цен на отели ниже, чем на Корсике, потому что коэффициент при DaysSardinia по модулю больше коэффициента при DaysCorsica.
Цены на сетевые отели выросли больше, чем цены на несетевые отели, так как коэффициент при PostLaw_Treated_Chain > коэффициента при PostLaw_Treated_NoChain.
Чем больше количество звезд у отеля, тем сильнее увеличилась цена после введения закона Макрона, кроме однозвездочных отелей - на них цена снизилась после введения закона (коэффициент при PostLaw_Treated_1stars < 0).
Результаты построения моделей методом разность разностей дали нам более содержательную интерпретацию, чем построение модели объединенного МНК, моделей с фиксированными и случайными эффектами.
Скачать код можно тут.
[1] Andrea Mantovani, Claudio A. Piga, Carlo Reggiani, Online platform price parity clauses: Evidence from the EU Booking.com case, European Economic Review, Volume 131, 2021, 103625, ISSN 0014-2921, https://doi.org/10.1016/j.euroecorev.2020.103625.
[2] Филипп Картаев “Введение в эконометрику”: учебник. – М.: Экономический факультет МГУ имени М.В. Ломоносова, 2019. – 472 с. ISBN 978-5-906932-22-8