Zum Inhalt
wannacode

Warum ist der Umsatz gefallen?

Kurz gesagt: Ein Datenanalyse-Projekt von der Frage bis zum Bericht: bereinigen, Diagramme, eine Prüfung auf Rauschen, den Umsatzeinbruch nach Bestellanzahl, Kategorie, Tag und Kanal zerlegen und ein Modell für den Verlust mit Intervall.

Problem

„Warum ist der September so schwach?“

Der Chef hat den Bericht gesehen: Der Umsatz im September 2026 beträgt 7.128,80 €, im August waren es 8.812,70 €. Das sind 19,1 % weniger. Er hat drei Ideen auf dem Tisch: die Werbung stoppen, weil sie „nicht wirkt“, den Milchlieferanten wechseln oder Rabatte geben.

Jede Idee antwortet auf eine andere Ursache. Ist der Einbruch Rauschen, tust du nichts. Kommt er von einem Produkt, schaust du auf den Lieferanten. Kaufen die Leute weniger, denkst du an die Preise. Die Frage der Analyse ist also nicht „wie groß ist der Einbruch“, sondern „wo ist der Einbruch, damit wir wissen, was wir reparieren“.

In diesem Projekt gehst du den ganzen Weg des Kurses: die Daten, das Bereinigen, das Diagramm, die Prüfung auf Rauschen, das Zerlegen, das Modell und den Bericht. Die Zahlen kommen aus dem Bestelljahr des Shops, und die Antwort liegt nicht dort, wo die Ideen des Chefs sie erwarten.

Die Idee

Die Daten und das Bereinigen

Zwei Tabellen: year_orders.csv (Bestellung, Kunde, Zeit, Kanal, Status) und year_items.csv (die Zeilen der Bestellungen). Der Wert einer Bestellung ist die Summe ihrer Zeilen. Stornierte Bestellungen sind kein Umsatz:

PythonDaten: die Tabellen des Shops

Es gibt 11.116 Bestellungen, und ohne die 657 stornierten bleiben 10.459 übrig. Bevor du etwas rechnest, prüfst du das Bereinigen: Jede Bestellung hat einen Wert (0 fehlen), die kleinste beträgt 0,80 €, und die Tage sind 365, vom 1. November 2025 bis zum 31. Oktober 2026. Der Umsatz des Jahres beträgt 111.186,20 €, dieselbe Zahl wie im Modul „Daten aufbereiten“.

new, paid und shipped sind Bestellungen der letzten Woche, die noch unterwegs sind. Du zählst sie mit: Sie sind bezahlt oder werden es. Die Spalte channel sagt, woher die Bestellung kam: von der Website (web), aus der App (app) oder vom Telefon (phone).

Schreib das Bereinigen selbst. orders['ordered_at'] soll ein Datum sein, items['revenue'] der Umsatz jeder Zeile, orders['value'] der Wert jeder Bestellung und sold die Bestellungen ohne die stornierten.

PythonDaten: die Tabellen des Shops

Die Idee

Zuerst das Diagramm

Bevor du nach einer Ursache suchst, sieh dir den Einbruch im Kontext des Jahres an:

PythonDaten: die Tabellen des Shops

Der September liegt bei 7.128,80 € gegenüber 8.812,70 € im August: −19,1 %. Der Oktober liegt bei 9.920,40 €, der Einbruch geht also nicht weiter. Das Diagramm zeigt noch etwas anderes: Der Dezember liegt über 15.000 €, und Januar und Februar sind ebenfalls niedrig. Der September ist ein Einbruch mitten in normalen Monaten, kein Teil einer Saison.

Der September hat 30 Tage und der August 31. Könnte das einen Teil des Einbruchs erklären? Lege in per_day den Umsatz pro Tag für jeden Monat ab und in change die Änderung des Umsatzes pro Tag von August zu September in Prozent.

PythonDaten: die Tabellen des Shops

Die Idee

Ist es Rauschen?

Monate unterscheiden sich immer ein wenig. Bevor du nach einer Ursache suchst, prüfst du, ob der Einbruch größer ist als das übliche Rauschen. Du vergleichst die Tage: 31 Tage im August gegen 30 im September, mit dem Permutationstest aus der Lektion über Hypothesen. Unten siehst du die Änderung des Umsatzes pro Tag von Monat zu Monat:

PythonDaten: die Tabellen des Shops

Ein Septembertag bringt im Durchschnitt 46,65 € weniger. Von 10.000 Mischungen der Tage ergeben nur 74 einen mindestens so großen Unterschied: p = 0,0074. Die normalen Monate von März bis August bewegen sich zwischen −6,5 % und +5,8 %, und der September liegt bei −16,4 %. Der Februar (+16,8 %) ist die Rückkehr nach den Feiertagen, und der Oktober (+34,7 %) ist die Rückkehr nach dem September.

Der Einbruch ist kein Rauschen. Es lohnt sich, nach einer Ursache zu suchen.

1. Der Permutationstest für die Tage im August gegen die Tage im September ergibt p = 0,0074. Was folgt daraus?

Die Idee

Den Einbruch zerlegen

Der Umsatz ist die Anzahl der Bestellungen mal der Wert einer Bestellung. Die erste Zerlegung zeigt, welches von beiden fällt:

PythonDaten: die Tabellen des Shops

Die Bestellungen fallen von 854 auf 701 (−17,9 %), und die durchschnittliche Bestellung ist fast gleich: 10,32 € und 10,17 €. Der Warenkorb ist nicht kleiner; es gibt weniger Bestellungen. Rabatte sind nicht die Antwort.

Könnte es ein Produkt sein, etwa die Milchprodukte vom neuen Lieferanten? Der Umsatz nach Kategorie:

PythonDaten: die Tabellen des Shops

Jede Kategorie fällt, von −9,0 % (Obst und Gemüse) bis −34,3 % (Getränke). Die Geschenkkarte liegt bei +14,3 %, aber das sind 160 € gegen 140 € im August, ein paar Karten. Wäre ein Produkt schuld, wären die anderen Kategorien normal. Der Einbruch ist überall: Es liegt nicht am Produkt.

Liegt der Einbruch an einem Wochentag, etwa weil der Shop donnerstags geschlossen war? Lege in by_wd den durchschnittlichen Umsatz pro Tag für jeden Wochentag (Zeilen 0–6) für August und September (Spalten) ab und dazu eine Spalte change_% mit der Änderung in Prozent.

PythonDaten: die Tabellen des Shops

Die Idee

Der Kanal

Eine Spalte bleibt, die du noch nicht angesehen hast: channel. Der Umsatz nach Monat und Kanal:

PythonDaten: die Tabellen des Shops

Da ist es. Die Website (web) liegt im August bei 4.517,00 € und im September bei 4.660,80 €. Das Telefon (phone) bei 999,70 € und 1.028,70 €. Sie sind normal. Die App (app) fällt von 3.296,00 € auf 1.439,30 € und kommt im Oktober auf 3.755,40 € zurück.

Der Einbruch der App beträgt 1.856,70 € und ist damit größer als der ganze Einbruch des Shops (1.683,90 €): Die anderen beiden Kanäle wachsen sogar ein wenig. Deshalb fallen alle Kategorien und alle Tage gemeinsam: Die App verkauft alles, jeden Tag.

Wie viel des Umsatzes kommt in einem gewöhnlichen Monat aus der App? Lege in share den Anteil von app am Umsatz jedes Monats in Prozent ab.

PythonDaten: die Tabellen des Shops

Die Idee

Wann genau?

„September“ ist grob. Das App-Team wird nach einem Datum fragen. Die App-Bestellungen Tag für Tag, mit dem gleitenden 7-Tage-Durchschnitt, und die Tage um beide Enden:

PythonDaten: die Tabellen des Shops

Die Linie fällt Anfang September und kommt Anfang Oktober zurück. Die Tabelle zeigt die Enden. Am 2. September hat die App 9 Bestellungen, am 3. September nur 3 und am 5. September null, während die Website 25 und 15 hat. Am Ende: 6 Bestellungen am 1. Oktober, 8 am 2. Oktober und 12 am 3. Oktober.

Der gleitende Durchschnitt verschmiert die Enden um ein paar Tage, deshalb suchst du die Daten in den einzelnen Tagen. Der Beginn ist klar: 3. September. Das Ende ist unschärfer: Der 2. Oktober ist schon halb zurück, und ab dem 3. Oktober ist die App wie zuvor. Der Einbruch läuft vom 3. September bis zum 2. Oktober, plus/minus ein Tag.

Finde die Daten mit einer Regel, nicht nach Augenmaß. start ist der erste Tag ab dem 1. August 2026, an dem die App unter 20 % der Bestellungen des Tages hat. back ist der erste Tag nach start, an dem die App-Bestellungen mindestens dem Median der normalen Tage (bis zum 31. August) entsprechen.

PythonDaten: die Tabellen des Shops

Die Idee

Was hat der Einbruch gekostet?

Der Chef wird fragen, wie viel Geld es gekostet hat. Die naive Antwort ist „August minus September“: 1.683,90 €. Aber das nimmt an, der September hätte wie der August sein sollen. Der Oktober ist stärker als der August, also hätte auch der September stärker sein können.

Die Kanäle, die funktionieren, geben einen besseren Vergleich. In normalen Zeiten bringt die App einen stabilen Anteil im Verhältnis zur Website und zum Telefon. Dieser Anteil k ist ein Modell mit einer einzigen Zahl: Es sagt den Umsatz der App aus dem Umsatz der anderen beiden Kanäle an denselben Tagen voraus. Du lernst es aus den Tagen vor dem Einbruch und prüfst es an Tagen, die es nicht gesehen hat: dem Oktober nach dem Einbruch.

PythonDaten: die Tabellen des Shops

Für jeden Euro von der Website und vom Telefon bringt die App 0,641 €. Für den Oktober nach dem Einbruch sagt das Modell 3.688,92 €, und die Wahrheit sind 3.614,60 €: ein Fehler von 2,1 %. Für die Tage vom 3. September bis zum 2. Oktober erwartet das Modell 3.713,41 €, und die App hat 1.292,60 € gebracht. Der Verlust beträgt 2.420,81 €, 737 € mehr als beim naiven Vergleich.

Wie genau ist das? Der Anteil k über 30 Tage ist nicht immer derselbe. Mit dem Bootstrap ziehst du 10.000-mal 30 normale Tage und siehst, wie stark sich der Verlust bewegt:

PythonDaten: die Tabellen des Shops

Über 30 zufällige normale Tage liegt k zwischen 0,457 und 0,906. Das 95-%-Intervall für den Verlust reicht von 1.802,46 € bis 3.142,14 €. Im Bericht heißt das „etwa 2.400 €, zwischen 1.800 € und 3.150 €“. Die Schätzung ist grob, aber selbst die untere Grenze liegt über den naiven 1.683,90 €.

Genauer, für Neugierige

Das Modell app = k × (web + phone) hat ein Merkmal und eine Zahl, wie eine lineare Regression ohne Achsenabschnitt. Seine Prüfung ist wie im Modul über Modelle: Du lernst es an einigen Tagen (vor dem 3. September) und misst es an anderen (nach dem 2. Oktober). Es nimmt an, dass sich die Kanäle gemeinsam bewegen, und die Prüfung am Oktober bestätigt das. Wären App-Kunden im September zur Website gewechselt, würde das Modell den Verlust überschätzen: Es würde Bestellungen als verloren zählen, die tatsächlich über die Website kamen. Die Website liegt im September bei +3,2 % gegenüber dem August, innerhalb der üblichen Bewegung, also sind die meisten der verlorenen Bestellungen nicht woandershin gegangen.

Wie viele Bestellungen hat die App verloren? Baue dasselbe Modell für die Anzahl der Bestellungen: k ist app / (web + phone) an den Tagen vor dem 3. September, expected ist, wie viele App-Bestellungen es vom 3. September bis zum 2. Oktober gegeben hätte, actual, wie viele es waren, und lost_pct, welcher Teil der erwarteten verloren ging, in Prozent.

PythonDaten: die Tabellen des Shops

Die Idee

Der Bericht

Eine Analyse endet mit einer Geschichte, die jemand in einer Minute lesen kann. Drei Sätze, ein Diagramm, die Grenzen.

PythonDaten: die Tabellen des Shops
  1. Was: Der Umsatz im September liegt 19,1 % unter dem August, weil die App vom 3. September bis zum 2. Oktober etwa zwei Drittel weniger Bestellungen hatte; die Website und das Telefon sind normal, und ab dem 3. Oktober ist auch die App wieder normal.
  2. Wie viel: Der Verlust beträgt etwa 2.400 € (zwischen 1.800 € und 3.150 €), etwa 240 Bestellungen.
  3. Was tun: Prüft, was sich um den 3. September in der App geändert hat (ein Update, der Warenkorb, die Zahlung), und richtet einen Alarm ein, falls der Anteil eines Kanals fällt.

Die Grenzen. Die Daten sagen, wann und wo: in der App, ab dem 3. September. Warum, sagen sie nicht. Ein App-Update um den 3. September ist eine Hypothese, keine Tatsache: Das App-Team prüft sie mit den Warenkorb-Logs und den Zahlungsfehlern dieses Datums. Es könnte auch etwas anderes sein: der App-Store, ein abgelaufenes Zertifikat, eine Änderung beim Zahlungsanbieter. Das Diagramm kann diese Ursachen nicht unterscheiden. Deshalb sagt der Bericht „prüft“, nicht „das war es“.

Die Werbung, der Lieferant und die Preise sind nicht die Ursache. Keine der drei Ideen des Chefs hätte den Umsatz zurückgebracht.

1. Welcher Satz gehört in den Bericht?

Übung

Ein Alarm für das nächste Mal

Der Einbruch dauerte einen Monat, weil niemand den Kanal beobachtet hat. Eine Alarmregel soll den Einbruch schnell erkennen und in normalen Zeiten still bleiben. Einen Alarm, der jede Woche losgeht, beachtet niemand.

Baue den Alarm. week ist die Anzahl der Bestellungen pro Kanal über die letzten 7 Tage (rolling(7).sum()), app_share der Anteil von app daran, und alerts die Tage, an denen app_share unter 0.25 liegt. Wie viele Alarme gibt es vor dem 3. September, und wann ist der erste?

PythonDaten: die Tabellen des Shops

Die Idee

Wie es weitergeht

Der Kurs endet hier, und die Arbeit mit Daten geht weiter. Wohin:

  • Notebooks. Mach dieses Projekt in JupyterLab auf deinem eigenen Computer: Zelle für Zelle, mit der Ausgabe darunter. Das Notebook ist zugleich der Bericht: Code, Diagramme und Text an einem Ort.
  • Größere Daten. pandas hält alles im Speicher und arbeitet auf einem Kern. Polars hat dieselben Ideen (Gruppen, Joins, Daten), nutzt aber alle Kerne und liest Dateien, die größer sind als der Speicher: pl.read_csv('year_orders.csv').group_by('channel').len() zählt die Bestellungen nach Kanal.
  • Dashboards. Der Alarm aus der Übung gehört am besten auf ein Dashboard, das sich von selbst aktualisiert. Streamlit und Dash machen aus einem Python-Skript ein Web-Dashboard mit Diagrammen und Filtern.
  • Diagramme. seaborn und plotly sind Schichten über den Ideen von matplotlib: seaborn liefert statistische Diagramme in einer Zeile, plotly interaktive mit Zoom und Tooltips.
  • Datenbanken. In einem echten Shop liegen die Bestellungen in einer Datenbank, nicht in einer CSV. pd.read_sql liest das Ergebnis einer SQL-Abfrage direkt in einen DataFrame. SQL lernst du im Kurs Datenbanken und SQL.

Der Weg bleibt derselbe: die Frage, die Daten, das Bereinigen, das Erkunden, der Schluss mit seinen Grenzen und die Geschichte.

Test

Teste dich

1. Warum steht am Anfang des Projekts die Frage „Was entscheiden wir mit der Antwort?“
2. Die Summe des Umsatzes an Mittwochen liegt von August zu September bei +18,0 %, und der durchschnittliche Umsatz an einem Mittwoch bei −5,6 %. Welcher traust du?
3. Warum sagt das Modell die App aus Web und Telefon voraus, statt einfach anzunehmen, der September hätte wie der August sein sollen?
4. Der Verlust beträgt 2.420,81 €, mit einem 95-%-Intervall von 1.802,46 € bis 3.142,14 €. Wie sagst du es im Bericht?
5. Der Alarm lautet: „Der Anteil der App an den Bestellungen der letzten 7 Tage liegt unter 0,25.“ Warum 7 Tage und nicht jeder Tag für sich?

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