from google.colab import files import matplotlib.pyplot as plt from statsmodels.distributions.empirical_distribution import ECDF import math from scipy import stats as sp_stats import numpy as np def load_data(uploaded: dict) -> dict[str, np.ndarray]: filename = list(uploaded.keys())[0] data = np.loadtxt(filename, delimiter=",", skiprows=1) return {"X3": data[:, 2]} def compute_statistics(data: np.ndarray) -> dict: return { "mean": np.mean(data), "var_b": np.var(data), "var_ub": np.var(data, ddof=1), "std_b": np.std(data), "std_ub": np.std(data, ddof=1), "median": np.median(data), "q25": np.quantile(data, 0.25), "q75": np.quantile(data, 0.75), "q10": np.quantile(data, 0.10), "q90": np.quantile(data, 0.90), "min": np.min(data), "max": np.max(data), "n": len(data), } def print_statistics(name: str, stats: dict) -> None: print(f"\n{'=' * 40}") print(f" Выборка: {name} (n = {stats['n']})") print(f"{'=' * 40}") print(f" Среднее: {stats['mean']:.4f}") print(f" Медиана: {stats['median']:.4f}") print(f" Дисперсия смещённая S²: {stats['var_b']:.4f}") print(f" Дисперсия несмещённая σ̂²: {stats['var_ub']:.4f}") print(f" Ст. откл. смещённое S: {stats['std_b']:.4f}") print(f" Ст. откл. несмещённое σ̂: {stats['std_ub']:.4f}") print(f" Квартиль Q1 (0.25): {stats['q25']:.4f}") print(f" Квартиль Q3 (0.75): {stats['q75']:.4f}") print(f" Квантиль 0.10: {stats['q10']:.4f}") print(f" Квантиль 0.90: {stats['q90']:.4f}") print(f" Минимум: {stats['min']:.4f}") print(f" Максимум: {stats['max']:.4f}") print(f" IQR: {stats['q75'] - stats['q25']:.4f}") def plot_variation_series(data: np.ndarray, name: str) -> None: sorted_data = np.sort(data) plt.figure(figsize=(8, 3)) plt.plot(sorted_data, color="steelblue", linewidth=0.8) plt.title(f"Вариационный ряд — {name}", fontweight="bold") plt.xlabel("Порядковый номер") plt.ylabel("Значение") plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() def plot_ecdf(data: np.ndarray, name: str) -> None: ecdf = ECDF(data) x = np.linspace(np.min(data) - 0.5, np.max(data) + 0.5, 1000) plt.figure(figsize=(7, 4)) plt.step(x, ecdf(x), where="post", color="steelblue", linewidth=1.5) plt.title(f"Эмпирическая функция распределения — {name}", fontweight="bold") plt.xlabel("x") plt.ylabel(r"$F_n(x)$") plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() def _scott_bins(data: np.ndarray) -> int: h = 3.5 * np.std(data, ddof=1) * len(data) ** (-1 / 3) return math.ceil((np.max(data) - np.min(data)) / h) def _freedman_diaconis_bins(data: np.ndarray) -> int: iqr = np.quantile(data, 0.75) - np.quantile(data, 0.25) h = 2 * iqr * len(data) ** (-1 / 3) return math.ceil((np.max(data) - np.min(data)) / h) def _sturges_bins(data: np.ndarray) -> int: return 1 + math.floor(math.log2(len(data))) _BIN_RULES: dict[str, callable] = { "scott": _scott_bins, "freedman-diaconis": _freedman_diaconis_bins, "sturges": _sturges_bins, } def get_number_of_bins(data: np.ndarray, method: str = "scott") -> int: if method not in _BIN_RULES: raise ValueError( f"unknown method '{method}'. available methods are: {list(_BIN_RULES.keys())}" ) return _BIN_RULES[method](data) def plot_histogram( data: np.ndarray, name: str, methods: list[str] | None = None, ) -> None: if methods is None: methods = list(_BIN_RULES.keys()) fig, axes = plt.subplots( 1, len(methods), figsize=(5 * len(methods), 4), sharey=False ) if len(methods) == 1: axes = [axes] for ax, method in zip(axes, methods): bins = get_number_of_bins(data, method) print(f"{method} -- {name} -- {bins}") ax.hist(data, bins=bins, color="steelblue", edgecolor="white", linewidth=0.5) ax.set_title(f"{method}\n(k = {bins})", fontsize=10) ax.set_xlabel("Значения") ax.set_ylabel("Частота") fig.suptitle(f"Гистограммы — {name}", fontsize=13, fontweight="bold") plt.tight_layout() plt.show() def describe_distribution(data: np.ndarray, name: str) -> None: n = len(data) mean = np.mean(data) median = np.median(data) std = np.std(data, ddof=1) skewness = sp_stats.skew(data) kurtosis = sp_stats.kurtosis(data) q25, q75 = np.quantile(data, [0.25, 0.75]) iqr = q75 - q25 if abs(skewness) < 0.2: sym = "симметричное" elif skewness > 0: sym = f"правосторонняя асимметрия (skew = {skewness:.3f})" else: sym = f"левосторонняя асимметрия (skew = {skewness:.3f})" if kurtosis > 1: tails = f"тяжёлые хвосты (kurt = {kurtosis:.3f})" elif kurtosis < -1: tails = f"лёгкие хвосты (kurt = {kurtosis:.3f})" else: tails = f"хвосты близки к нормальным (kurt = {kurtosis:.3f})" lo, hi = q25 - 1.5 * iqr, q75 + 1.5 * iqr outliers = data[(data < lo) | (data > hi)] if len(outliers) == 0: out_str = "выбросы не обнаружены" else: pct = 100 * len(outliers) / n out_str = f"{len(outliers)} выброс(ов) ({pct:.1f}%) за пределами [{lo:.3f}, {hi:.3f}]" kde = sp_stats.gaussian_kde(data) x_grid = np.linspace(np.min(data), np.max(data), 1000) kde_vals = kde(x_grid) peaks = [i for i in range(1, len(kde_vals) - 1) if kde_vals[i] > kde_vals[i-1] and kde_vals[i] > kde_vals[i+1]] threshold = 0.1 * np.max(kde_vals) significant_peaks = [x_grid[i] for i in peaks if kde_vals[i] > threshold] if len(significant_peaks) == 1: modes_str = f"унимодальное (пик ≈ {significant_peaks[0]:.3f})" elif len(significant_peaks) == 2: modes_str = f"бимодальное (пики ≈ {significant_peaks[0]:.3f} и {significant_peaks[1]:.3f})" else: modes_str = f"многомодальное ({len(significant_peaks)} пика)" print(f"\n{'=' * 40}") print(f" Описание формы распределения — {name}") print(f"{'=' * 40}") print(f" Симметрия: {sym}") print(f" Хвосты: {tails}") print(f" Выбросы: {out_str}") print(f" Модальность: {modes_str}") print(f" Среднее vs медиана: {mean:.4f} vs {median:.4f}") def estimate_exponential_parameters(data: np.ndarray, name: str) -> None: n = len(data) mean = np.mean(data) var_ub = np.var(data, ddof=1) lambda_mm = 1.0 / mean lambda_mm2 = 1.0 / np.sqrt(var_ub) lambda_mle = n / np.sum(data) def log_likelihood(lam: float) -> float: return n * np.log(lam) - lam * np.sum(data) ll_mm = log_likelihood(lambda_mm) ll_mle = log_likelihood(lambda_mle) print(f"\n{'=' * 50}") print(f" Оценка параметров — {name}") print(f" Модель: Экспоненциальное распределение Exp(λ)") print(f"{'=' * 50}") print(f"\n [Метод моментов]") print(f" EX = {lambda_mm:.6f}") print(f" DX = {lambda_mm2:.6f} (через дисперсию)") print(f"\n [Метод максимального правдоподобия]") print(f" ММП = {lambda_mle:.6f}") print(f"\n [Сравнение]") print(f" ММ = {lambda_mm:.6f}") print(f" ММП = {lambda_mle:.6f}") print(f" Разница = {abs(lambda_mle - lambda_mm):.2e}") if abs(lambda_mle - lambda_mm) < 1e-10: comment = () else: comment = ("Небольшое расхождение обусловлено численными погрешностями.") print(f"\n Оценки совпадают: для Exp(λ) метод моментов (через EX) и МПП дают одинаковую формулу λ̂ = 1/x̄.") lambdas = np.linspace(lambda_mle * 0.5, lambda_mle * 1.8, 500) ll_vals = [log_likelihood(l) for l in lambdas] plt.figure(figsize=(7, 4)) plt.plot(lambdas, ll_vals, color="steelblue", linewidth=1.8, label="log L(λ)") plt.axvline(lambda_mle, color="crimson", linestyle="--", linewidth=1.2, label=f"λ̂_MLE = {lambda_mle:.4f}") plt.axvline(lambda_mm, color="darkorange", linestyle=":", linewidth=1.2, label=f"λ̂_MM = {lambda_mm:.4f}") plt.scatter([lambda_mle, lambda_mm], [log_likelihood(lambda_mle), log_likelihood(lambda_mm)], color=["crimson", "darkorange"], zorder=5) plt.title(f"Логарифм правдоподобия — {name}", fontweight="bold") plt.xlabel("λ") plt.ylabel("log L(λ)") plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() return lambda_mle def estimate_probability(data: np.ndarray, name: str, lambda_mle: float) -> None: n = len(data) mean = np.mean(data) std_ub = np.std(data, ddof=1) c_hat = np.min(data) x0 = mean + std_ub p_empirical = np.sum(data > x0) / n p_parametric = np.exp(-lambda_mle * (x0 - c_hat)) print(f"\n{'=' * 50}") print(f" Оценка P(X > x0) — {name}") print(f" Модель: Exp(λ={lambda_mle:.4f}, c={c_hat:.4f})") print(f"{'=' * 50}") print(f" Порог x0 = x̄ + σ̂ = {mean:.4f} + {std_ub:.4f} = {x0:.4f}") print(f"\n Эмпирическая P(X > x0) = {p_empirical:.6f} ({int(p_empirical*n)}/{n} наблюдений)") print(f" Параметрическая P(X > x0) = {p_parametric:.6f}") print(f"\n Абсолютное расхождение: {abs(p_parametric - p_empirical):.2e}") rel = abs(p_parametric - p_empirical) / p_parametric * 100 print(f" Относительное расхождение: {rel:.2f}%") if rel < 5: comment = "Расхождение незначительное — модель хорошо описывает данные." elif rel < 15: comment = "Умеренное расхождение — модель в целом подходит, но есть отклонения." else: comment = "Заметное расхождение — стоит перепроверить выбор модели или оценки параметров." print(f" Вывод: {comment}") x_grid = np.linspace(np.min(data) - 0.5, np.max(data) + 2, 500) cdf_param = np.where(x_grid >= c_hat, 1 - np.exp(-lambda_mle * (x_grid - c_hat)), 0.0) from statsmodels.distributions.empirical_distribution import ECDF ecdf = ECDF(data) fig, ax = plt.subplots(figsize=(8, 4)) ax.plot(x_grid, 1 - ecdf(x_grid), color="steelblue", linewidth=1.5, label="Эмпирическая P(X > x)") ax.plot(x_grid, 1 - cdf_param, color="crimson", linewidth=1.5, linestyle="--", label=f"Параметрическая P(X > x)") ax.axvline(x0, color="darkorange", linestyle=":", linewidth=1.5, label=f"x₀ = {x0:.3f}") ax.scatter([x0], [p_empirical], color="steelblue", zorder=5, s=60) ax.scatter([x0], [p_parametric], color="crimson", zorder=5, s=60) ax.annotate(f" эмп. = {p_empirical:.4f}", (x0, p_empirical), fontsize=9, color="steelblue") ax.annotate(f" пар. = {p_parametric:.4f}", (x0, p_parametric), fontsize=9, color="crimson", xytext=(x0, p_parametric - 0.03)) ax.set_title(f"P(X > x) — эмпирическая vs параметрическая — {name}", fontweight="bold") ax.set_xlabel("x") ax.set_ylabel("P(X > x)") ax.legend() ax.grid(True, alpha=0.3) plt.tight_layout() plt.show() def estimate_grouped_moments(data: np.ndarray, name: str, method: str = "scott") -> None: n = len(data) bins = get_number_of_bins(data, method) counts, edges = np.histogram(data, bins=bins) midpoints = (edges[:-1] + edges[1:]) / 2 mean_grouped = np.sum(counts * midpoints) / n var_grouped = np.sum(counts * (midpoints - mean_grouped) ** 2) / (n - 1) std_grouped = np.sqrt(var_grouped) mean_raw = np.mean(data) var_raw = np.var(data, ddof=1) std_raw = np.std(data, ddof=1) print(f"\n{'=' * 55}") print(f" Моменты по сгруппированной выборке — {name}") print(f" Правило разбиения: {method} (k = {bins} интервалов)") print(f"{'=' * 55}") print(f" {'Характеристика':<28} {'По исходным':>12} {'По группир.':>12} {'Δ':>10}") print(f" {'-'*62}") print(f" {'Среднее EX':<28} {mean_raw:>12.6f} {mean_grouped:>12.6f} {abs(mean_grouped - mean_raw):>10.2e}") print(f" {'Дисперсия DX (несмещ.)':<28} {var_raw:>12.6f} {var_grouped:>12.6f} {abs(var_grouped - var_raw):>10.2e}") print(f" {'Ст. отклонение σ̂':<28} {std_raw:>12.6f} {std_grouped:>12.6f} {abs(std_grouped - std_raw):>10.2e}") rel_mean = abs(mean_grouped - mean_raw) / abs(mean_raw) * 100 rel_var = abs(var_grouped - var_raw) / abs(var_raw) * 100 print(f"\n Относительное отклонение среднего: {rel_mean:.3f}%") print(f" Относительное отклонение дисперсии: {rel_var:.3f}%") if max(rel_mean, rel_var) < 1: comment = "Группировка практически не вносит погрешности — интервалов достаточно." elif max(rel_mean, rel_var) < 5: comment = "Незначительная потеря точности из-за группировки — результат приемлем." else: comment = "Заметная погрешность группировки — стоит увеличить число интервалов." print(f" Вывод: {comment}") fig, ax = plt.subplots(figsize=(8, 4)) ax.bar(edges[:-1], counts, width=np.diff(edges), align="edge", color="steelblue", edgecolor="white", linewidth=0.5, alpha=0.7, label="Частоты интервалов") ax.scatter(midpoints, counts, color="darkorange", zorder=5, s=50, label="Середины интервалов") ax.axvline(mean_raw, color="crimson", linestyle="--", linewidth=1.5, label=f"x̄ исходное = {mean_raw:.4f}") ax.axvline(mean_grouped, color="darkgreen", linestyle=":", linewidth=1.5, label=f"x̄ группир. = {mean_grouped:.4f}") ax.set_title(f"Гистограмма с оценками среднего — {name}", fontweight="bold") ax.set_xlabel("Значения") ax.set_ylabel("Частота") ax.legend(fontsize=9) ax.grid(True, alpha=0.3) plt.tight_layout() plt.show() def analyze_column(name: str, data: np.ndarray) -> None: stats = compute_statistics(data) print_statistics(name, stats) describe_distribution(data, name) plot_variation_series(data, name) plot_ecdf(data, name) plot_histogram(data, name) lambda_mle = estimate_exponential_parameters(data, name) estimate_probability(data, name, lambda_mle) estimate_grouped_moments(data, name) uploaded = files.upload() columns = load_data(uploaded) analyze_column("X3", columns["X3"])