Skip to content

9. Предобработка AI-данных: обработка пропусков (NaN/None) и дубликатов ​

Стратегии импутации (заполнения) пропусков в ML ​

Поиск и аудит скрытых пустот в датасете ​

В реальной практике ИИ-инженера сырые датасеты часто содержат пропуски. Они возникают из-за сбоев в работе датчиков, ошибок логирования или неполных анкет пользователей. Наличие пропущенных значений (маркеров NaN или объектов None) блокирует математические операции и вызывает критические ошибки при попытке передать данные в нейронную сеть.

Первым шагом предобработки всегда является локализация и аудит пустот. Для этого к DataFrame применяется векторизованный метод .isna(), который заменяет каждое значение в таблице на булев флаг (True, если значение пропущено). Чтобы быстро оценить масштаб проблемы в разрезе признаков, к полученной маске добавляют вертикальный агрегатор .sum() (по умолчанию axis=0), сжимающий строки.

python
import pandas

raw_corrupted_data = {
    "feature_age": [23, 45, pandas.NA, 31],
    "feature_score": [0.85, pandas.NA, 0.12, 0.99]
}
df_corrupted = pandas.DataFrame(raw_corrupted_data)

# Counting the absolute number of missing fields for each feature column
print(df_corrupted.isna().sum())
# Outputs:
# feature_age      1
# feature_score    1
# dtype: int64

Математические стратегии борьбы с пропусками ​

Когда пропуски обнаружены, ИИ-инженер выбирает одну из двух фундаментальных стратегий предобработки данных:

  1. Жесткое удаление (dataframe.dropna()): Полностью выбрасывает строки или столбцы, содержащие хотя бы одно пропущенное значение. Стратегия применима, если пропусков очень мало (менее 1–5% от всей выборки), чтобы не потерять важную информацию для обучения. Параметр axis=0 удаляет строки, а axis=1 — столбцы.
  2. Заполнение или Импутация (dataframe.fillna()): Заменяет маркеры пропусков на фиксированные числовые значения. В машинном обучении хорошим тоном считается заполнять пустоты средним арифметическим значением (.mean()) или медианой (.median()) данного столбца признаков, рассчитанной по остальным валидным объектам.
python
import pandas

df_weights = pandas.DataFrame({"weight": [1.5, pandas.NA, 2.5, 4.0]})

# Strategy 1: Drop rows containing any missing value tokens
clean_rows = df_weights.dropna(axis=0)

# Strategy 2: Impute missing entries with the feature's calculated mean scalar
column_mean = df_weights["weight"].mean()
df_weights["weight"] = df_weights["weight"].fillna(column_mean)

Очистка от дубликатов ​

Обнаружение и удаление идентичных строк ​

Дубликаты объектов (абсолютно одинаковые строки в таблице) искусственно завышают важность отдельных паттернов данных, смещают баланс классов и могут привести к переобучению (overfitting) модели, если один и тот же объект попадет и в обучающую, и в тестовую выборки.

Для очистки множества от повторений Pandas предоставляет два эффективных C-метода:

  • dataframe.duplicated() — возвращает булеву маску, где строки, повторяющие ранее встреченные записи, помечаются как True.
  • dataframe.drop_duplicates() — находит, удаляет все дублирующиеся строки и возвращает чистый оттянутый датасет.

Оба метода поддерживают управляющий параметр inplace=True для проведения модификации физического буфера памяти на месте без лишних аллокаций RAM.

python
import pandas

raw_logs = {"feature_X": [1, 1, 2, 2], "feature_Y": [5, 5, 6, 6]}
df_logs = pandas.DataFrame(raw_logs)

# Removing duplicated row instances directly in-place
df_logs.drop_duplicates(inplace=True)
print(df_logs.shape)  # Outputs reduced geometry: (2, 2)

Упражнения базовые (5 шт) (только с опорой на объясненную в данном уроке теорию) ​

1. Подсчет пропущенных полей в таблице ​

Задача: Дан DataFrame df_metrics = pandas.DataFrame({"loss": [0.1, pandas.NA, 0.4], "acc": [pandas.NA, 0.8, 0.9]}). Напишите строку кода, которая подсчитывает общее количество пропущенных значений для каждого столбца. Выведите результат.

Посмотреть решение
python
import pandas

df_metrics = pandas.DataFrame({"loss": [0.1, pandas.NA, 0.4], "acc": [pandas.NA, 0.8, 0.9]})

# Combining missing detectors and sum aggregators vertically
missing_counts = df_metrics.isna().sum()
print(missing_counts)

2. Жесткое удаление поврежденных строк ​

Задача: На основе объекта df_metrics из предыдущего упражнения примените метод .dropna(), чтобы удалить все строки, содержащие хотя бы один маркер пропуска. Выведите очищенный DataFrame.

Посмотреть решение
python
import pandas

df_metrics = pandas.DataFrame({"loss": [0.1, pandas.NA, 0.4], "acc": [pandas.NA, 0.8, 0.9]})

# Deleting rows along the samples index axis axis=0
sanitized_df = df_metrics.dropna(axis=0)
print(sanitized_df)  # Contains only the last row index 2

3. Импутация пропусков константой на месте ​

Задача: Создайте одномерную структуру biases = pandas.Series([0.15, pandas.NA, -0.44]). Используя метод .fillna(), замените пропущенное значение на фиксированную константу 0.0 принудительно на месте (inplace=True). Выведите измененную структуру.

Посмотреть решение
python
import pandas

biases = pandas.Series([0.15, pandas.NA, -0.44])

# Imputing static scalar value to patch missing fields inside memory
biases.fillna(0.0, inplace=True)
print(biases)

4. Поиск дублирующихся строк объектов ​

Задача: Дан DataFrame df_samples = pandas.DataFrame({"X": [1, 1, 2, 1], "Y": [10, 10, 12, 10]}). Примените метод .duplicated(), чтобы получить булеву маску дубликатов, и выведите её на экран.

Посмотреть решение
python
import pandas

df_samples = pandas.DataFrame({"X": [1, 1, 2, 1], "Y": [10, 10, 12, 10]})

# Screening for identical sample duplicates records
duplicates_mask = df_samples.duplicated()
print(duplicates_mask)  # Rows at indices 1 and 3 will be marked True

5. Очистка датасета от повторений ​

Задача: На основе объекта df_samples из прошлого упражнения вызовите метод .drop_duplicates(), чтобы получить очищенную таблицу, где каждая строка уникальна. Выведите её форму .shape.

Посмотреть решение
python
import pandas

df_samples = pandas.DataFrame({"X": [1, 1, 2, 1], "Y": [10, 10, 12, 10]})

# Dropping duplicate rows from the dataset block
clean_df = df_samples.drop_duplicates()
print("Sanitized unique shape geometry:", clean_df.shape)  # Outputs: (2, 2)

Упражнения смешанные (5 шт) (включают материал прошлых уроков) ​

6. Загрузка CSV-файла с ограничением колонок и заполнением пропусков медианой ​

Задача: Прочитайте файл "raw_features.csv", ограничив загрузку только признаком ["feature_velocity"] с помощью параметра usecols (Урок 8). Извлеките этот столбец в виде объекта Series (Урок 2). Рассчитайте медиану по валидным элементам столбца с помощью метода .median() (Урок 5). Заполните все скрытые пропуски NaN этой вычисленной медианой на месте (Урок 9). Выведите очищенный вектор.

Посмотреть решение
python
import pandas

# Step 1: Read specific column array directly from storage (Lesson 8)
raw_df = pandas.read_csv(filepath_or_buffer="raw_features.csv", usecols=["feature_velocity"])
velocity_series = raw_df["feature_velocity"]

# Step 2: Compute median statistics metric (Lesson 5)
calculated_median = velocity_series.median()

# Step 3: Impute missing entries in place using the median (Lesson 9)
velocity_series.fillna(calculated_median, inplace=True)
print(velocity_series)

7. Удаление дубликатов и выделение матрицы X из Parquet-файла ​

Задача: Загрузите бинарный колоночный файл данных "raw_batch.parquet" (Урок 8). Выполните операцию удаления полностью идентичных строк-дубликатов объектов на месте через .drop_duplicates(inplace=True) (Урок 9). После очистки примените метод .drop(axis=1), чтобы отделить чистую матрицу признаков X, убрав столбец разметки с именем "target_class" (Урок 7). Выведите форму .shape полученной матрицы X (Урок 5).

Посмотреть решение
python
import pandas

# Step 1: Highly optimized parquet file reading (Lesson 8)
raw_data_df = pandas.read_parquet(path="raw_batch.parquet")

# Step 2: Remove duplicated records inside memory buffer (Lesson 9)
raw_data_df.drop_duplicates(inplace=True)

# Step 3: Feature spaces isolation along axis=1 (Lesson 5 & Lesson 7)
matrix_X = raw_data_df.drop("target_class", axis=1)
print("Unique training samples feature space geometry shape:", matrix_X.shape)

8. Сложное булево фильтрование строк датасета после жесткого удаления NaN ​

Задача: Дан DataFrame логов экспериментов df_experiments = pandas.DataFrame({"loss_rate": [0.85, pandas.NA, 0.02, 0.15], "status": ["active", "valid", "corrupted", "active"]}) (Урок 5). Жестко удалите строки, содержащие пропуски в данных, с помощью метода .dropna(axis=0) (Урок 9). К полученной чистой таблице примените оператор .loc[] для отбора строк, у которых показатель "loss_rate" строго меньше 0.5 и одновременно статус равен строго "active" (Урок 6). Выведите батч строк.

Посмотреть решение
python
import pandas

df_experiments = pandas.DataFrame({"loss_rate": [0.85, pandas.NA, 0.02, 0.15], "status": ["active", "valid", "corrupted", "active"]})

# Step 1: Discard any corrupted rows blocks from memory layout (Lesson 9)
sanitized_base_df = df_experiments.dropna(axis=0)

# Step 2: Extract subset rows complying with dual constraint criteria masks (Lesson 6)
target_batch_df = sanitized_base_df.loc[
    (sanitized_base_df["loss_rate"] < 0.5) & (sanitized_base_df["status"] == "active"), :
]
print(target_batch_df)

9. Сортировка по значениям Series после точечной импутации средним ​

Задача: Создайте одномерную структуру оценок точности scores = pandas.Series([0.78, pandas.NA, 0.92, 0.85, pandas.NA]) (Урок 2). Вычислите среднее арифметическое значение фичи методом .mean() (Урок 5). Заполните все маркеры пропусков этим средним значением (Урок 9). После импутации отсортируйте полученный плотный вектор по значениям элементов в порядке убывания через метод .sort_values(ascending=False) (Урок 4). Выведите результат.

Посмотреть решение
python
import pandas

scores = pandas.Series([0.78, pandas.NA, 0.92, 0.85, pandas.NA])

# Step 1: Calculate arithmetic feature average scalar (Lesson 5)
feature_mean = scores.mean()

# Step 2: Missing tokens imputation replacement (Lesson 9)
scores.fillna(feature_mean, inplace=True)

# Step 3: Descending sorting operation (Lesson 4)
ordered_scores = scores.sort_values(ascending=False)
print(ordered_scores)

10. Векторизованный расчет экспоненциального сглаживания над очищенным C-буфером через NumPy ​

Задача: Дан DataFrame сырых логитов df_logits = pandas.DataFrame({"logits": [1.2, pandas.NA, -0.5, 1.2], "idx": [1, 2, 3, 1]}) (Урок 5). Удалите полностью все строки-дубликаты на месте (Урок 9). После удаления дубликатов заполните оставшиеся пропуски методом .fillna(0.0) и приведите столбец к строгому типу float64 через .astype("float64") (колонка, содержащая pandas.NA, по умолчанию имеет тип object, который векторизованные ufunc-функции NumPy не принимают) (Урок 9). Обратитесь к низкоуровневому C-буферу .values (Урок 2) и пропустите его сквозь векторизованную ufunc-функцию экспоненты numpy.exp() из модуля NumPy (Урок 8 модуля NumPy). Выведите результат.

Посмотреть решение
python
import numpy
import pandas

df_logits = pandas.DataFrame({"logits": [1.2, pandas.NA, -0.5, 1.2], "idx": [1, 2, 3, 1]})

# Step 1: Remove twin rows duplicates from container (Lesson 9)
df_logits.drop_duplicates(inplace=True)

# Step 2: Impute the missing field and cast the object buffer to strict float64
# (a column holding pandas.NA is inferred as object dtype, which NumPy ufuncs reject)
df_logits["logits"] = df_logits["logits"].fillna(0.0).astype("float64")

# Step 3: Stream clean C-array memory layout into a NumPy universal math_oge function (NumPy Lesson 8 & Pandas Lesson 2)
vectorized_exponentials = numpy.exp(df_logits["logits"].values)
print(vectorized_exponentials)