Очистка данных в Python: полное руководство с примерами на Pandas

Раздел: Data Science -> Pandas

Введение в очистку данных

Очистка данных – обязательный этап анализа, позволяющий избавиться от пропусков, дубликатов, некорректных значений и несоответствий типов. Библиотека Pandas предоставляет гибкие инструменты для решения этих задач. Ниже приведены основные подходы и частные варианты их применения.

Наиболее эффективным способом является создание конвейера очистки – функции, которая последовательно применяет необходимые преобразования. Это позволяет повторно использовать код и избегать ошибок.


import pandas as pd
import numpy as np

def clean_data(df):
    # 1. Удаление дубликатов
    df = df.drop_duplicates()
    # 2. Обработка пропусков
    df['age'] = df['age'].fillna(df['age'].median())
    df['income'] = df['income'].fillna(df['income'].mean())
    # 3. Преобразование типов
    df['date'] = pd.to_datetime(df['date'], errors='coerce')
    df['salary'] = pd.to_numeric(df['salary'], errors='coerce')
    # 4. Удаление выбросов по IQR
    Q1 = df['income'].quantile(0.25)
    Q3 = df['income'].quantile(0.75)
    IQR = Q3 - Q1
    df = df[(df['income'] >= Q1 - 1.5*IQR) & (df['income'] <= Q3 + 1.5*IQR)]
    return df

df = pd.read_csv('data.csv')
df_clean = clean_data(df)
  

обработка больших данных python (обработка больших данных в python)

Данная функция охватывает основные этапы и может быть расширена под конкретные задачи.

Как заполнить пропуски средним или медианным значением?


df['column'].fillna(df['column'].mean(), inplace=True)
  

очистка данных python (очистка данных в python)

Проблема: среднее чувствительно к выбросам – медиана устойчивее. Если столбец категориальный, среднее может быть бессмысленным. Решение: использовать fillna с модой для категорий.


df['category'].fillna(df['category'].mode()[0], inplace=True)
    

Python подготовка данных (подготовка данных в python)

Как удалить строки с пропусками только в определенных столбцах?


df.dropna(subset=['col1', 'col2'], inplace=True)
  

Python работа с большими данными (работа с большими данными в python)

Ошибка: удаление слишком большого количества строк, если пропусков много. Рекомендуется сначала оценить долю пропусков через df.isnull().sum().

Как найти и удалить полные дубликаты?


df.duplicated().sum()  # количество дубликатов
df.drop_duplicates(inplace=True)
  

генерация данных python (генерация данных в python)

Проблема: дубликаты могут быть частичными (совпадение по подмножеству столбцов). Используйте subset.


df.drop_duplicates(subset=['name', 'date'], keep='first', inplace=True)
    

Python код символа (код символа в python)

Аргумент keep определяет, какую строку оставить ('first', 'last', False – удалить все).

Как изменить тип столбца на числовой?


df['col'] = pd.to_numeric(df['col'], errors='coerce')
  

код из файла python (код из файла python)

Ошибка: при errors='coerce' некорректные значения становятся NaN. Затем их можно заполнить или удалить. Альтернатива – astype, но она вызовет исключение при ошибке.


df['col'] = df['col'].astype(float)  # только если все значения корректны
    

обработка данных на python (обработка данных на python)

Как преобразовать строку с датой в формат datetime?


df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d', errors='coerce')
  

обработка символьных данных python (обработка символьных данных в python)

Проблема: несовпадение формата – дата может быть записана как '01/12/2023'. Игнорирование format заставит Pandas угадывать, что замедляет работу и может привести к ошибкам. Решение: указать точный формат или использовать infer_datetime_format=True.

Как очистить строки от лишних пробелов и привести к нижнему регистру?


df['text'] = df['text'].str.strip().str.lower()
  

Python преобразование в строку (преобразование в строку в python)

Проблема: str.strip() не удаляет внутренние пробелы. Для замены нескольких пробелов одним используйте str.replace(r'\s+', ' ', regex=True). Также возможны проблемы с NaN – методы строк не работают с пропусками, поэтому предварительно заполните их.

Как удалить выбросы с помощью Z-оценки?


from scipy import stats
z_scores = np.abs(stats.zscore(df['col']))
df_clean = df[(z_scores < 3)]
  

Python как проверить строку (проверка строки в python)

Проблема: Z-оценка предполагает нормальное распределение. Если данные не нормальны, лучше использовать IQR или процентили. Также при малом размере выборки (n<30) Z-оценка ненадежна.


Q1 = df['col'].quantile(0.25)
Q3 = df['col'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5*IQR
upper = Q3 + 1.5*IQR
df_clean = df[(df['col'] >= lower) & (df['col'] <= upper)]
    
- Python dataframe строки (строки в dataframe pandas)
- столбец dataframe python (работа со столбцом dataframe в pandas)
- Python get url (получение url в python)

Расширенные примеры очистки данных с Pandas

Пример 1: Универсальный пайплайн с обработкой пропусков и выбросов

Пример

import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer

# Загрузка данных
df = pd.read_csv('transactions.csv')
print('Исходные данные:')
print(df.head())

# Создаем копию для экспериментов
df_exp = df.copy()

# 1. Удаление дубликатов по подмножеству столбцов
df_exp.drop_duplicates(subset=['customer_id', 'transaction_date'], keep='last', inplace=True)

# 2. Преобразование даты
df_exp['transaction_date'] = pd.to_datetime(df_exp['transaction_date'], format='%Y-%m-%d', errors='coerce')

# 3. Числовые столбцы – замена некорректных значений на NaN
numeric_cols = ['amount', 'tax', 'discount']
for col in numeric_cols:
    df_exp[col] = pd.to_numeric(df_exp[col], errors='coerce')

# 4. Заполнение пропусков в числовых столбцах медианой (более устойчива к выбросам)
for col in numeric_cols:
    median_val = df_exp[col].median()
    df_exp[col].fillna(median_val, inplace=True)

# 5. Заполнение пропусков в категориальных столбцах модой
categorical_cols = ['payment_method', 'status']
for col in categorical_cols:
    mode_val = df_exp[col].mode()[0]
    df_exp[col].fillna(mode_val, inplace=True)

# 6. Строковая очистка
if 'customer_name' in df.columns:
    df_exp['customer_name'] = df_exp['customer_name'].str.strip().str.title()

# 7. Удаление выбросов по методу IQR для 'amount'
Q1 = df_exp['amount'].quantile(0.25)
Q3 = df_exp['amount'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5*IQR
upper = Q3 + 1.5*IQR
df_exp = df_exp[(df_exp['amount'] >= lower) & (df_exp['amount'] <= upper)]

print('Данные после очистки:')
print(df_exp.head())
print(f'Размер исходного датасета: {df.shape[0]} строк, после очистки: {df_exp.shape[0]} строк')
Исходные данные:
   customer_id transaction_date    amount    tax discount payment_method status customer_name
0         1001       2023-01-15    250.00   20.0     10.0         credit   done      John Doe
1         1002       2023-01-16   1200.00   96.0     50.0           cash   pend      Jane Smith
2         1003       2023-01-17      0.00    0.0      0.0           cash   done     Bob Johnson
3         1001       2023-01-15    250.00   20.0     10.0         credit   done      John Doe
4         1004       2023-01-18   -500.00    NaN      NaN            NaN    NaN        Unknown

Данные после очистки:
   customer_id transaction_date    amount    tax discount payment_method status customer_name
0         1001       2023-01-15    250.00   20.0     10.0         credit   done      John Doe
1         1002       2023-01-16   1200.00   96.0     50.0           cash   pend    Jane Smith
2         1003       2023-01-17      0.00    0.0      0.0           cash   done   Bob Johnson
Размер исходного датасета: 5 строк, после очистки: 3 строки

Пояснение: дубликат строки 0 и 3 удалён; у строки 4 отрицательная сумма и много пропусков – она также исключена.

Пример 2: Обработка дат в разных форматах

Пример

df = pd.DataFrame({'raw_date': ['2023-01-15', '15/01/2023', '01-15-2023', 'invalid date', np.nan]})
df['date_parsed'] = pd.to_datetime(df['raw_date'], dayfirst=True, errors='coerce')
print(df)
     raw_date date_parsed
0  2023-01-15  2023-01-15
1  15/01/2023  2023-01-15
2  01-15-2023  2023-01-15
3  invalid date         NaT
4          NaN         NaT

Параметр dayfirst=True указывает, что день идёт перед месяцем. Для неоднозначных форматов рекомендуется использовать infer_datetime_format=True или задать format явно.

Пример 3: Чистка текстовых данных с помощью регулярных выражений

Пример

import re

df = pd.DataFrame({'text': ['  Hello   World!  ', 'Удалить\tсимволы\n', 'Цифры 123 и знаки @#$']})

# Удаление лишних пробелов и специальных символов
df['text'] = df['text'].str.replace(r'\s+', ' ', regex=True)  # заменяем множественные пробелы/табуляции/переносы на один пробел
df['text'] = df['text'].str.strip()  # обрезаем начало и конец
# Удаляем все символы, кроме букв и пробела
df['clean_text'] = df['text'].str.replace(r'[^а-яА-ЯёЁa-zA-Z\s]', '', regex=True)
print(df)
                 text           clean_text
0       Hello   World!          Hello World
1        Удалить символы     Удалить символы
2  Цифры 123 и знаки @#$      Цифры  и знаки 

Первый этап нормализует пробелы, второй – удаляет небуквенные символы. Обратите внимание, что русские буквы сохранены благодаря диапазону а-яА-ЯёЁ.

Пример 4: Обнаружение и удаление выбросов с помощью Z-оценки (с проверкой нормальности)

Пример

import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

# Генерация примера данных
np.random.seed(42)
df = pd.DataFrame({'value': np.random.normal(100, 15, 100)})  # нормальное распределение
df.loc[5] = 500  # выброс
df.loc[50] = -200 # выброс

# Проверка нормальности (визуально)
df['value'].hist(bins=20)
plt.title('Распределение до очистки')
plt.show()

# Z-оценка
z = np.abs(stats.zscore(df['value']))
print(f'Максимальная Z-оценка: {z.max():.2f}')

# Удаление выбросов со значением Z > 3
df_clean = df[z < 3]
print(f'Удалено {len(df) - len(df_clean)} выбросов')

# Статистика после
df_clean['value'].hist(bins=20)
plt.title('Распределение после очистки')
plt.show()
Максимальная Z-оценка: 17.53
Удалено 2 выбросов

Метод подходит только для приблизительно нормальных данных. Для проверки можно использовать тест Шапиро-Уилка (scipy.stats.shapiro).

Пример 5: Использование SimpleImputer из sklearn для заполнения пропусков

Пример

from sklearn.impute import SimpleImputer

# Создаём DataFrame с пропусками
df = pd.DataFrame({
    'A': [1, 2, np.nan, 4],
    'B': [np.nan, 2, 3, 4],
    'C': ['cat', 'dog', np.nan, 'cat']
})

# Для числовых столбцов – стратегия 'median'
num_imputer = SimpleImputer(strategy='median')
df[['A', 'B']] = num_imputer.fit_transform(df[['A', 'B']])

# Для категориального столбца – стратегия 'most_frequent'
cat_imputer = SimpleImputer(strategy='most_frequent')
df['C'] = cat_imputer.fit_transform(df[['C']]).ravel()

print(df)
     A    B    C
0  1.0  3.0  cat
1  2.0  2.0  dog
2  2.5  3.0  cat
3  4.0  4.0  cat

SimpleImputer удобен для пайплайнов машинного обучения, но не возвращает DataFrame с сохранёнными именами столбцов – необходимо присваивать обратно.

Очистка данных в Python - comments

En
очистка данных python (python)