ПРОВЕРКА ПРЕДПОСЫЛКИ МОДЕЛИ О ПАРАЛЛЕЛЬНОСТИ ТРЕНДОВ
region_means <- data %>%
filter(region %in% c(12, 137, 129), id_w >= 14 & id_w <= 25) %>%
group_by(region, id_w) %>%
summarise(mean_cons = mean(cons, na.rm = TRUE))
## `summarise()` has grouped output by 'region'. You can override using the
## `.groups` argument.
ggplot(region_means, aes(x = id_w, y = mean_cons, color = factor(region))) +
geom_line() +
geom_point() +
labs(
title = "Среднее потребление электроэнергии для регионов",
subtitle = "Волны с 14 по 25",
x = "Волна (id_w)",
y = "Среднее потребление электроэнергии",
color = "Регион"
) +
scale_x_continuous(breaks = 14:25) +
theme_minimal() +
theme(legend.position = "bottom")

ПОСТРОЕНИЕ БАЗОВОЙ И РАСШИРЕННОЙ МОДЕЛЕЙ РЕГРЕССИЙ
DIFF-IN-DIFFS
did_data <- data %>%
filter(region %in% c(129, 137))
did_data <- did_data %>%
mutate(
treatment = ifelse(region == 137, 1, 0),
post = ifelse(id_w > 22, 1, 0), # 22 bc id_w=22 values represent pre and post periods
treatment_effect = treatment * post)
# 1. Basic DiD model without controls using lm instead of feols
basic_model <- lm(cons ~ treatment + post + treatment_effect, data = did_data)
# 2. DiD model with controls using lm
control_vars <- c("c71", "c77", "c78", "c91a","e99a", "e123", "f14")
control_formula <- paste(control_vars, collapse = " + ")
full_formula <- paste("cons ~ treatment + post + treatment_effect +", control_formula)
full_model <- lm(as.formula(full_formula), data = did_data)
summary(basic_model)
##
## Call:
## lm(formula = cons ~ treatment + post + treatment_effect, data = did_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -63.93 -26.36 -13.35 11.40 933.07
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 63.9281 1.1799 54.182 <2e-16 ***
## treatment -0.3315 1.7628 -0.188 0.8508
## post 5.2901 2.3803 2.222 0.0263 *
## treatment_effect -2.3774 3.5039 -0.679 0.4975
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 44.91 on 3505 degrees of freedom
## Multiple R-squared: 0.001866, Adjusted R-squared: 0.001012
## F-statistic: 2.184 on 3 and 3505 DF, p-value: 0.0878
summary(full_model)
##
## Call:
## lm(formula = as.formula(full_formula), data = did_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -129.70 -20.59 -7.53 10.82 695.67
##
## Coefficients: (1 not defined because of singularities)
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.781e+01 4.159e+00 11.494 < 2e-16 ***
## treatment 5.020e+00 1.587e+00 3.163 0.00157 **
## post 1.094e+01 2.050e+00 5.337 1.00e-07 ***
## treatment_effect -5.273e+00 2.926e+00 -1.802 0.07167 .
## c71 -4.097e+00 1.524e+00 -2.689 0.00721 **
## c77 -2.106e-01 1.960e+00 -0.107 0.91445
## c78 -2.047e+01 3.477e+00 -5.888 4.28e-09 ***
## c91a 6.650e+00 1.313e+00 5.064 4.33e-07 ***
## e99a NA NA NA NA
## e123 2.525e-01 6.670e-03 37.864 < 2e-16 ***
## f14 -4.026e-04 2.045e-05 -19.690 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 37.27 on 3498 degrees of freedom
## (1 пропущенное наблюдение удалено)
## Multiple R-squared: 0.3135, Adjusted R-squared: 0.3117
## F-statistic: 177.5 on 9 and 3498 DF, p-value: < 2.2e-16
# Визуализация результатов DiD
# Рассчитаем средние значения потребления для групп до и после
plot_data <- did_data %>%
group_by(treatment, post) %>%
summarise(mean_cons = mean(cons, na.rm = TRUE), .groups = 'drop') %>%
mutate(
group = ifelse(treatment == 1, "137 (Целевой регион)", "129 (Контрольный регион)"),
period = ifelse(post == 1, "После", "До")
)
# Создание графика DiD
ggplot(plot_data, aes(x = period, y = mean_cons, group = group, color = group)) +
geom_line(size = 1) +
geom_point(size = 3) +
theme_minimal() +
labs(
title = "Влияние введения социальной нормы на потребление электроэнергии",
x = "Период",
y = "Среднее потребление",
color = "Регион"
)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

# Получение значения коэффициента взаимодействия и его p-значения
interaction_coef <- coef(summary(full_model))["treatment_effect", ]
cat("Коэффициент взаимодействия (DiD эффект):", interaction_coef[1], "\n")
## Коэффициент взаимодействия (DiD эффект): -5.272621
cat("Стандартная ошибка:", interaction_coef[2], "\n")
## Стандартная ошибка: 2.926371
cat("t-значение:", interaction_coef[3], "\n")
## t-значение: -1.801761
cat("p-значение:", interaction_coef[4], "\n")
## p-значение: 0.0716691
Вывод: введение соц нормы в 2014 году было связано со снижением
потребления электроэнергии на одного члена семьи в Ростовской об-ти на
5.02 киловатт
ВИЗУАЛИЗАЦИЯ
# 1. График с доверительными интервалами
did_ci_data <- did_data %>%
group_by(treatment, post) %>%
summarise(
mean_cons = mean(cons, na.rm = TRUE),
sd_cons = sd(cons, na.rm = TRUE),
n = n(),
se_cons = sd_cons / sqrt(n),
lower_ci = mean_cons - 1.96 * se_cons,
upper_ci = mean_cons + 1.96 * se_cons,
.groups = 'drop'
) %>%
mutate(
group = ifelse(treatment == 1, "137 (Целевой регион)", "129 (Контрольный регион)"),
period = ifelse(post == 1, "После", "До")
)
# График с доверительными интервалами
did_ci_plot <- ggplot(did_ci_data, aes(x = period, y = mean_cons, group = group, color = group)) +
geom_line(size = 1) +
geom_point(size = 3) +
geom_errorbar(aes(ymin = lower_ci, ymax = upper_ci), width = 0.2) +
theme_minimal() +
labs(
title = "Влияние социальной нормы на потребление электроэнергии",
subtitle = "С 95% доверительными интервалами",
x = "Период",
y = "Среднее потребление (кВт·ч)",
color = "Регион"
) +
theme(
legend.position = "bottom",
plot.title = element_text(face = "bold"),
axis.title = element_text(face = "bold"),
panel.grid.minor = element_blank()
)
print(did_ci_plot)

# 2. Барплот для сравнения изменений до и после
change_data <- did_data %>%
group_by(treatment, post) %>%
summarise(mean_cons = mean(cons, na.rm = TRUE), .groups = 'drop') %>%
pivot_wider(names_from = post, values_from = mean_cons) %>%
mutate(
change = `1` - `0`,
percent_change = (change / `0`) * 100,
group = ifelse(treatment == 1, "137 (Целевой регион)", "129 (Контрольный регион)")
)
bar_plot <- ggplot(change_data, aes(x = group, y = change, fill = group)) +
geom_col(width = 0.6) +
geom_text(aes(label = sprintf("%.2f", change)), vjust = -0.5) +
theme_minimal() +
labs(
title = "Абсолютное изменение в потреблении электроэнергии",
subtitle = "Сравнение целевого и контрольного регионов",
x = "",
y = "Изменение в потреблении (кВт·ч)",
fill = "Регион"
) +
theme(
legend.position = "none",
plot.title = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
percent_bar_plot <- ggplot(change_data, aes(x = group, y = percent_change, fill = group)) +
geom_col(width = 0.6) +
geom_text(aes(label = sprintf("%.1f%%", percent_change)), vjust = -0.5) +
theme_minimal() +
labs(
title = "Процентное изменение в потреблении электроэнергии",
subtitle = "Сравнение целевого и контрольного регионов",
x = "",
y = "Изменение в % от исходного уровня",
fill = "Регион"
) +
theme(
legend.position = "none",
plot.title = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
# 3. Распределение потребления по группам и периодам (плотность распределения)
density_plot <- ggplot(did_data, aes(x = cons, fill = interaction(treatment, post))) +
geom_density(alpha = 0.5) +
scale_fill_discrete(
name = "Группа и период",
labels = c("129 (Контрольный), До", "129 (Контрольный), После",
"137 (Целевой), До", "137 (Целевой), После")
) +
theme_minimal() +
labs(
title = "Распределение потребления электроэнергии",
subtitle = "По регионам и периодам до/после",
x = "Потребление (кВт·ч)",
y = "Плотность"
) +
theme(
legend.position = "bottom",
plot.title = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
# 4. Boxplot для сравнения распределений
boxplot <- ggplot(did_data, aes(x = interaction(treatment, post), y = cons, fill = factor(treatment))) +
geom_boxplot() +
scale_x_discrete(labels = c("Контрольный, До", "Целевой, До", "Контрольный, После", "Целевой, После")) +
scale_fill_discrete(
name = "Регион",
labels = c("129 (Контрольный)", "137 (Целевой)")
) +
theme_minimal() +
labs(
title = "Распределение потребления электроэнергии",
subtitle = "Боксплот по регионам и периодам",
x = "",
y = "Потребление (кВт·ч)"
) +
theme(
legend.position = "bottom",
plot.title = element_text(face = "bold"),
axis.title = element_text(face = "bold"),
axis.text.x = element_text(angle = 45, hjust = 1)
)
# 5. Визуализация эффекта DiD в виде arrows plot
did_arrow_data <- did_data %>%
group_by(treatment, post) %>%
summarise(mean_cons = mean(cons, na.rm = TRUE), .groups = 'drop') %>%
mutate(
group = ifelse(treatment == 1, "Целевой", "Контрольный"),
period = ifelse(post == 1, "После", "До"),
x_pos = ifelse(post == 0, 1, 2),
group_offset = ifelse(treatment == 1, 0.15, -0.15)
)
arrow_plot <- ggplot(did_arrow_data, aes(x = x_pos + group_offset, y = mean_cons, color = group)) +
geom_point(size = 3) +
geom_line(aes(group = group)) +
geom_text(aes(label = sprintf("%.1f", mean_cons)), vjust = -1) +
scale_x_continuous(
breaks = c(1, 2),
labels = c("До", "После"),
limits = c(0.7, 2.3)
) +
theme_minimal() +
labs(
title = "Эффект разности разностей (DiD)",
subtitle = "Изменение среднего потребления электроэнергии",
x = "",
y = "Среднее потребление (кВт·ч)",
color = "Регион"
) +
theme(
legend.position = "bottom",
plot.title = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
# Вывод всех графиков
print(did_ci_plot)

print(bar_plot)

print(percent_bar_plot)

print(density_plot)

print(boxplot)

print(arrow_plot)

ggsave("did_ci_plot.png", did_ci_plot, width = 8, height = 6, dpi = 300)
ggsave("did_bar_plot.png", bar_plot, width = 8, height = 6, dpi = 300)
ggsave("did_percent_bar_plot.png", percent_bar_plot, width = 8, height = 6, dpi = 300)
ggsave("did_density_plot.png", density_plot, width = 8, height = 6, dpi = 300)
ggsave("did_boxplot.png", boxplot, width = 8, height = 6, dpi = 300)
ggsave("did_arrow_plot.png", arrow_plot, width = 8, height = 6, dpi = 300)