Series und DataFrame
Kurz gesagt: In pandas ist eine Series eine Spalte mit Beschriftungen und ein DataFrame eine Tabelle aus solchen Spalten; der Index trägt die Namen der Zeilen und richtet die Rechnung nach ihnen aus.
Problem
„Wie viel Geld liegt im Lager?“
Der Chef fragt, was der Bestand im Lager wert ist. Die Antwort ist Preis mal Bestand für jedes Produkt, dann eine Summe. Als Nächstes will er die Zahl für ein Produkt sehen, mit Namen.
Mit NumPy kannst du das schon rechnen. Aber in der Lektion über Zusammenfassungen lieferte argmax 22, eine Position, und den Namen musstest du aus einem getrennten Array names holen. Sortierst du das eine Array und vergisst das andere, laufen Namen und Zahlen auseinander, und niemand merkt es.
pandas hält neben jeder Zahl eine Beschriftung: welches Produkt es ist. In dieser Lektion siehst du, wie.
Die Idee
Eine Spalte mit Beschriftungen: Series
Eine Series ist ein NumPy-Array mit einer Beschriftung neben jedem Wert. Die Beschriftungen heißen Index. Hier sind drei Preise mit den Produktnamen als Index:
prices['Weißkäse'] liefert 7.9: Der Wert wird nach Beschriftung genommen, nicht nach Platz. prices.index zeigt die Beschriftungen, und to_numpy() liefert die nackten Zahlen als Array.
Die Idee
Eine Tabelle aus Spalten: DataFrame
Ein DataFrame sind mehrere Series mit einem gemeinsamen Index, nebeneinander. Jede Spalte hat einen Namen und einen eigenen Typ. Im ersten Blick hast du ihn gesehen, jetzt siehst du ihn Stück für Stück:
columnssind die Namen der Spalten: 6 davon.indexist einRangeIndexvon 0 bis 24:read_csvnummeriert die Zeilen ab 0, wenn du nichts anderes sagst. Die Spalteidist etwas anderes und beginnt bei 1.dtypesist der Typ jeder Spalte.int64undfloat64sind Zahlen,strist Text.
Sieh es dir an
Eine Spalte ist eine Series
Du nimmst eine Spalte mit ihrem Namen in eckigen Klammern, wie im ersten Blick. Das Ergebnis ist eine Series:
Links steht der Index der Tabelle (0, 1, 2), und unten der Name der Spalte (Name: price) und ihr Typ. Alles, was du mit einer Series tun kannst, kannst du mit jeder Spalte tun.
Die Idee
Namen im Index: set_index
Die Zahlen 0 bis 24 sagen dem Chef nichts. set_index('name') macht die Spalte name zum Index: Die Produktnamen werden zu den Beschriftungen der Zeilen.
Jetzt ist der Bestand an Kaffee by_name['stock']['Kaffeebohnen']: 15 Packungen. Und das teuerste Produkt ist eine Antwort mit Namen:
argmax()liefert weiterhin die Position: 22.idxmax()liefert die Beschriftung des größten Werts: „Messerset“. Es gibt auchidxmin().
Die Beschriftungen wandern mit den Zahlen: Was immer du mit einer Series machst, der Name bleibt neben dem Preis.
Die Idee
Und zurück: reset_index
set_index verändert products nicht, es liefert eine neue Tabelle. Die Spalte name ist jetzt ihr Index, also sind es 5 Spalten statt 6. reset_index() macht den Index wieder zu einer gewöhnlichen Spalte und nummeriert die Zeilen wieder ab 0:
Was in den Index kommt, hängt von der Frage ab. Für „nach Namen“ legst du die Namen dorthin; für eine gewöhnliche Tabelle, die du in eine Datei schreibst, machst du sie wieder zu einer Spalte.
Probier es aus
Das billigste Produkt
Welches Produkt ist das billigste? Lege die Namen in den Index (by_name) und lege in cheapest den Namen des Produkts mit dem kleinsten Preis ab.
Lösung
import pandas as pd
products = pd.read_csv('products.csv')
by_name = products.set_index('name')
cheapest = by_name['price'].idxmin()
print(cheapest)by_name = products.set_index('name') und cheapest = by_name['price'].idxmin() liefert „Butterbrötchen“ (0,80 €). argmin() würde 2 liefern: eine Position, zu der du den Namen noch nachschlagen musst. Ohne set_index liefert idxmin() ebenfalls 2, weil die Beschriftungen dann die Zeilennummern sind.
Sieh es dir an
Der Index richtet die Rechnung aus
Der Lieferant schickt eine neue Preisliste. Die Produkte sind dieselben, aber in anderer Reihenfolge. Mit NumPy-Arrays würdest du das Erste vom Ersten abziehen und das Zweite vom Zweiten. Mit Series zieht pandas die Werte mit der gleichen Beschriftung ab, wo immer sie stehen:
Bei den Series ist der Kaffee um 1 € gestiegen, das Brot um 0,10 € und der Käse um 0,50 €. Bei den Arrays ist die erste Zahl 13,70 €: der neue Preis des Kaffees minus der alte Preis des Brotes. Die letzte ist -12,30 €: Milch minus Kaffee. Die Zahlen sehen völlig seriös aus, und sie sind falsch.
Das nennt man Ausrichtung am Index. Das Ergebnis ist nach Beschriftung geordnet, alphabetisch.
Sieh es dir an
Eine Beschriftung, die fehlt
Steht eine Beschriftung nur in einer der Series, hat pandas nichts, womit es rechnen kann:
Weißkäse steht nur in dieser Woche, also ist seine Differenz NaN. Das ist ehrlich: Wir wissen nicht, wie viel Käse wir in der letzten Woche verkauft haben. In den Zusammenfassungen hast du gesehen, dass fehlend nicht null ist, und pandas hält sich daran.
Genauer, für Neugierige
Wenn du weißt, dass fehlend null bedeutet (ein Produkt, das wir letzte Woche nicht verkauft haben), sag es ausdrücklich: this_week.sub(last_week, fill_value=0). So ist die Entscheidung im Code sichtbar und nicht versteckt. Die Ausrichtung gilt für +, -, *, / und für Vergleiche zwischen zwei Series.
Probier es aus
Was teurer geworden ist
Die beiden Preislisten haben eine andere Reihenfolge. Lege in change ab, wie sich der Preis jedes Produkts geändert hat (neu minus alt), auf 2 Stellen gerundet, mit den Produktnamen im Index.
Lösung
import pandas as pd
old_prices = pd.Series([1.2, 1.6, 7.9, 13.9], index=['Weißbrot', 'Frischmilch', 'Weißkäse', 'Kaffeebohnen'])
new_prices = pd.Series([14.9, 1.3, 8.4, 1.6], index=['Kaffeebohnen', 'Weißbrot', 'Weißkäse', 'Frischmilch'])
change = (new_prices - old_prices).round(2)
print(change)change = (new_prices - old_prices).round(2): pandas findet jedes Produkt in beiden Listen. Kaffee ist +1,00 €, Käse +0,50 €, Brot +0,10 €, und die Milch ist unverändert. Mit to_numpy() werden die Zahlen nach Platz abgezogen: Die erste ist 13,70 €, neuer Kaffee minus altes Brot.
Übung
Der Bestand im Lager
Beantworte die Frage des Chefs. Lege die Produktnamen in den Index. Lege in stock_value den Wert des Bestands jedes Produkts ab (Preis mal Bestand), in total seine Summe und in real_total die Summe ohne die Geschenkkarte, auf 2 Stellen gerundet.
Lösung
import pandas as pd
products = pd.read_csv('products.csv')
by_name = products.set_index('name')
stock_value = by_name['price'] * by_name['stock']
total = stock_value.sum()
real_total = round(total - stock_value['Geschenkkarte'], 2)
print(round(total, 2), real_total)stock_value = by_name['price'] * by_name['stock'] ist eine Series mit den Namen im Index. total ist 22.542,30 €, aber 19.980 € davon sind die Geschenkkarte: Bestand 999 zu 20 €. Aus dem ersten Blick weißt du, dass 999 „unbegrenzt“ bedeutet und nicht 999 Karten im Regal. real_total = total - stock_value['Geschenkkarte'] liefert 2.562,30 €: Das ist der Bestand. Mit den Namen im Index nimmst du die Karte nach Namen heraus und nicht über die Nummer 24, die sich verschiebt, sobald die Tabelle anders sortiert wird.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Die erste Tabelle · Das NumPy-Array · Vektorisierung · Zusammenfassungen und fehlende Werte
- Geht weiter in: Auswählen: [], loc und iloc · Filtern und Sortieren · Neue Spalten · Typen und fehlende Werte
- Aufgaben zum Thema: Tabellen mit pandas