1. Выбор датасета

В работе используется открытый датасет iris (встроен в R).
Для бутстрапа будем анализировать переменную Petal.Length (длина лепестка).

data(iris)
head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa

2. Небольшой разведочный анализ (EDA)

2.1 Общая статистика

summary(iris)
##   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
##        Species  
##  setosa    :50  
##  versicolor:50  
##  virginica :50  
##                 
##                 
## 

2.2 Распределение Petal.Length

ggplot(iris, aes(x = Petal.Length)) +
  geom_histogram(bins = 15, fill = "skyblue") +
  theme_minimal() +
  labs(title = "Распределение Petal.Length",
       x = "Petal.Length",
       y = "Частота")

Пояснение графика:
Гистограмма показывает, какие значения Petal.Length встречаются чаще. Видно, что распределение не полностью симметрично из-за различий между видами ирисов.

2.3 Petal.Length по видам

ggplot(iris, aes(x = Species, y = Petal.Length)) +
  geom_boxplot(fill = "lightgreen") +
  theme_minimal() +
  labs(title = "Petal.Length по видам ириса",
       x = "Вид (Species)",
       y = "Petal.Length")

Пояснение графика:
Boxplot показывает медиану, разброс и возможные выбросы. Видно, что длина лепестка сильно отличается между видами.


3. Бутстрап на Python + визуализация

Бутстрап — это многократное случайное выборочное извлечение с возвращением.
Мы будем оценивать среднее Petal.Length и строить распределение бутстрап-средних.

import numpy as np
import matplotlib.pyplot as plt

# данные Petal.Length
data = np.array([ 
    1.4,1.4,1.3,1.5,1.4,1.7,1.4,1.5,1.4,1.5,
    1.5,1.6,1.4,1.1,1.2,1.5,1.3,1.4,1.7,1.5,
    1.7,1.5,1.0,1.7,1.9,1.6,1.6,1.5,1.4,1.6,
    1.6,1.5,1.5,1.4,1.5,1.2,1.3,1.4,1.3,1.5,
    1.3,1.3,1.3,1.6,1.9,1.4,1.6,1.4,1.5,1.4,
    4.7,4.5,4.9,4.0,4.6,4.5,4.7,3.3,4.6,3.9,
    3.5,4.2,4.0,4.7,3.6,4.4,4.5,4.1,4.5,3.9,
    4.8,4.0,4.9,4.7,4.3,4.4,4.8,5.0,4.5,3.5,
    3.8,3.7,3.9,5.1,4.5,4.5,4.7,4.4,4.1,4.0,
    4.4,4.6,4.0,3.3,4.2,4.2,4.2,4.3,3.0,4.1,
    6.0,5.1,5.9,5.6,5.8,6.6,4.5,6.3,5.8,6.1,
    5.1,5.3,5.5,5.0,5.1,5.3,5.5,6.7,6.9,5.0,
    5.7,4.9,6.7,4.9,5.7,6.0,4.8,4.9,5.6,5.8,
    6.1,6.4,5.6,5.1,5.6,6.1,5.6,5.5,4.8,5.4,
    5.6,5.1,5.1,5.9,5.7,5.2,5.0,5.2,5.4,5.1
])

B = 3000
boot_means = np.empty(B)

for i in range(B):
    sample = np.random.choice(data, size=len(data), replace=True)
    boot_means[i] = np.mean(sample)

mean_est = np.mean(boot_means)
ci_low, ci_high = np.percentile(boot_means, [2.5, 97.5])

plt.hist(boot_means, bins=35)
plt.axvline(mean_est, linestyle='--')
plt.axvline(ci_low, linestyle=':')
plt.axvline(ci_high, linestyle=':')
plt.title("Bootstrap means of Petal.Length (Python)")
plt.xlabel("Mean Petal.Length")
plt.ylabel("Frequency")
plt.show()

mean_est, (ci_low, ci_high)
## (np.float64(3.7578711111111107), (np.float64(3.4766166666666667), np.float64(4.038016666666667)))

Пояснение графика:
- Гистограмма показывает распределение бутстрап-средних.
- Пунктирная линия — оценка среднего.
- Две линии по краям — границы 95% доверительного интервала (percentile CI).

Вывод (Python):
Метод бутстрапа позволяет оценить среднее Petal.Length и получить доверительный интервал без строгих предположений о распределении данных.


4. Бутстрап на R + визуализация

Повторим тот же подход в R, используя данные iris$Petal.Length.

set.seed(2026)

x <- iris$Petal.Length
B <- 3000
boot_means <- replicate(B, mean(sample(x, replace = TRUE)))

mean_est <- mean(boot_means)
ci_percentile <- quantile(boot_means, probs = c(0.025, 0.975))

mean_est
## [1] 3.757568
ci_percentile
##     2.5%    97.5% 
## 3.483983 4.044700

Гистограмма бутстрап-средних

hist(boot_means, breaks = 35,
     col = "lightblue",
     main = "Bootstrap means of Petal.Length (R)",
     xlab = "Mean Petal.Length")

abline(v = mean_est, lty = 2)
abline(v = ci_percentile[1], lty = 3)
abline(v = ci_percentile[2], lty = 3)

Пояснение графика:
Линия по центру — среднее бутстрап-оценок.
Линии по краям показывают 95% доверительный интервал.

Дополнительно: нормальный доверительный интервал

se_boot <- sd(boot_means)
ci_normal <- c(mean_est - 1.96*se_boot, mean_est + 1.96*se_boot)
ci_normal
## [1] 3.476762 4.038375

Короткий вывод:
Percentile CI строится по квантилям бутстрап-распределения, а Normal CI использует оценку стандартной ошибки и приближение нормальностью.


5. Общий вывод

  1. Датасет iris был выбран как открытый пример.
  2. Проведен EDA: распределение Petal.Length и различия по классам Species.
  3. Алгоритм бутстрапа реализован на Python и R.
  4. Гистограммы бутстрап-средних показывают, что распределение средних становится близким к нормальному.
  5. Получены оценки среднего и доверительные интервалы, что демонстрирует практическую пользу бутстрапа.