import numpy as np
import pandas as pd
import math

data = {
    'x1': [32.8, 29.9, 30.12, 31.15, 25.25, 32.8, 29.9, 24.12, 20.15, 33.25], 
    'x2': [0.92, 0.46, 0.73, 0.64, 0.54, 0.74, 0.68, 0.46, 0.36, 0.95],       
    'x3': [20.0, 16.2, 14.2, 10.0, 24.1, 13.2, 18.0, 15.1, 15.2, 75.5]        
}

df = pd.DataFrame(data)
n = len(df)
m = len(df.columns)

print("=== Вхідні дані (Варіант 2) ===")
print(df)
print("-" * 50)

def manual_pearson_corr(x, y):
    x_arr = np.array(x)
    y_arr = np.array(y)
    
    x_mean = np.mean(x_arr)
    y_mean = np.mean(y_arr)
    
    numerator = np.sum((x_arr - x_mean) * (y_arr - y_mean))
    
    sum_sq_diff_x = np.sum((x_arr - x_mean) ** 2)
    sum_sq_diff_y = np.sum((y_arr - y_mean) ** 2)
    denominator = np.sqrt(sum_sq_diff_x * sum_sq_diff_y)
    
    return numerator / denominator

cols = df.columns
R_matrix = np.zeros((m, m))

for i in range(m):
    for j in range(m):
        if i == j:
            R_matrix[i, j] = 1.0
        else:
            r = manual_pearson_corr(df[cols[i]], df[cols[j]])
            R_matrix[i, j] = r

corr_matrix = pd.DataFrame(R_matrix, index=cols, columns=cols)

det_R = np.linalg.det(corr_matrix.values)
chi2_observed = - (n - 1 - (2 * m + 5) / 6) * np.log(det_R)

# df = m(m-1)/2 = 3(2)/2 = 3
# alpha = 0.05
# Таблиця: chi2(0.95, 3) ≈ 7.815
CHI2_CRITICAL_TABLE = 7.815 

print("\n>>> КРОК 1: Перевірка всього масиву (Критерій Пірсона)")
print(f"Кореляційна матриця R:\n{corr_matrix.round(4)}")
print(f"Визначник матриці det(R) = {det_R:.6f}")
print(f"Хі-квадрат спостережуване = {chi2_observed:.4f}")
print(f"Хі-квадрат критичне (табличне, df=3) = {CHI2_CRITICAL_TABLE}")

if chi2_observed > CHI2_CRITICAL_TABLE:
    print("ВИСНОВОК: Мультиколінеарність ІСНУЄ.")
    
    inv_matrix_values = np.linalg.inv(corr_matrix.values)
    inv_corr_matrix = pd.DataFrame(inv_matrix_values, index=cols, columns=cols)
    
    print("\n>>> КРОК 2: Оцінка кожної змінної (F-критерій)")
    print(f"Обернена матриця C:\n{inv_corr_matrix.round(4)}")
    
    # df1 = m - 1 = 2
    # df2 = n - m = 7
    # Таблиця: F(0.95, 2, 7) ≈ 4.737
    F_CRITICAL_TABLE = 4.737
    
    f_results = []
    collinear_vars = []
    
    for col in cols:
        c_kk = inv_corr_matrix.loc[col, col]
        F_obs = (c_kk - 1) * (n - m) / (m - 1)
        
        is_bad = F_obs > F_CRITICAL_TABLE
        if is_bad: collinear_vars.append(col)
        
        f_results.append({
            "Змінна": col,
            "c_kk": round(c_kk, 4),
            "F_спост": round(F_obs, 4),
            "F_крит": F_CRITICAL_TABLE,
            "Результат": "Мультиколінеарна" if is_bad else "Норма"
        })
    
    print(pd.DataFrame(f_results).to_string(index=False))
    
    print("\n>>> КРОК 3: Виявлення пар (t-критерій)")
    
    # df = n - m - 1 = 6
    # alpha = 0.05 (двостороння), тому шукаємо t(0.975, 6)
    # Таблиця: t(0.975, 6) ≈ 2.447
    T_CRITICAL_TABLE = 2.447
    
    pairs_results = []
    conflict_pairs = []
    
    for i in range(m):
        for j in range(i + 1, m):
            var1 = cols[i]
            var2 = cols[j]
            
            c_ij = inv_corr_matrix.loc[var1, var2]
            c_ii = inv_corr_matrix.loc[var1, var1]
            c_jj = inv_corr_matrix.loc[var2, var2]
            
            r_partial = -c_ij / math.sqrt(c_ii * c_jj)
            
            if abs(r_partial) >= 1.0:
                t_obs = float('inf')
            else:
                t_obs = (abs(r_partial) * math.sqrt(n - m - 1)) / math.sqrt(1 - r_partial**2)
            
            is_significant = t_obs > T_CRITICAL_TABLE
            
            if is_significant:
                conflict_pairs.append((var1, var2, r_partial))
            
            pairs_results.append({
                "Пара": f"{var1} - {var2}",
                "r_частковий": round(r_partial, 4),
                "t_спост": round(t_obs, 4),
                "t_крит": T_CRITICAL_TABLE,
                "Зв'язок": "Є мультикол." if is_significant else "Немає"
            })
            
    print(pd.DataFrame(pairs_results).to_string(index=False))
    
    print("\n" + "="*50)
    print("РІШЕННЯ ЩОДО КОРИГУВАННЯ МОДЕЛІ")
    print("="*50)
    
    if conflict_pairs:
        print("Виявлено проблемні пари:")
        variables_to_drop = set()
        for v1, v2, r in conflict_pairs:
            print(f"- {v1} та {v2} (часткова кореляція r = {r:.4f})")
            
            if 'x1' in (v1, v2) and 'x2' in (v1, v2):
                print(f"  -> Залишаємо x1 (Продуктивність праці), прибираємо x2 (Фондомісткість).")
                if v1 == 'x2': variables_to_drop.add(v1)
                else: variables_to_drop.add(v2)
            elif 'x3' in (v1, v2):
                print(f"  -> Прибираємо x3 (Плинність кадрів) як менш стабільний фактор.")
                variables_to_drop.add('x3')
        
        if variables_to_drop:
            print(f"\nОстаточне рішення: виключаємо з моделі змінну(і): {', '.join(variables_to_drop)}")
    else:
        print("Значущої колінеарності між окремими парами за t-критерієм не виявлено. Змінні залишаємо.")
else:
    print("ВИСНОВОК: Мультиколінеарність відсутня. Всі змінні залишаємо в моделі.")
