mirosh111000

Прикладна_економетрика_ЛР№4_Мірошниченко

Oct 27th, 2025
723
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.59 KB | None | 0 0
  1. import numpy as np
  2. import pandas as pd
  3. import statsmodels.api as sm
  4. from scipy.stats import t as tdist, f as fdist, spearmanr
  5. import matplotlib.pyplot as plt
  6.  
  7. def print_table(df: pd.DataFrame, title: str, floatfmt="{:,.6f}".format):
  8.     print("=" * (len(title) + 2))
  9.     print(title)
  10.     print("=" * (len(title) + 2))
  11.     df_print = df.copy()
  12.     for c in df_print.columns:
  13.         if np.issubdtype(df_print[c].dtype, np.number):
  14.             df_print[c] = df_print[c].map(lambda v: floatfmt(v))
  15.     print(df_print.to_string(index=False))
  16.     print()
  17.  
  18. def _add_const(x):
  19.     return sm.add_constant(np.asarray(x, float))
  20.  
  21. def table1_base(x, y):
  22.     x = np.asarray(x, float); y = np.asarray(y, float)
  23.     df = pd.DataFrame({
  24.         "i": np.arange(1, len(x)+1),
  25.         "x": x, "y": y,
  26.         "x^2": x**2, "x*y": x*y, "y^2": y**2
  27.     })
  28.     print_table(df, "Таблиця 1. Базові розрахунки")
  29.     return df
  30.  
  31. def fit_ols(x, y):
  32.     X = _add_const(x)
  33.     model = sm.OLS(y, X).fit()
  34.     a0, a1 = model.params
  35.     se0, se1 = model.bse
  36.     t0, t1 = model.tvalues
  37.     p0, p1 = model.pvalues
  38.     r2 = model.rsquared
  39.     rss = np.sum(model.resid**2)
  40.     df = int(model.df_resid)
  41.     df_out = pd.DataFrame([{
  42.         "a0": a0, "a1": a1, "SE(a0)": se0, "SE(a1)": se1,
  43.         "t(a0)": t0, "p(a0)": p0, "t(a1)": t1, "p(a1)": p1,
  44.         "R2": r2, "RSS": rss, "df": df
  45.     }])
  46.     print_table(df_out, "OLS: параметри та статистики")
  47.     yhat = model.fittedvalues
  48.     u = model.resid
  49.     df2 = pd.DataFrame({
  50.         "i": np.arange(1, len(x)+1),
  51.         "x": x, "y": y, "ŷ": yhat, "u": u, "u^2": u**2, "|u|": np.abs(u)
  52.     })
  53.     print_table(df2, "Таблиця 2. Залишки та прогнози (OLS)")
  54.     return model, df2
  55.  
  56. def var_bins(x, vals, nbins=3):
  57.     order = np.argsort(x)
  58.     x_sorted = np.asarray(x)[order]
  59.     v_sorted = np.asarray(vals)[order]
  60.     groups = np.array_split(np.arange(len(x)), nbins)
  61.     rows = []
  62.     for b, idx in enumerate(groups, start=1):
  63.         xx = x_sorted[idx]
  64.         vv = v_sorted[idx]
  65.         rows.append({"bin": b, "x_min": xx.min(), "x_max": xx.max(),
  66.                      "n": len(idx), "Var(u)": vv.var(ddof=1)})
  67.     return pd.DataFrame(rows)
  68.  
  69. def show_bins_ols(x, u):
  70.     dfbins = var_bins(x, u, 3)
  71.     print_table(dfbins, "Таблиця 3. Розкид Var(u) у 3 біннах x (OLS)")
  72.     return dfbins
  73.  
  74. def spearman_test(x, z, alpha=0.05, label="|u|"):
  75.     rho, p = spearmanr(x, z)
  76.     n = len(x)
  77.     if abs(rho) < 1.0:
  78.         t_stat = rho * np.sqrt((n - 2) / (1 - rho**2))
  79.     else:
  80.         t_stat = np.sign(rho) * np.inf
  81.     tcrit = tdist.ppf(1 - alpha/2, df=n-2)
  82.     out = pd.DataFrame([{
  83.         "rho": rho, "t_stat": t_stat, "t_crit(α/2)": tcrit,
  84.         "p_value": p, "reject_H0": bool(p < alpha)
  85.     }])
  86.     print_table(
  87.         pd.DataFrame({
  88.             "i": np.arange(1, n+1),
  89.             "x": x, f"|{label}|": z,
  90.             "rank_x": pd.Series(x).rank(method="average"),
  91.             f"rank_|{label}|": pd.Series(z).rank(method="average"),
  92.             "(Δrank)^2": (pd.Series(x).rank(method="average")
  93.                           - pd.Series(z).rank(method="average"))**2
  94.         }),
  95.         f"Таблиця 4. Спірмен — ранги x та |{label}|"
  96.     )
  97.     print_table(out, "Підсумок тесту Спірмена")
  98.     return rho, p
  99.  
  100. def gq_groups(x, y, m=4, c=2):
  101.     order = np.argsort(x)
  102.     x_sorted, y_sorted = np.asarray(x)[order], np.asarray(y)[order]
  103.     left_idx = np.arange(0, m)
  104.     right_idx = np.arange(len(x_sorted)-m, len(x_sorted))
  105.     mid_cut = slice(m, len(x_sorted)-m)
  106.     if c > 0:
  107.         right_idx = np.arange(len(x_sorted)-m, len(x_sorted))
  108.     Xl = _add_const(x_sorted[left_idx]); yl = y_sorted[left_idx]
  109.     Xr = _add_const(x_sorted[right_idx]); yr = y_sorted[right_idx]
  110.     ols_l = sm.OLS(yl, Xl).fit()
  111.     ols_r = sm.OLS(yr, Xr).fit()
  112.     left_tbl = pd.DataFrame({
  113.         "i": order[left_idx] + 1,
  114.         "x": x_sorted[left_idx],
  115.         "y": yl,
  116.         "ŷ(group)": ols_l.fittedvalues,
  117.         "u(group)": ols_l.resid,
  118.         "u^2(group)": ols_l.resid**2
  119.     })
  120.     right_tbl = pd.DataFrame({
  121.         "i": order[right_idx] + 1,
  122.         "x": x_sorted[right_idx],
  123.         "y": yr,
  124.         "ŷ(group)": ols_r.fittedvalues,
  125.         "u(group)": ols_r.resid,
  126.         "u^2(group)": ols_r.resid**2
  127.     })
  128.     return left_tbl.sort_values("i"), right_tbl.sort_values("i"), ols_l, ols_r
  129.  
  130. def gq_summary_compact(left_fit, right_fit, m, alpha=0.05):
  131.     var_left = left_fit.resid.var(ddof=1)
  132.     var_right = right_fit.resid.var(ddof=1)
  133.     F = var_left / var_right
  134.     df_m1 = m - 1
  135.     Fcrit = fdist.ppf(1 - alpha, df_m1, df_m1)
  136.     reject = bool(F > Fcrit)
  137.     out = pd.DataFrame([{
  138.         "m": m, "c": 2, "df_m1": df_m1,
  139.         "RSS_left": np.sum(left_fit.resid**2),
  140.         "RSS_right": np.sum(right_fit.resid**2),
  141.         "F_stat": F, "Fcrit_df_m1(one-sided)": Fcrit,
  142.         "reject_H0": reject
  143.     }])
  144.     return out
  145.  
  146. def run_gq(x, y, m=4, c=2, alpha=0.05, prefix=""):
  147.     left_tbl, right_tbl, ols_l, ols_r = gq_groups(x, y, m, c)
  148.     print_table(left_tbl, f"Таблиця 5. Група LEFT для тесту ГК (за x↑)")
  149.     print_table(right_tbl, f"Таблиця 6. Група RIGHT для тесту ГК (за x↑)")
  150.     compact = gq_summary_compact(ols_l, ols_r, m, alpha)
  151.     print_table(compact, "Зведена таблиця тесту Голдфелда–Квандта")
  152.     return compact
  153.  
  154. def glejser_table(x, yhat, uabs, deltas=(1,2,3,0.5,1/3), alpha=0.05, label="|u|"):
  155.     rows = []
  156.     long_tbl = pd.DataFrame({
  157.         "i": np.arange(1, len(x)+1),
  158.         "x": x, "y": y, "x^2": x**2, "ŷ": yhat, "|u|": uabs, "x*|u|": x*uabs
  159.     })
  160.     for delta in deltas:
  161.         X = _add_const(x**delta)
  162.         mod = sm.OLS(uabs, X).fit()
  163.         b0, b1 = mod.params
  164.         se_b1 = mod.bse[1]
  165.         t_ratio = abs(b1) / se_b1 if se_b1 > 0 else np.inf
  166.         tcrit = tdist.ppf(1 - alpha/2, df=len(x)-2)
  167.         pval = mod.pvalues[1]
  168.         rows.append({
  169.             "δ": delta, "b0": b0, "b1": b1, "SE(b1)": se_b1,
  170.             "|b1|/SE(b1)": t_ratio, "t_crit(α/2)": tcrit,
  171.             "p-value": pval, "Sε^2": np.mean(mod.resid**2),
  172.             "decision": "ВІДХИЛЯЄМО H0" if pval < alpha else "Приймаємо H0"
  173.         })
  174.         long_tbl[f"û({label})_δ={delta}"] = mod.fittedvalues
  175.         long_tbl[f"({label}-û)_δ={delta}^2"] = (uabs - mod.fittedvalues)**2
  176.  
  177.     short_tbl = pd.DataFrame(rows)
  178.     print_table(long_tbl, f"Таблиця 7а. Тест Глейзера — покрокова таблиця")
  179.     print_table(short_tbl, "Таблиця 7b. Тест Глейзера — параметри й рішення по δ")
  180.     return short_tbl, long_tbl
  181.  
  182. def choose_delta_for_weights(glejser_short, alpha=0.05):
  183.     ok = glejser_short[glejser_short["p-value"] >= alpha]
  184.     if len(ok) == 0:
  185.         best = glejser_short.sort_values("Sε^2").iloc[0]
  186.     else:
  187.         best = ok.iloc[ok["b1"].abs().argmin()]
  188.     return float(best["δ"]), best
  189.  
  190. def wls_fit_with_weights(x, y, delta, glejser_short):
  191.     row = glejser_short.loc[glejser_short["δ"] == delta].iloc[0]
  192.     b0, b1 = row["b0"], row["b1"]
  193.     sigma_hat = b0 + b1 * (x ** delta)
  194.     mn = sigma_hat.min()
  195.     if mn <= 0:
  196.         sigma_hat = sigma_hat + (abs(mn) + 1e-8)
  197.     w = 1.0 / (sigma_hat ** 2)
  198.     model = sm.WLS(y, _add_const(x), weights=w).fit()
  199.     df_out = pd.DataFrame([{
  200.         "a0": model.params[0], "a1": model.params[1],
  201.         "SE(a0)": model.bse[0], "SE(a1)": model.bse[1],
  202.         "t(a0)": model.tvalues[0], "p(a0)": model.pvalues[0],
  203.         "t(a1)": model.tvalues[1], "p(a1)": model.pvalues[1],
  204.         "R2": model.rsquared, "RSS": np.sum(model.resid**2), "df": int(model.df_resid)
  205.     }])
  206.     print_table(df_out, "WLS: параметри та статистики (після усунення)")
  207.     comp = pd.DataFrame({
  208.         "model": ["OLS", "WLS"],
  209.         "a0": [np.nan, np.nan],
  210.         "a1": [np.nan, np.nan],
  211.         "SE(a0)": [np.nan, np.nan],
  212.         "SE(a1)": [np.nan, np.nan],
  213.         "t(a0)": [np.nan, np.nan],
  214.         "p(a0)": [np.nan, np.nan],
  215.         "t(a1)": [np.nan, np.nan],
  216.         "p(a1)": [np.nan, np.nan],
  217.         "R2": [np.nan, np.nan],
  218.         "RSS": [np.nan, np.nan],
  219.         "df": [np.nan, np.nan],
  220.     })
  221.     return model, sigma_hat
  222.  
  223. def compare_ols_wls(ols_model, wls_model):
  224.     comp = pd.DataFrame({
  225.         "model": ["OLS", "WLS"],
  226.         "a0": [ols_model.params[0], wls_model.params[0]],
  227.         "a1": [ols_model.params[1], wls_model.params[1]],
  228.         "SE(a0)": [ols_model.bse[0], wls_model.bse[0]],
  229.         "SE(a1)": [ols_model.bse[1], wls_model.bse[1]],
  230.         "t(a0)": [ols_model.tvalues[0], wls_model.tvalues[0]],
  231.         "p(a0)": [ols_model.pvalues[0], wls_model.pvalues[0]],
  232.         "t(a1)": [ols_model.tvalues[1], wls_model.tvalues[1]],
  233.         "p(a1)": [ols_model.pvalues[1], wls_model.pvalues[1]],
  234.         "R2": [ols_model.rsquared, wls_model.rsquared],
  235.         "RSS": [np.sum(ols_model.resid**2), np.sum(wls_model.resid**2)],
  236.         "df": [int(ols_model.df_resid), int(wls_model.df_resid)]
  237.     })
  238.     print_table(comp, "Таблиця 8. Порівняння OLS vs WLS")
  239.     return comp
  240.  
  241. def standardized_residuals(y, yhat, sigma_hat):
  242.     u = y - yhat
  243.     rstar = u / sigma_hat
  244.     return u, rstar
  245.  
  246. def plot_diagnostics_ols(x, y, yhat, u, nbins=3):
  247.     fig, axes = plt.subplots(2, 2, figsize=(10, 8))
  248.     axes = axes.ravel()
  249.  
  250.     axes[0].scatter(x, u)
  251.     axes[0].axhline(0, linewidth=1)
  252.     axes[0].set_title("OLS: u vs x"); axes[0].set_xlabel("x"); axes[0].set_ylabel("u")
  253.  
  254.     axes[1].scatter(yhat, u)
  255.     axes[1].axhline(0, linewidth=1)
  256.     axes[1].set_title("OLS: u vs ŷ"); axes[1].set_xlabel("ŷ"); axes[1].set_ylabel("u")
  257.  
  258.     axes[2].scatter(x, np.abs(u))
  259.     axes[2].set_title("OLS: |u| vs x"); axes[2].set_xlabel("x"); axes[2].set_ylabel("|u|")
  260.  
  261.     bins = var_bins(x, u, nbins)
  262.     axes[3].bar(bins["bin"], bins["Var(u)"])
  263.     axes[3].set_title("OLS: Var(u) у біннах x"); axes[3].set_xlabel("бін"); axes[3].set_ylabel("Var")
  264.     fig.suptitle("Візуальна діагностика — OLS")
  265.     plt.tight_layout()
  266.     plt.show()
  267.  
  268. def plot_diagnostics_wls(x, yhat_w, rstar, nbins=3):
  269.     fig, axes = plt.subplots(2, 2, figsize=(10, 8))
  270.     axes = axes.ravel()
  271.  
  272.     axes[0].scatter(x, rstar)
  273.     axes[0].axhline(0, linewidth=1)
  274.     axes[0].set_title("Після WLS (r*): r* vs x"); axes[0].set_xlabel("x"); axes[0].set_ylabel("r*")
  275.  
  276.     axes[1].scatter(yhat_w, rstar)
  277.     axes[1].axhline(0, linewidth=1)
  278.     axes[1].set_title("Після WLS (r*): r* vs ŷ"); axes[1].set_xlabel("ŷ"); axes[1].set_ylabel("r*")
  279.  
  280.     axes[2].scatter(x, np.abs(rstar))
  281.     axes[2].set_title("Після WLS (r*): |r*| vs x"); axes[2].set_xlabel("x"); axes[2].set_ylabel("|r*|")
  282.  
  283.     bins = var_bins(x, rstar, nbins)
  284.     axes[3].bar(bins["bin"], bins["Var(u)"])
  285.     axes[3].set_title("Після WLS (r*): Var(r*) у біннах x"); axes[3].set_xlabel("бін"); axes[3].set_ylabel("Var")
  286.     fig.suptitle("Візуальна діагностика — Після WLS (r*)")
  287.     plt.tight_layout()
  288.     plt.show()
  289.  
  290. def run_pipeline(x, y, alpha=0.05, deltas=(1,2,3,0.5,1/3), m=4, c=2, make_plots=True):
  291.     x = np.asarray(x, float); y = np.asarray(y, float)
  292.  
  293.     table1_base(x, y)
  294.  
  295.     ols_model, tbl2 = fit_ols(x, y)
  296.     yhat = ols_model.fittedvalues
  297.     u = ols_model.resid
  298.     show_bins_ols(x, u)
  299.     spearman_test(x, np.abs(u), alpha, label="u")
  300.  
  301.     gq_compact_ols = run_gq(x, y, m=m, c=c, alpha=alpha)
  302.  
  303.     glejser_short, glejser_long = glejser_table(x, yhat, np.abs(u), deltas, alpha)
  304.  
  305.     delta_best, row_best = choose_delta_for_weights(glejser_short, alpha)
  306.  
  307.     wls_model, sigma_hat = wls_fit_with_weights(x, y, delta_best, glejser_short)
  308.     compare_ols_wls(ols_model, wls_model)
  309.  
  310.     yhat_w = wls_model.fittedvalues
  311.     u_w, rstar = standardized_residuals(y, yhat_w, sigma_hat)
  312.  
  313.     df_r = pd.DataFrame({
  314.         "i": np.arange(1, len(x)+1), "x": x, "y": y,
  315.         "ŷ(WLS)": yhat_w, "u(WLS)": u_w, "r*(WLS)": rstar,
  316.         "r*^2(WLS)": rstar**2, "|r*|(WLS)": np.abs(rstar)
  317.     })
  318.     print_table(df_r, "Таблиця 2*. Прогнози та залишки (WLS) з r* = u/σ̂")
  319.  
  320.     df_bins_r = var_bins(x, rstar, 3).rename(columns={"Var(u)": "Var(r*)"})
  321.     print_table(df_bins_r, "Таблиця 3*. Розкид Var(r*) у 3 біннах x (після WLS)")
  322.  
  323.     spearman_rho_r, p_r = spearman_test(x, np.abs(rstar), alpha, label="r*")
  324.  
  325.     left_tbl_r, right_tbl_r, fit_l_r, fit_r_r = gq_groups(x, rstar, m, c)
  326.     left_r = pd.DataFrame({"i": left_tbl_r["i"], "x": left_tbl_r["x"], "r*(WLS)": fit_l_r.resid + fit_l_r.fittedvalues, "r*(WLS)^2": (fit_l_r.resid + fit_l_r.fittedvalues)**2})
  327.     right_r = pd.DataFrame({"i": right_tbl_r["i"], "x": right_tbl_r["x"], "r*(WLS)": fit_r_r.resid + fit_r_r.fittedvalues, "r*(WLS)^2": (fit_r_r.resid + fit_r_r.fittedvalues)**2})
  328.     print_table(left_r, "Таблиця 5*. LEFT (стандартизовані r*) для тесту ГК")
  329.     print_table(right_r, "Таблиця 6*. RIGHT (стандартизовані r*) для тесту ГК")
  330.  
  331.     gq_compact_r = gq_summary_compact(fit_l_r, fit_r_r, m, alpha)
  332.     print_table(gq_compact_r, "Зведення тесту Голдфелда–Квандта на r* (F, df=m−1)")
  333.  
  334.     glejser_r_short, glejser_r_long = glejser_table(
  335.         x, yhat_w, np.abs(rstar), deltas, alpha, label="r*"
  336.     )
  337.  
  338.     homosk_after_wls = (
  339.         (p_r >= alpha) and
  340.         (not bool(gq_compact_r["reject_H0"].iloc[0])) and
  341.         all(glejser_r_short["p-value"] >= alpha)
  342.     )
  343.     summary_line = f"ПІДСУМОК: Гомоскедастичність після WLS (за r*): {'ТАК' if homosk_after_wls else 'НІ'}; модель ваг за Глейзером: δ={delta_best:g}"
  344.     print(summary_line)
  345.  
  346.     if make_plots:
  347.         plot_diagnostics_ols(x, y, yhat, u, nbins=3)
  348.         plot_diagnostics_wls(x, yhat_w, rstar, nbins=3)
  349.  
  350.     return {
  351.         "ols_model": ols_model,
  352.         "wls_model": wls_model,
  353.         "sigma_hat": sigma_hat,
  354.         "glejser_ols": glejser_short,
  355.         "glejser_wls_r": glejser_r_short,
  356.         "gq_ols_compact": gq_compact_ols,
  357.         "gq_r_compact": gq_compact_r,
  358.         "homosked_after_wls": homosk_after_wls,
  359.         "delta_best": delta_best
  360.     }
  361.  
  362. if __name__ == "__main__":
  363.     x = [0.3, 0.6, 1.1, 0.2, 0.8, 1.6, 1.5, 0.9, 0.9, 0.8]
  364.     y = [2.8, 4.3, 6.3, 2.9, 5.3, 8.3, 8.0, 5.7, 5.4, 5.2]
  365.  
  366.     run_pipeline(x, y, alpha=0.05, deltas=(1,2,3,0.5,1/3), m=4, c=2, make_plots=True)
  367.  
Advertisement
Add Comment
Please, Sign In to add comment