import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
import math
VARIANT = 2
VARIABLE_NAME = "Вага, кг"
UNIFORM_MIN = 40
UNIFORM_MAX = 90
N_OBS = 100
RANDOM_SEED = 42
def generate_uniform_data(min_val: float, max_val: float, n: int, seed: int = None) -> np.ndarray:
rng = np.random.default_rng(seed)
raw = rng.uniform(min_val, max_val, n)
rounded = np.round(raw, 0).astype(int)
return rounded
def stats_by_formulas(x: np.ndarray) -> dict:
x = np.asarray(x, dtype=float)
n = x.size
x_sorted = np.sort(x)
x_min = x_sorted[0]
x_max = x_sorted[-1]
R = x_max - x_min
x_mean = x.sum() / n
if n % 2 == 1:
median = x_sorted[n // 2]
else:
median = 0.5 * (x_sorted[n // 2 - 1] + x_sorted[n // 2])
values, counts = np.unique(x, return_counts=True)
mode = values[np.argmax(counts)]
d = np.mean(np.abs(x - x_mean))
var_pop = np.mean((x - x_mean) ** 2)
sigma_pop = np.sqrt(var_pop)
var_unbiased = var_pop * n / (n - 1)
sigma_unbiased = np.sqrt(var_unbiased)
V_R = (R / x_mean) * 100.0
V_d = (d / x_mean) * 100.0
V_sigma = (sigma_pop / x_mean) * 100.0
centered = x - x_mean
A = (centered ** 3).sum() / (n * (sigma_pop ** 3)) if sigma_pop != 0 else np.nan
E = (centered ** 4).sum() / (n * (sigma_pop ** 4)) - 3 if sigma_pop != 0 else np.nan
return {
"n": n,
"min": x_min,
"max": x_max,
"R": R,
"mean": x_mean,
"median": median,
"mode": mode,
"d": d,
"var_pop": var_pop,
"sigma_pop": sigma_pop,
"var_unbiased": var_unbiased,
"sigma_unbiased": sigma_unbiased,
"V_R": V_R,
"V_d": V_d,
"V_sigma": V_sigma,
"A": A,
"E": E,
}
def stats_by_functions(x: np.ndarray) -> dict:
x = np.asarray(x, dtype=float)
n = x.size
x_min = np.min(x)
x_max = np.max(x)
R = x_max - x_min
x_mean = x.mean()
median = np.median(x)
mode = float(pd.Series(x).mode().iloc[0])
d = np.mean(np.abs(x - x_mean))
var_pop = np.var(x, ddof=0)
sigma_pop = np.std(x, ddof=0)
var_unbiased = np.var(x, ddof=1)
sigma_unbiased = np.std(x, ddof=1)
V_R = (R / x_mean) * 100.0
V_d = (d / x_mean) * 100.0
V_sigma = (sigma_pop / x_mean) * 100.0
A = stats.skew(x, bias=False)
E = stats.kurtosis(x, fisher=True, bias=False)
return {
"n": n,
"min": x_min,
"max": x_max,
"R": R,
"mean": x_mean,
"median": median,
"mode": mode,
"d": d,
"var_pop": var_pop,
"sigma_pop": sigma_pop,
"var_unbiased": var_unbiased,
"sigma_unbiased": sigma_unbiased,
"V_R": V_R,
"V_d": V_d,
"V_sigma": V_sigma,
"A": A,
"E": E,
}
def stats_by_addin(x: np.ndarray) -> dict:
x = np.asarray(x, dtype=float)
desc = stats.describe(x)
n = desc.nobs
x_min, x_max = desc.minmax
R = x_max - x_min
x_mean = desc.mean
var_unbiased = float(desc.variance)
sigma_unbiased = float(np.sqrt(var_unbiased))
var_pop = var_unbiased * (n - 1) / n
sigma_pop = float(np.sqrt(var_pop))
A = float(desc.skewness)
E = float(desc.kurtosis)
median = float(np.median(x))
mode = float(pd.Series(x).mode().iloc[0])
d = float(np.mean(np.abs(x - x_mean)))
V_R = (R / x_mean) * 100.0
V_d = (d / x_mean) * 100.0
V_sigma = (sigma_pop / x_mean) * 100.0
return {
"n": n,
"min": x_min,
"max": x_max,
"R": R,
"mean": x_mean,
"median": median,
"mode": mode,
"d": d,
"var_pop": var_pop,
"sigma_pop": sigma_pop,
"var_unbiased": var_unbiased,
"sigma_unbiased": sigma_unbiased,
"V_R": V_R,
"V_d": V_d,
"V_sigma": V_sigma,
"A": A,
"E": E,
}
from typing import Tuple
def check_homogeneity(V_sigma: float) -> str:
if V_sigma <= 30.0:
return f"Вибірка однорідна (V_sigma = {V_sigma:.2f}% ≤ 30%)"
else:
return f"Вибірка НЕ є однорідною (V_sigma = {V_sigma:.2f}% > 30%)"
def critical_values_A_E(n: int) -> Tuple[float, float]:
D_A = 6 * (n - 1) / ((n + 1) * (n + 3))
D_E = 24 * n * (n - 2) * (n - 3) / (((n + 1) ** 2) * (n + 3) * (n + 5))
return D_A, D_E
def check_normality(A: float, E: float, n: int) -> str:
D_A, D_E = critical_values_A_E(n)
sigma_A = math.sqrt(D_A)
sigma_E = math.sqrt(D_E)
crit_A = 3 * sigma_A
crit_E = 5 * sigma_E
cond_A = abs(A) <= crit_A
cond_E = abs(E) <= crit_E
lines = []
lines.append(f"A = {A:.4f}, D(A) = {D_A:.4f}, 3·sqrt(D(A)) = {crit_A:.4f}")
lines.append(f"E = {E:.4f}, D(E) = {D_E:.4f}, 5·sqrt(D(E)) = {crit_E:.4f}")
if cond_A and cond_E:
lines.append("Форма розподілу МОЖЕ вважатися близькою до нормальної (обидві нерівності виконуються).")
else:
lines.append("Форма розподілу НЕ є близькою до нормальної (хоча б одна з нерівностей порушена).")
return "\n".join(lines)
def sturges_intervals(n: int) -> int:
if n <= 1:
return 1
k = 1 + 3.322 * math.log10(n)
return int(round(k))
def build_intervals(x: np.ndarray, k: int) -> np.ndarray:
x = np.asarray(x, dtype=float)
x_min = float(np.min(x))
x_max = float(np.max(x))
boundaries = np.linspace(x_min, x_max, k + 1)
boundaries = np.round(boundaries, 1)
return boundaries
def grouping_manual(x: np.ndarray, boundaries: np.ndarray) -> pd.DataFrame:
x = np.asarray(x, dtype=float)
n = x.size
k = len(boundaries) - 1
intervals_left = []
intervals_right = []
midpoints = []
freqs = []
for i in range(k):
left = boundaries[i]
right = boundaries[i + 1]
if i < k - 1:
mask = (x >= left) & (x < right)
else:
mask = (x >= left) & (x <= right)
count = int(mask.sum())
intervals_left.append(left)
intervals_right.append(right)
midpoints.append(0.5 * (left + right))
freqs.append(count)
freqs = np.array(freqs, dtype=int)
rel_freq = freqs / n
cum_freq_pct = np.cumsum(rel_freq) * 100.0
df = pd.DataFrame({
"L": intervals_left,
"U": intervals_right,
"midpoint": midpoints,
"n_manual": freqs,
"p_manual": rel_freq,
"K_manual_pct": cum_freq_pct,
})
return df
def grouping_addin(x: np.ndarray, boundaries: np.ndarray, df_manual: pd.DataFrame) -> pd.DataFrame:
x = np.asarray(x, dtype=float)
k = len(boundaries) - 1
freqs_addin, _ = np.histogram(x, bins=boundaries)
n = x.size
rel_freq_addin = freqs_addin / n
cum_freq_addin_pct = np.cumsum(rel_freq_addin) * 100.0
df = df_manual.copy()
df["n_addin"] = freqs_addin
df["p_addin"] = rel_freq_addin
df["K_addin_pct"] = cum_freq_addin_pct
return df
def theoretical_uniform_counts(boundaries: np.ndarray, n: int, a: float, b: float) -> np.ndarray:
probs = (boundaries[1:] - boundaries[:-1]) / (b - a)
counts = n * probs
return counts
def plot_hist_addin(x: np.ndarray, boundaries: np.ndarray):
plt.figure(figsize=(8, 5))
plt.hist(x, bins=boundaries, edgecolor="black", alpha=0.7)
plt.title("Гістограма (аналог статистичної надбудови)")
plt.xlabel(VARIABLE_NAME)
plt.ylabel("Частота")
plt.grid(axis="y", alpha=0.3)
def plot_hist_polygon_cumulative(df_group_manual: pd.DataFrame):
fig, ax1 = plt.subplots(figsize=(8, 5))
L = df_group_manual["L"].values
U = df_group_manual["U"].values
mid = df_group_manual["midpoint"].values
n_manual = df_group_manual["n_manual"].values
K_manual_pct = df_group_manual["K_manual_pct"].values
widths = U - L
ax1.bar(mid, n_manual, width=widths * 0.9, edgecolor="black", alpha=0.6, label="Гістограма (ручне)")
ax1.plot(mid, n_manual, "o-", label="Полігон")
ax1.set_xlabel(VARIABLE_NAME)
ax1.set_ylabel("Частота")
ax1.set_title("Гістограма та полігон (ручне групування)")
ax1.grid(alpha=0.3)
ax2 = ax1.twinx()
ax2.plot(mid, K_manual_pct, "s-", color="purple", label="Кумулята")
ax2.set_ylabel("Накопичена частота, %")
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc="upper left")
def plot_empirical_vs_theoretical(df_group_manual: pd.DataFrame, theor_counts: np.ndarray):
plt.figure(figsize=(8, 5))
mid = df_group_manual["midpoint"].values
n_manual = df_group_manual["n_manual"].values
plt.bar(mid, n_manual, width=(df_group_manual["U"].values - df_group_manual["L"].values) * 0.9,
edgecolor="black", alpha=0.6, label="Емпірична гістограма")
plt.plot(mid, theor_counts, "o-", label="Теоретичний розподіл")
plt.xlabel(VARIABLE_NAME)
plt.ylabel("Частота")
plt.title("Емпіричний vs теоретичний розподіл")
plt.grid(alpha=0.3)
plt.legend()
def run_lab(show_plots: bool = True):
x = generate_uniform_data(UNIFORM_MIN, UNIFORM_MAX, N_OBS, seed=RANDOM_SEED)
df = pd.DataFrame({"X": x})
print(f"Практична робота 1-3. Варіант {VARIANT}. Випадкова величина: {VARIABLE_NAME}")
print(f"Розподіл: рівномірний на [{UNIFORM_MIN}; {UNIFORM_MAX}], n = {N_OBS}")
print("\nПерші 10 спостережень (округлені значення):")
print(df.head(10))
stats_formulas = stats_by_formulas(x)
stats_funcs = stats_by_functions(x)
stats_addin = stats_by_addin(x)
all_keys = list(stats_formulas.keys())
table = pd.DataFrame({
"Показник": all_keys,
"За формулами": [stats_formulas[k] for k in all_keys],
"Стат. функції": [stats_funcs[k] for k in all_keys],
"Надбудова": [stats_addin[k] for k in all_keys],
})
table.set_index("Показник", inplace=True)
print("\n=== Показники варіації (три способи) ===")
print(table)
homog_text = check_homogeneity(stats_formulas["V_sigma"])
print("\n=== Однорідність вибірки ===")
print(homog_text)
normality_text = check_normality(stats_formulas["A"], stats_formulas["E"], stats_formulas["n"])
print("\n=== Перевірка близькості до нормального розподілу ===")
print(normality_text)
k = sturges_intervals(N_OBS)
print(f"\nКількість інтервалів (формула Стерджеса): k = {k}")
boundaries = build_intervals(x, k=k)
df_group_manual = grouping_manual(x, boundaries)
df_group = grouping_addin(x, boundaries, df_group_manual)
print("\n=== Групування даних (ручне + надбудова) ===")
print(df_group)
theor_counts = theoretical_uniform_counts(boundaries, N_OBS, UNIFORM_MIN, UNIFORM_MAX)
print("\nТеоретичні частоти для рівномірного розподілу:")
print(theor_counts)
if show_plots:
plot_hist_addin(x, boundaries)
plot_hist_polygon_cumulative(df_group_manual)
plot_empirical_vs_theoretical(df_group_manual, theor_counts)
plt.show()
run_lab(show_plots=True)
print(f"--- РОЗВ'ЯЗАННЯ КОНТРОЛЬНОЇ ЗАДАЧІ ---")
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
data = [
21, 17, 27, 20, 22, 12, 24, 13, 20, 19, 22, 16, 22,
9, 21, 16, 23, 16, 21, 24, 18, 11, 22, 15, 23, 21,
10, 15, 18, 15, 21, 14, 15, 18, 22, 15, 17, 19, 17,
18, 17, 24, 18, 19, 16, 17, 15, 17, 25, 16
]
x = np.array(data)
n = len(x)
print(f"Обсяг вибірки n = {n}")
unique_elements, counts_elements = np.unique(x, return_counts=True)
var_series = pd.DataFrame({
'Варіанта (xi)': unique_elements,
'Частота (ni)': counts_elements
})
print("\n1. Варіаційний ряд:")
print(var_series.to_string(index=False))
mean_val = np.mean(x)
median_val = np.median(x)
mode_result = stats.mode(x, keepdims=True)
mode_val = mode_result.mode[0]
mode_count = mode_result.count[0]
std_dev = np.std(x, ddof=1)
cv = (std_dev / mean_val) * 100
print("\n2. Статистичні показники:")
print(f"{'Середня арифметична (x̄)':<35} {mean_val:.4f}")
print(f"{'Медіана (Me)':<35} {median_val:.4f}")
print(f"{'Мода (Mo)':<35} {mode_val} (зустрічається {mode_count} разів)")
print(f"{'Середнє кв. відхилення (σ або S)':<35} {std_dev:.4f}")
print(f"{'Коефіцієнт варіації (V)':<35} {cv:.2f} %")
homogeneity = "ОДНОРІДНА" if cv <= 30 else "НЕОДНОРІДНА"
print(f"-> Оскільки V = {cv:.2f}%, сукупність є {homogeneity} (поріг 30%).")
plt.figure(figsize=(10, 6))
plt.plot(unique_elements, counts_elements, marker='o', linestyle='-', color='b', linewidth=2, markersize=8)
for i, txt in enumerate(counts_elements):
plt.annotate(txt, (unique_elements[i], counts_elements[i]),
textcoords="offset points", xytext=(0,10), ha='center')
plt.title('Лінійний графік варіаційного ряду (Полігон розподілу)')
plt.xlabel('Кількість зерен у колосі ($x_i$)')
plt.ylabel('Частота ($n_i$)')
plt.xticks(unique_elements)
plt.grid(True, linestyle='--', alpha=0.7)
print("\n3. Графік побудовано.")
plt.show()