Отдел маркетинга «Кофейной лавки» выгрузил список клиентов из CRM и попросил простую вещь: сколько клиентов в каждом городе и сколько бонусных баллов у них в среднем. Но сначала выгрузку придётся привести в порядок. Новых методов в проекте нет — только приёмы этого модуля, собранные в правильном порядке. Проект идёт в двух уроках: в этом чистим текст, во второй части — даты, баллы и дубли, сверяем результат с эталоном и отвечаем маркетингу.
import pandas as pd
customers = pd.read_csv("data/customers_raw.csv")
customers.head(8)| customer_id | name | city | signup_date | segment | bonus | ||
|---|---|---|---|---|---|---|---|
| 0 | C001 | Сергей Ю. | Екб | 2025-06-12 | постоянный | client001@lavka.example | 120 |
| 1 | C002 | Сергей В. | екатеринбург | 2025-01-14 | новый | client002@lavka.example | 2 400 |
| 2 | C003 | Максим С. | Екб | 2025-05-19 | новый | client003@lavka.example | 50 |
| 3 | C004 | Андрей Д. | москва | 2023-09-15 | НОВЫЙ | client004@lavka.example | 1900 |
| 4 | C005 | Наталья Е. | Екатеринбург | 2024-08-24 | постоянный | NaN | NaN |
| 5 | C006 | Иван Е. | москва | 2023-02-26 | новый | NaN | 450 |
| 6 | C222 | Максим Я. | Екб | 24.08.2024 | постоянный | client222@lavka.example | 450 |
| 7 | C007 | Алексей П. | САНКТ-ПЕТЕРБУРГ | 2023-04-13 | новый | client007@lavka.example | NaN |
В первых же строках видно почти всё: «Екб» и «екатеринбург», пробелы вокруг имён, НОВЫЙ заглавными, баллы
с пробелом (2 400), пустые поля.
customers.info()<class 'pandas.DataFrame'> RangeIndex: 257 entries, 0 to 256 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customer_id 257 non-null str 1 name 257 non-null str 2 city 257 non-null str 3 signup_date 251 non-null str 4 segment 257 non-null str 5 email 208 non-null str 6 bonus 246 non-null str dtypes: str(7) memory usage: 7.1 KB
Все семь столбцов текстовые — и дата, и баллы. Пропуски есть в дате регистрации, адресе почты и баллах.
print(customers.duplicated().sum())
print(customers["customer_id"].nunique())
customers["city"].value_counts().head(8)10 243
city москва 48 Москва 34 Санкт-Петербург 26 Екб 23 казань 20 СПб 18 новосибирск 10 Новосибирск 9 Name: count, dtype: int64
Полных повторов 10, разных customer_id — 243, а городов — два десятка вместо пяти. Верить этим числам
пока нельзя: из-за пробелов и регистра одно и то же значение считается несколько раз.
План очистки: текст → даты → числа → дубликаты. Дубликаты — в конце: пока текст грязный, их не видно.
Шаг 1. Коды и имена
Приведите customer_id к виду C001: без пробелов, заглавная буква. Приведите name к виду «Сергей Ю.»:
без пробелов по краям, один пробел между именем и инициалом, заглавные буквы только в начале слов. В n_ids
сохраните число разных customer_id после очистки.
customers["customer_id"].str.strip().str.upper()..str.strip().str.replace(" ", " ").str.title(): title делает заглавной первую букву каждого слова.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
# почистите столбцы customer_id и name
n_ids = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
customers["customer_id"] = customers["customer_id"].str.strip().str.upper()
customers["name"] = customers["name"].str.strip().str.replace(" ", " ").str.title()
n_ids = customers["customer_id"].nunique()Разных кодов стало меньше: часть записей отличалась только регистром или пробелом.
Шаг 2. Города и сегменты
После strip и title варианты регистра схлопнутся, но сокращения останутся:
sorted(customers["city"].str.strip().str.title().unique())['Екатеринбург', 'Екб', 'Казань', 'Москва', 'Новосибирск', 'С.-Петербург', 'Санкт-Петербург', 'Спб']
Приведите city к пяти городам: уберите пробелы, выровняйте регистр и замените сокращения по словарю short.
Приведите segment к трём значениям строчными буквами.
customers["city"].str.strip().str.title().replace(short) — последний replace без .str: он заменяет значение целиком.customers["segment"].str.strip().str.lower().Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
short = {"Спб": "Санкт-Петербург", "С.-Петербург": "Санкт-Петербург", "Екб": "Екатеринбург"}
# почистите столбцы city и segmentУпражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
short = {"Спб": "Санкт-Петербург", "С.-Петербург": "Санкт-Петербург", "Екб": "Екатеринбург"}
customers["city"] = customers["city"].str.strip().str.title().replace(short)
customers["segment"] = customers["segment"].str.strip().str.lower()Шаг 3. Почта
Приведите email к строчным буквам. Пропуски оставьте пропусками. В no_email сохраните число строк без
адреса.
.str пропуски не трогают: NaN остаётся NaN.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
# приведите столбец email к строчным буквам
no_email = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
customers["email"] = customers["email"].str.lower()
no_email = customers["email"].isna().sum()Итог первой части
print(customers["customer_id"].nunique(), customers["city"].nunique(), customers["segment"].nunique())
print(customers.duplicated().sum())
customers["city"].value_counts()240 5 3 12
city Москва 95 Санкт-Петербург 64 Екатеринбург 42 Казань 35 Новосибирск 21 Name: count, dtype: int64
Кодов клиентов 240, городов пять, сегментов три — текстовые столбцы в порядке. Но отвечать маркетингу ещё
рано: value_counts считает строки, а не клиентов, и дубли в этих числах пока есть. Полных повторов стало 12
вместо 10 — часть дублей пряталась за пробелами и регистром. Удалять их будем в конце: дата регистрации
и баллы всё ещё записаны текстом. Этим займёмся во второй части.