Zum Inhalt
wannacode

Datumswerte und Zeitreihen

Kurz gesagt: Eine Zeitreihe sind Daten, die nach Datum geordnet sind; in pandas wird Text mit to_datetime zu einem Datum, resample fasst sie nach Tag, Woche und Monat zusammen, und rolling liefert einen gleitenden Durchschnitt.

Problem

„Wie läuft das Jahr?“

Der Shop hat die Bestellungen eines ganzen Jahres: von November 2025 bis Ende Oktober 2026. Der Chef fragt, welcher Monat der stärkste ist, an welchem Wochentag am meisten verkauft wird und wie sich die Einnahmen von Monat zu Monat ändern.

PythonDaten: die Tabellen des Shops

Es sind 11.116 Bestellungen. Aber die Spalte ordered_at ist str: Für pandas ist „2025-11-01 07:20“ nur Text. Den Monat kannst du daraus nicht nehmen:

PythonDaten: die Tabellen des Shops

Die Idee

Der Text wird zum Datum

parse_dates sagt read_csv, welche Spalten als Datumswerte gelesen werden. pd.to_datetime(orders['ordered_at']) macht dasselbe mit einer vorhandenen Spalte, wie du es in der Lektion über Typen gesehen hast:

PythonDaten: die Tabellen des Shops

Der Typ ist datetime64: ein Zeitpunkt, mit Datum und Uhrzeit. Damit kannst du vergleichen, subtrahieren und Teile entnehmen. Der Unterschied zwischen der letzten und der ersten Bestellung ist 364 Tage, 15 Stunden und 41 Minuten. Das ist ein Zeitunterschied (Timedelta), keine Zahl.

Eine Tabelle, in der jede Zeile ein Zeitpunkt oder ein Zeitraum ist und die Zeilen der Zeit folgen, heißt Zeitreihe.

Die Idee

Teile des Datums: .dt

.dt liefert die Teile jedes Datums in der Spalte: year, month, day, hour, dayofweek (0 ist Montag), day_name(). Wann am Tag wird am meisten bestellt?

PythonDaten: die Tabellen des Shops

Es gibt zwei Gipfel: mittags (zwischen 12 und 14 Uhr, etwa 800 Bestellungen pro Stunde) und abends (zwischen 19 und 21 Uhr, etwa 1.200 pro Stunde). Morgens und spät am Abend sind es weniger als halb so viele.

day_name() liefert die Namen auf Englisch („Saturday“). Die deutschen Namen setzt du gleich selbst mit einem Dictionary ein, etwas weiter unten.

Die Idee

Filtern nach Datum

Eine Datumsspalte kann mit Text wie „2025-12-01“ verglichen werden: pandas macht daraus ein Datum. Die Bestellungen im Dezember:

PythonDaten: die Tabellen des Shops

Die richtige Antwort ist 1285. Mit <= '2025-12-31' bekommst du 1250, und die letzte Bestellung darunter ist vom 30. Dezember. Der Grund ist die Uhrzeit: „2025-12-31“ ist der 31. Dezember um 00:00, und jede Bestellung am 31. kommt nach Mitternacht.

Für einen Zeitraum schreibst du also „ab dem ersten Tag, eingeschlossen, bis zum ersten Tag des nächsten Zeitraums, ausgeschlossen“: >= '2025-12-01' und < '2026-01-01'.

Probier es aus

Die Woche vor Weihnachten

Wie viele Bestellungen gibt es vom 22. bis 28. Dezember 2025 einschließlich? Leg diese Bestellungen in week ab.

PythonDaten: die Tabellen des Shops

Die Idee

Nach Monat: resample

Für die Einnahmen nach Monat brauchst du zuerst den Umsatz jeder Bestellung. Er ist die Summe ihrer Zeilen: groupby auf order_id, dann merge mit den Bestellungen. Stornierte Bestellungen sind kein Umsatz, also lässt sold sie weg.

Dann fasst resample die Zeilen in Zeiträume zusammen. Es ist ein groupby, dessen Gruppen Zeiträume sind: Tage ('D'), Wochen ('W'), Monate ('MS'). Es arbeitet auf einem Index aus Datumswerten, also zuerst set_index('ordered_at'):

PythonDaten: die Tabellen des Shops

Der Dezember ist der stärkste Monat: 15.915,90 €. Januar (7.228,20 €) und Februar (7.622,30 €) sind schwach, wie es nach den Feiertagen oft ist. Der schwächste ist aber der September: 7.128,80 €. Warum, finden wir im Projekt am Ende des Kurses heraus.

Genauer, für Neugierige

'MS' (month start) beschriftet jeden Monat mit seinem ersten Tag und 'ME' (month end) mit seinem letzten. Die Summen sind dieselben.

'W' beschriftet jede Woche mit dem Sonntag, an dem sie endet. Die erste Woche hier besteht nur aus dem 1. und 2. November (Samstag und Sonntag), also ist sie viel kleiner als die anderen. Der erste und der letzte Zeitraum sind oft unvollständig: Prüfe sie, bevor du sie vergleichst.

Wenn es an einem Tag keine Bestellungen gibt, liefert resample('D').sum() trotzdem die Zeile, mit 0. In diesen Daten gibt es an jedem Tag Bestellungen.

Probier es aus

Die stärkste Woche

Welche Woche hat am meisten Umsatz gebracht? Leg den Umsatz nach Woche (ohne die stornierten Bestellungen) in weekly ab und das Datum der stärksten Woche in best_week.

PythonDaten: die Tabellen des Shops

Die Idee

Gleitender Durchschnitt: rolling

Die Tageseinnahmen springen auf und ab: hoch an Freitagen, niedrig an Sonntagen. Hinter diesem Rauschen ist schwer zu sehen, ob es aufwärts geht. Ein gleitender Durchschnitt berechnet für jeden Tag den Durchschnitt der letzten 7 Tage, einschließlich dieses Tages:

PythonDaten: die Tabellen des Shops

rolling(7) ist ein Fenster von 7 Zeilen, das Zeile für Zeile nach unten gleitet. Die ersten 6 Werte sind NaN: Vor dem 7. November gibt es keine 7 Tage zurück. Am 7. November ist der Durchschnitt der ersten sieben Tage 374,23 €. Danach kommt mit jedem Tag ein neuer Tag hinein und der älteste geht heraus, also ändert sich die Linie sanft.

Die Idee

Wochentag

Wie hoch sind die durchschnittlichen Einnahmen an einem Montag, an einem Dienstag und so weiter? Du gruppierst die Tageseinnahmen nach der Nummer des Tages aus dem Index, daily.index.dayofweek, und nimmst den Mittelwert. Du gruppierst nach der Nummer und nicht nach day_name(), weil Namen alphabetisch sortiert werden und der Freitag zuerst käme. Dann gibt ein Dictionary (und map) den Nummern ihre Namen:

PythonDaten: die Tabellen des Shops

Der Freitag ist der stärkste: durchschnittlich 350,79 €. Der Sonntag ist der schwächste: 250,85 €. Das ist der Wochenrhythmus, den rolling(7) glättet: Ein 7-Tage-Fenster enthält jeden Wochentag genau einmal.

Hier besteht der Index schon aus Datumswerten, also ist dayofweek direkt auf dem Index richtig. Bei einer Spalte schreibst du .dt.dayofweek.

1. Warum nimmt der gleitende Durchschnitt der Tageseinnahmen ein Fenster von 7, nicht von 5 oder 10?

Die Idee

Monat zu Monat: pct_change

Der Chef fragt nicht „wie viel“, sondern „wie viel mehr oder weniger als im letzten Monat“. pct_change() teilt jeden Wert durch den davor und zieht 1 ab: Es liefert die Veränderung als Anteil. Mal 100 ist es ein Prozentsatz:

PythonDaten: die Tabellen des Shops

Der November ist NaN: Es gibt keinen Monat davor. Der Dezember liegt 71,2 % über dem November, und der Januar liegt 54,6 % unter dem Dezember. Der September liegt 19,1 % unter dem August, und der Oktober kommt um 39,2 % wieder hoch.

Ob der Einbruch im September echt oder nur Rauschen ist und was ihn verursacht hat, ist die Frage des Projekts. Für jetzt weißt du, dass er da ist und wie groß er ist.

Übung

Wann wurde der Kaffee teurer?

Die Kaffeebohnen (Produkt 17) wurden irgendwann im Jahr teurer. Leg in price_by_month den mittleren Preis des Kaffees für jeden Monat ab (in der zeitlichen Reihenfolge der Monate) und in first_new das Datum und die Uhrzeit des ersten Verkaufs zum neuen Preis von 14.9.

PythonDaten: die Tabellen des Shops

Test

Teste dich

1. Die Spalte ordered_at enthält ein Datum und eine Uhrzeit. Warum schreibst du für die Bestellungen im Dezember < "2026-01-01" und nicht <= "2025-12-31"?
2. Was braucht die Tabelle, damit resample("MS") funktioniert?
3. Warum sind die ersten 6 Werte von daily.rolling(7).mean() NaN?
4. Die Einnahmen sind 8.812,70 € im August und 7.128,80 € im September. Was liefert pct_change() für den September, mal 100 und gerundet?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·