Текстовые столбцы — главный источник грязи в данных. Один человек пишет «Москва», другой «москва», третий ставит пробел в конце. Для человека это одно и то же, для программы — три разных значения. Продолжим чистить прайс поставщика: возьмёмся за названия.
import pandas as pd
supplier = pd.read_csv("data/supplier_prices.csv")
supplier["name"].head(6).tolist()['эспрессо-смесь 1 кг ', 'Колумбия 250 г', 'эфиопия 250 г ', 'БРАЗИЛИЯ 1 КГ', 'Кофе без кофеина 250 г', ' Чёрный чай 100 г']
Лишние пробелы в начале и в конце, строчные и заглавные буквы вперемешку.
Методы строк — через .str
У строк Python есть методы lower, strip, replace и другие. Но столбец — не строка, и вызвать их у него
напрямую нельзя:
supplier["name"].lower()AttributeError: 'Series' object has no attribute 'lower'
Методы строк у столбца живут за приставкой .str. Она значит: «применить к каждому значению столбца»:
supplier["name"].str.lower().head(4)0 эспрессо-смесь 1 кг 1 колумбия 250 г 2 эфиопия 250 г 3 бразилия 1 кг Name: name, dtype: str
Через .str доступны знакомые методы строк и ещё несколько. Например, len — длина каждого значения. Длина
выдаёт пробелы, которых не видно в таблице:
print(supplier["name"].str.len().head(6).tolist())
print(supplier["name"].str.strip().str.len().head(6).tolist())[20, 14, 14, 13, 22, 18] [19, 14, 13, 13, 22, 16]
strip убирает пробелы в начале и в конце строки. Там, где длины отличаются, они были.
Невидимые пробелы
Лишний пробел не виден на экране, но ломает сравнение:
print((supplier["name"] == "Шоколад горький").sum())
print((supplier["name"].str.strip() == "Шоколад горький").sum())0 1
В файле он записан как " Шоколад горький" — с двумя пробелами в начале. Точное сравнение его не находит.
Результат метода .str — снова Series, поэтому методы можно вызывать цепочкой; приставку .str пишут перед
каждым. Регистр выравнивают четыре метода: lower — всё строчными, upper — всё заглавными, title — каждое
слово с заглавной буквы, capitalize — заглавная только первая буква строки, остальные строчные.
stripped = supplier["name"].str.strip()
print(stripped.str.upper().head(3).tolist())
print(stripped.str.title().head(3).tolist())['ЭСПРЕССО-СМЕСЬ 1 КГ', 'КОЛУМБИЯ 250 Г', 'ЭФИОПИЯ 250 Г'] ['Эспрессо-Смесь 1 Кг', 'Колумбия 250 Г', 'Эфиопия 250 Г']
Приведите столбец name таблицы supplier к единому виду: без пробелов в начале и в конце, первая буква
заглавная, остальные строчные. Результат запишите в тот же столбец.
supplier["name"].str.strip().str.capitalize().supplier["name"] = ....Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
# приведите в порядок столбец name таблицы supplierУпражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
supplier["name"] = supplier["name"].str.strip().str.capitalize()Заменить часть текста
.str.replace(что, на что) заменяет часть текста внутри каждого значения:
supplier["price"].str.replace(" ₽", "").head(3).tolist()['1 560', '760', '830']
Не путайте с методом replace без .str из прошлого модуля: тот заменяет значение целиком. Значения
" ₽" в столбце нет, и он ничего не меняет:
supplier["price"].replace(" ₽", "").head(3).tolist()['1 560 ₽', '760 ₽', '830 ₽']
Теперь можно довести до конца то, что не получилось в прошлом уроке, — сделать цены числами. Мешают знак рубля и пробел между тысячами.
Превратите столбец price таблицы supplier из текста вида 1 560 ₽ в целые числа и запишите в тот же
столбец. В price_total сохраните сумму цен.
.str.replace(" ₽", ""), затем .str.replace(" ", "").astype("int64").Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
# превратите столбец price таблицы supplier в числа
price_total = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
supplier["price"] = supplier["price"].str.replace(" ₽", "").str.replace(" ", "").astype("int64")
price_total = supplier["price"].sum()Найти подстроку
.str.contains(текст) возвращает маску: есть ли текст внутри значения.
supplier[supplier["name"].str.contains("кофе")]| product_id | name | category | price | cost | stock | updated | |
|---|---|---|---|---|---|---|---|
| 4 | P05 | Кофе без кофеина 250 г | Кофе | 760 | нет данных | 59.0 | 13.01.2026 |
Нашёлся только «Кофе без кофеина» — и то благодаря слову «кофеина»: поиск учитывает регистр, а «Кофемолка»
пишется с заглавной. Параметр case=False отключает учёт регистра:
supplier.loc[supplier["name"].str.contains("кофе", case=False), "name"].tolist()['Кофе без кофеина 250 г', 'Кофемолка ручная']
Есть и родственные методы: .str.startswith(текст) — начинается с текста, .str.endswith(текст) —
заканчивается им.
Что выведет print(pd.Series([" кофе", "кофе ", "Кофе"]).str.strip().nunique())?
strip убрал пробелы: первые два значения стали одинаковыми — «кофе». Третье отличается регистром, а регистр strip не трогает. Разных значений два. Одно осталось бы после .str.strip().str.lower().Сохраните в with_tea товары, в названии которых встречается «чай» в любом регистре. В real_tea —
товары категории «Чай».
supplier["name"].str.contains("чай", case=False) — маска.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
with_tea = ...
real_tea = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
with_tea = supplier[supplier["name"].str.contains("чай", case=False)]
real_tea = supplier[supplier["category"] == "Чай"]В with_tea попал заварочный чайник, а травяной сбор — нет, хотя он чай. Поиск по подстроке удобен, но слеп
к смыслу: проверяйте глазами, что именно нашлось.
Разбить на части
.str.split() разбивает каждое значение на слова по пробелам. В ячейке оказывается список:
words = supplier["name"].str.split()
words.head(3).tolist()[['Эспрессо-смесь', '1', 'кг'], ['Колумбия', '250', 'г'], ['Эфиопия', '250', 'г']]
Элемент списка достают через .str[номер] — как индекс у обычного списка:
print(words.str[0].head(3).tolist())
print(words.str[-1].head(3).tolist())['Эспрессо-смесь', 'Колумбия', 'Эфиопия'] ['кг', 'г', 'г']
Для самих строк .str[...] работает как срез — берёт символы:
print(supplier["updated"].head(3).tolist())
print(supplier["updated"].str[:2].head(3).tolist())['15.01.2026', '13.01.2026', '12.01.2026'] ['15', '13', '12']
Текстовые столбцы можно складывать друг с другом и со строками — получается склейка:
(supplier["product_id"] + ": " + supplier["name"]).head(3).tolist()['P01: Эспрессо-смесь 1 кг', 'P02: Колумбия 250 г', 'P03: Эфиопия 250 г']
У весовых товаров название заканчивается единицей: «250 г», «1 кг». Добавьте в supplier столбец unit —
последнее слово названия. В n_grams сохраните, сколько товаров продаётся в граммах (единица — ровно «г»).
supplier["name"].str.split().str[-1].supplier["unit"] == "г".Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
# добавьте в supplier столбец unit — последнее слово названия
n_grams = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
supplier["unit"] = supplier["name"].str.split().str[-1]
n_grams = (supplier["unit"] == "г").sum()Итоги
- Методы строк вызывают через
.str:столбец.str.lower(); в цепочке.strпишут перед каждым методом. strip— убрать пробелы по краям,lower/upper/title/capitalize— регистр,len— длина..str.replaceменяет часть текста;replaceбез.str— значение целиком..str.contains(текст, case=False)— маска «текст встречается»..str.split()— список слов,.str[i]— элемент списка или символы строки.