Към съдържанието
wannacode

Типове и липсващи стойности

Накратко: Всяка колона в pandas има тип (dtype), който решава какво може с нея, а липсващите стойности (NaN) се намират с isna и се попълват с fillna или махат с dropna.

Проблем

„В кой ден поръчват най-много?“

Управителят иска да знае кога да има повече хора на склада. Във файла year_orders.csv са 11 116 поръчки за една година, всяка с дата и час. Изглежда като една команда: вземи деня от датата и преброй.

Pythonданни: таблиците на магазина

Грешка. dtypes казва защо: ordered_at е str, текст. За нас „2025-11-01 07:20“ е дата, но read_csv вижда цифри, тирета и двоеточие и не гадае.

Типът на колоната решава какво можеш да правиш с нея. В този урок ще видиш как се сменя, кои типове пестят памет и дават ред, и какво става, когато стойност липсва.

Идеята

Всяка колона има тип: astype

Всяка колона на DataFrame има един тип (dtype). Видя int64 и float64 за числа и str за текст. Има още bool за True и False, datetime64 за дати и category за малко на брой повтарящи се стойности.

read_csv познава типа от текста във файла. Когато сбърка или когато ти трябва друг, astype прави колона от нов тип. В products категорията е float64, защото подаръчната карта няма категория, а NaN е дробно число. Целите числа с липси пази типът Int64 (с главно I):

Pythonданни: таблиците на магазина

Числата отново са цели, а липсата е <NA>: знакът за „няма“ на новите типове в pandas. С astype(int) щеше да получиш грешка IntCastingNaNError, защото обикновеният int няма място за липса.

1. prices са цените на първите четири продукта: 1.2, 1.9, 0.8 и 2.5. Какво дава prices.astype(int)?

Виж го

Малко стойности, много редове: category

Колоната status има 11 116 реда, но само пет различни думи. Като текст всяка дума се пази във всеки ред. Типът category пази петте думи веднъж, а за всеки ред само номера на думата:

Pythonданни: таблиците на магазина

377 057 байта стават 11 345, около 33 пъти по-малко. В годината на магазина това е дребно, но в таблица с милиони редове е разликата между „влиза в паметта“ и „не влиза“.

Категорията има и втора полза: ред. Статусите не са азбука, а път: нова, платена, изпратена, доставена. CategoricalDtype с ordered=True казва този ред:

Pythonданни: таблиците на магазина

value_counts(sort=False) брои в реда на пътя, а не по брой. Сравнението < 'delivered' значи „преди доставката“: 205 поръчки са още на път. Отказаните са сложени накрая, встрани от пътя.

Пробвай

Статусите по пътя

Направи колоната status категория с реда new, paid, shipped, delivered, cancelled и запиши в status_counts колко поръчки има на всяка стъпка, в този ред.

Pythonданни: таблиците на магазина

Идеята

Дати: pd.to_datetime и .dt

pd.to_datetime прави от текста дати. Колоната става от тип datetime64 и вече знае календара. Частите на датата дава .dt: .dt.year, .dt.month, .dt.day_name() и още много:

Pythonданни: таблиците на магазина

Годината е от 1 ноември 2025, 7:20, до 31 октомври 2026, 23:01. Първата поръчка е в събота. .dt работи само върху дати: върху текста от началото даде грешката.

day_name() дава имената на английски, както ще ги видиш и в документацията. Повече за датите (по седмици, по месеци, плъзгащо средно) има в урока за времето.

1. when е колона с дати. Първата поръчка е от 1 ноември 2025, събота. Какво дава when.dt.day за нея?

Идеята

Текст: .str

Каквото .dt е за дати, .str е за текст: прилага метод на низа към всеки ред наведнъж. str.contains вече видя във филтрите. Има още str.lower() и str.upper(), str.strip() (маха интервалите в краищата), str.len(), str.split() и изрязване като при низ: str[:14].

Етикетите на рафта побират 14 знака. Кои имена не се побират и как ще изглеждат отрязани?

Pythonданни: таблиците на магазина

7 имена са по-дълги от 14 знака. str[:14] ги реже, а str.upper() ги прави с главни букви за етикета. Резултатът е пак колона, затова assign я слага до името.

Идеята

Липсващи стойности: isna и notna

В почти всяка истинска таблица нещо липсва: клиент не е казал града си, касата не е пратила отчет. pandas пише липсата като NaN (при дати NaT, при новите типове <NA>). isna() дава маска True там, където липсва, а .sum() върху нея брои липсите във всяка колона:

Pythonданни: таблиците на магазина

Един клиент няма град. notna() е обратната маска: True там, където има стойност. С двете маски филтрираш, както с всяко условие.

Виж го

Защо 0 не е „липсва“

Баницата и краставиците са с наличност 0. Липса ли е това?

Pythonданни: таблиците на магазина

isna() не намира нищо в stock: нулите са истински стойности. 0 значи „знаем, че няма нито една“, а NaN значи „не знаем“.

Сега оборотът по дни от урока за обобщенията, но като Series в pandas. Една важна разлика: NumPy даваше nan за средната, а pandas прескача липсите:

Pythonданни: таблиците на магазина

count() брои 6 известни дни от 7. Средната на тях е 494,70 €, както с np.nanmean. Сумата 2 968,20 € обаче тихо е без сряда: pandas не предупреждава. Със fillna(0) средната пада на 424,03 €, защото нулата казва „в сряда не продадохме нищо“, а това не е вярно.

По-точно, за любопитните

Прескачането се управлява с параметъра skipna: revenue.mean(skipna=False) дава nan, като NumPy. Полезно е, когато предпочиташ сметката да „светне“ при липса, вместо тихо да даде по-малко число.

Идеята

fillna, dropna или нищо

Има три пътя с липсата, и изборът зависи от въпроса, а не от кода:

  • fillna(стойност), когато знаеш какво значи липсата. В отчет „клиенти по град“ липсващият град е „неизвестен“: той е отговор, не измислица.
  • dropna(), когато редът не може да отговори на въпроса. В отчет по категории подаръчната карта няма къде да отиде. subset=[...] гледа само дадените колони.
  • Нищо, когато не знаеш. Оборотът в сряда не е 0, а средната на известните дни е по-честна.
Pythonданни: таблиците на магазина

dropna махна подаръчната карта: от 25 продукта остават 24, колкото са и notna() в категорията. В отчет „колко продукта продаваме“ това би била грешка. fillna('неизвестен') прави липсващия град отделен ред в value_counts.

Пробвай

Клиентите по град

Маркетингът иска да знае откъде са клиентите. Запиши в missing_city колко клиенти нямат град, а в by_city колко клиенти има във всеки град, като липсващият град се брои като „неизвестен“.

Pythonданни: таблиците на магазина

Упражнение

Денят с най-много поръчки

Отговори на управителя. Направи ordered_at дата, сложи в нова колона day името на деня от седмицата, запиши в by_day колко поръчки има във всеки ден и в best_day деня с най-много поръчки.

Pythonданни: таблиците на магазина

Тест

Провери се

1. Колоната ordered_at е прочетена от CSV и е от тип str. Защо orders['ordered_at'].dt.month дава грешка?
2. Колоната status има 11 116 реда, но само 5 различни стойности. Какво печелиш с astype('category')?
3. В оборота по дни един ден е NaN. Какво дава revenue.mean() в pandas?
4. Две стоки имат наличност 0, а една има категория NaN. Кое от тях е „липса“?
5. Правиш отчет „колко продукта продаваме“, а подаръчната карта няма категория. Какво правиш с нея?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·