Zum Inhalt
wannacode

Filtern und Sortieren

Kurz gesagt: Filtern in pandas behält nur die Zeilen, in denen eine Maske True ist (oder isin, between, query), und sort_values ordnet sie nach einer Spalte.

Problem

Die Liste für den Lieferanten

Morgen kommt der Lieferant für Lebensmittel. Der Chef will eine Liste: die Lebensmittel, von denen wir weniger als 20 haben, das Dringendste zuerst. Danach fragt er auch nach den Bestellungen des Jahres: wie viele storniert wurden und wie viele Kunden regelmäßig bei uns kaufen.

PythonDaten: die Tabellen des Shops

25 Produkte kannst du mit dem Auge überblicken. Die Bestellungen des Jahres sind 11.116, und da reichen die Augen nicht. Du brauchst eine Möglichkeit, der Tabelle zu sagen „gib mir nur diese Zeilen“ und „ordne sie so“.

Die Idee

Eine Maske auf einer Tabelle

Die Maske aus NumPy funktioniert auch in pandas. products['stock'] < 20 vergleicht jede Zeile und liefert eine Spalte (eine Series) aus True und False, mit dem Index der Tabelle. In eckige Klammern gesetzt, behält die Maske die Zeilen mit True:

PythonDaten: die Tabellen des Shops

low.sum() zählt die Trues: 9 Produkte haben weniger als 20 auf Lager. Das nennt man einen Filter: eine Tabelle mit nur den Zeilen, für die die Bedingung wahr ist.

Sieh dir links den Index an: 3, 6, 7, 12 … Die Zeilen behalten ihre Beschriftungen aus products, du weißt also immer, woher jede kam. In [] wählt ein Spaltenname eine Spalte, und eine Maske wählt Zeilen.

Die Idee

Zwei Bedingungen: &, | und ~

Unter 20 auf Lager und unter 5 € sind zwei Bedingungen zusammen. Mit and bekommst du dasselbe wie in NumPy:

PythonDaten: die Tabellen des Shops

and will ein einziges True oder False, und die Spalte hat 25. Also schreibst du auch hier & für „und“, | für „oder“ und ~ für „nicht“, mit jeder Bedingung in Klammern:

PythonDaten: die Tabellen des Shops

Unter 20 auf Lager und unter 5 € sind die Banitsa und die Gurken, beide ausverkauft. Unter 1 € oder über 20 € sind das Butterbrötchen (0,80 €), das Wasser (0,90 €), die Bratpfanne (24,90 €) und das Messerset (39 €). Die Geschenkkarte kostet genau 20 € und ist nicht „über 20“.

Probier es aus

Die teureren Milchprodukte

Welche Milchprodukte (Kategorie 4) kosten über 3 €? Lege die Tabelle mit ihnen in pricey_dairy ab.

PythonDaten: die Tabellen des Shops

Die Idee

Aus einer Liste und zwischen zwei Zahlen: isin und between

Lebensmittel sind in vier Kategorien: 3, 4, 5 und 6. Statt vier Bedingungen mit | schreibst du isin, das fragt „steht es in dieser Liste?“:

PythonDaten: die Tabellen des Shops

Es gibt 18 Lebensmittel. Mit ~ bekommst du die anderen 7: die sechs Haushaltsprodukte und die Geschenkkarte. Die Karte hat keine Kategorie (NaN), und NaN steht in keiner Liste, also erfasst sie ~.

Für einen Bereich gibt es between. between(2, 5) heißt „von 2 bis 5 einschließlich“: Beide Enden gehören dazu:

PythonDaten: die Tabellen des Shops

Es liefert 9 Produkte, so viele wie (prices >= 2) & (prices <= 5) in der NumPy-Lektion.

Genauer, für Neugierige

between(2, 5, inclusive='neither') lässt beide Enden weg, und 'left' und 'right' behalten nur eines davon. Standard ist 'both'.

Die Idee

Text, der etwas enthält: str.contains

„Zeig mir alle Brote.“ Unter den Namen sind „Weißbrot“ und „Vollkornbrot“, aber du kannst nicht == 'brot' schreiben: Kein Name ist genau „brot“. str.contains fragt, ob der Text irgendwo im Namen vorkommt:

PythonDaten: die Tabellen des Shops

.str öffnet die Textmethoden für die ganze Spalte. Genauer siehst du es in der Lektion über Typen.

Die Suche unterscheidet Groß- und Kleinbuchstaben. „käse“ findet nur „Weißkäse“, also ist die erste Zahl 1. „Banitsa mit Käse“ hat ein großes K und fällt heraus, und „Kaschkawal“ ist zwar ein Käse, hat das Wort aber nicht im Namen. Mit case=False werden die Buchstaben nicht unterschieden, und du bekommst 2.

Die Idee

Die Bedingung als Text: query

Lange Masken mit überall products['...'] werden schwer lesbar. query nimmt die Bedingung als Text, in dem die Spaltennamen für sich stehen:

PythonDaten: die Tabellen des Shops

In query schreibst du and, or und not. Der Text wird nicht von Python gerechnet, sondern von pandas, und pandas weiß, dass das Spalten sind. Unter 20 auf Lager und über 5 € sind 7 Produkte.

Eine Python-Variable kommt mit @ hinein: category_id in @food ist dasselbe wie isin(food). Das liefert die fünf Lebensmittel mit weniger als 20 auf Lager, aus denen du gleich die Liste für den Lieferanten machst.

1. limit = 10 enthält die Preisgrenze. Welche Zeile nimmt die Produkte darüber?

Die Idee

Sortieren: sort_values und nlargest

sort_values('price') ordnet die Zeilen nach Preis, vom kleinen zum großen. Mit ascending=False ist es umgekehrt. Für „die Top N“ gibt es nlargest(N, Spalte) und nsmallest:

PythonDaten: die Tabellen des Shops

Am teuersten sind das Messerset (39 €), die Bratpfanne (24,90 €) und die Geschenkkarte (20 €). nlargest(3, 'price') liefert dieselben drei in einer Zeile.

Du kannst auch nach mehreren Spalten sortieren: zuerst nach Kategorie, und darin nach Preis. ascending ist dann eine Liste, eine für jede Spalte. Die Kategorien steigen und die Preise darin fallen: Die Banitsa (2,50 €) steht beim Brot an erster Stelle, der Kaschkawal (9,50 €) bei den Milchprodukten.

Genauer, für Neugierige

Das Sortieren verschiebt die Zeilen, ändert aber nicht ihre Beschriftungen: Der Index in der Ausgabe ist 22, 21, 24 … Brauchst du die Nummern 0, 1, 2 in der neuen Reihenfolge, füge .reset_index(drop=True) hinzu.

Die Idee

Wie viele von jedem: value_counts

„Wie viele Bestellungen wurden storniert?“ ist eine Zählfrage. value_counts zählt, wie oft jeder Wert in einer Spalte vorkommt, den häufigsten oben:

PythonDaten: die Tabellen des Shops

Von den 11.116 Bestellungen des Jahres wurden 10.254 geliefert und 657 storniert. Mit normalize=True bekommst du statt der Zahl einen Anteil: 0.059, das heißt 5,9 % der Bestellungen wurden storniert.

Dasselbe mit der Kundennummer sagt dir, wer wie oft bestellt hat:

PythonDaten: die Tabellen des Shops

1.390 Kunden haben bestellt. Der treueste, Kunde 1, hat 121-mal bestellt, und 224 Kunden haben nur einmal bestellt. Der Index des Ergebnisses sind die Kundennummern, und die Werte sind die Zahl der Bestellungen jedes Kunden.

Probier es aus

Die offenen Bestellungen

Bestellungen mit dem Status new, paid oder shipped sind noch nicht beim Kunden angekommen. Lege die Tabelle mit ihnen in open_orders ab.

PythonDaten: die Tabellen des Shops

Übung

Die Liste und die Stammkunden

Mach die Liste für den Lieferanten: die Lebensmittel (Kategorien 3, 4, 5 und 6) mit einem Bestand unter 20, nach Bestand sortiert, vom kleinsten an. Bei gleichem Bestand steht das teurere Produkt zuerst. Lege die Tabelle in restock ab.

PythonDaten: die Tabellen des Shops

Wie viele Kunden haben im Jahr mindestens 20-mal bestellt? Lege ihre Zahl in regulars ab.

PythonDaten: die Tabellen des Shops

Test

Teste dich

1. Warum steht im Filter (products["stock"] < 20) & (products["price"] < 5) jede Bedingung in Klammern?
2. Was liefert products["price"].between(2, 5) für ein Produkt, das genau 5 € kostet?
3. food = [3, 4, 5, 6] enthält die Kategorien der Lebensmittel. Warum erfasst ~products["category_id"].isin(food) auch die Geschenkkarte?
4. year["status"].value_counts() lieferte 5 Zeilen. In welcher Reihenfolge stehen sie?

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