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

Дати и времеви редове

Накратко: Времевият ред е данни, подредени по дата; в pandas текстът става дата с to_datetime, resample събира по дни, седмици и месеци, а rolling дава плъзгащо средно.

Проблем

„Как върви годината?“

Магазинът има поръчките за цяла година: от ноември 2025 до края на октомври 2026. Управителят пита кой месец е най-силен, в кой ден от седмицата се продава най-много и как се мени оборотът от месец на месец.

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

Поръчките са 11 116. Колоната ordered_at обаче е str: за pandas „2025-11-01 07:20“ е само текст. Месецът не може да се вземе от него:

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

Идеята

Текстът става дата

parse_dates казва на read_csv кои колони да прочете като дати. Същото прави pd.to_datetime(orders['ordered_at']) върху готова колона, както видя в урока за типовете:

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

Типът е datetime64: момент във времето, с дата и час. С него можеш да сравняваш, да вадиш и да взимаш части. Разликата между последната и първата поръчка е 364 дни и 15 часа и 41 минути. Тя е времева разлика (Timedelta), не число.

Таблица, в която всеки ред е момент или период и редовете вървят по времето, се казва времеви ред (time series).

Идеята

Части от датата: .dt

.dt дава частите на всяка дата в колоната: year, month, day, hour, dayofweek (0 е понеделник), day_name(). Кога през деня се поръчва най-много?

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

Има два върха: на обед (между 12 и 14 часа, около 800 поръчки на час) и вечер (между 19 и 21 часа, около 1200 на час). Сутрин и късно вечер поръчките са по-малко от половината.

day_name() дава имената на английски („Saturday“). Имената на български ще ги сложиш сам с речник, малко по-нататък.

Идеята

Филтър по дата

Колона с дати се сравнява с текст като „2025-12-01“: pandas го превръща в дата. Поръчките през декември:

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

Правилно е 1285. С <= '2025-12-31' излизат 1250, а последната поръчка в тях е от 30 декември. Причината е часът: „2025-12-31“ е 31 декември в 00:00, а всички поръчки от 31-ви са след полунощ.

Затова за период пишеш „от първия ден включително до първия ден на следващия период без него“: >= '2025-12-01' и < '2026-01-01'.

Пробвай

Седмицата преди Коледа

Колко поръчки има от 22 до 28 декември 2025 включително? Запиши в week тези поръчки.

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

Идеята

По месеци: resample

За оборот по месеци трябва първо оборотът на всяка поръчка. Той е сборът на редовете ѝ: groupby по order_id, после merge с поръчките. Отказаните поръчки не са оборот, затова sold е без тях.

После resample събира редовете по периоди от времето. Той е groupby, само че групите са периоди: дни ('D'), седмици ('W'), месеци ('MS'). Работи върху индекс от дати, затова първо set_index('ordered_at'):

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

Декември е най-силният месец: 15 915,90 €. Януари (7 228,20 €) и февруари (7 622,30 €) са слаби, както често е след празниците. Най-слаб обаче е септември: 7 128,80 €. Защо, ще разберем в проекта в края на курса.

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

'MS' (month start) слага на всеки месец етикета на първия му ден, а 'ME' (month end) на последния. Сумите са същите.

'W' слага на седмицата етикета на неделята, с която свършва. Първата седмица тук е само 1 и 2 ноември (събота и неделя), затова е много по-малка от другите. Първият и последният период често са непълни: провери ги, преди да ги сравняваш.

Ако няма поръчки в някой ден, resample('D').sum() пак дава реда, с 0. В тези данни всеки ден има поръчки.

Пробвай

Най-силната седмица

Коя седмица е донесла най-голям оборот? Запиши в weekly оборота по седмици (без отказаните поръчки), а в best_week датата на най-силната седмица.

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

Идеята

Плъзгащо средно: rolling

Оборотът по дни скача нагоре-надолу: в петък е висок, в неделя е нисък. Зад този шум е трудно да видиш дали върви нагоре. Плъзгащото средно (moving average) смята за всеки ден средното на последните 7 дни, заедно с него:

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

rolling(7) е прозорец от 7 реда, който се плъзга с един ред надолу. Първите 6 стойности са NaN: преди 7 ноември няма 7 дни назад. На 7 ноември средното на първите седем дни е 374,23 €. После всеки ден влиза един нов и излиза най-старият, затова линията се мени плавно.

Идеята

Ден от седмицата

Колко е средният оборот в понеделник, във вторник и така нататък? Групираш оборота по дни по номера на деня от индекса, daily.index.dayofweek, и взимаш средната. Групираш по номера, а не по името, защото имената се подреждат по азбука. После с речник (и map) даваш на номерата имена:

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

Петъкът е най-силен: средно 350,79 €. Неделята е най-слаба: 250,85 €. Това е седмичният ритъм, който rolling(7) изглажда: прозорец от 7 дни съдържа всеки ден от седмицата точно веднъж.

Тук индексът вече е от дати, затова dayofweek е направо на индекса. При колона пишеш .dt.dayofweek.

1. Защо плъзгащото средно на оборота по дни е с прозорец 7, а не 5 или 10?

Идеята

Месец спрямо месец: pct_change

Управителят не пита „колко“, а „с колко повече или по-малко от миналия месец“. pct_change() дели всяка стойност на предишната и вади 1: дава промяната като дял. По 100 е в проценти:

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

Ноември е NaN: преди него няма месец. Декември е с 71,2% над ноември, а януари е с 54,6% под декември. Септември е с 19,1% под август, а октомври се връща с 39,2% нагоре.

Дали спадът през септември е истински, или е шум, и каква е причината, е въпросът на проекта. Засега знаеш, че го има и колко е голям.

Упражнение

Кога поскъпна кафето?

Кафето на зърна (продукт 17) поскъпна някъде през годината. Запиши в price_by_month средната цена на кафето за всеки месец (по реда на месеците във времето), а в first_new датата и часа на първата продажба по новата цена 14,9.

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

Тест

Провери се

1. Колоната ordered_at е дата и час. Защо за поръчките през декември пишеш < "2026-01-01", а не <= "2025-12-31"?
2. Какво трябва да има таблицата, за да работи resample("MS")?
3. Защо първите 6 стойности на daily.rolling(7).mean() са NaN?
4. Оборотът е 8 812,70 € през август и 7 128,80 € през септември. Какво дава pct_change() за септември, умножено по 100 и закръглено?

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