Baza wiedzy / Python

pandas.Series: tworzenie, indeks i odczyt danych

Wyjaśniam różnicę między etykietą a pozycją, pokazuję wyrównanie dwóch serii i wpływ braków na sumę. Poprawiam dawny przykład s[0] dla indeksu tekstowego.

Kajo RudzińskiOpublikowano Aktualizacja 5 min czytania

Trzy pojemniki na jednej szynie z osobnymi oznaczeniami geometrycznymi i selektorem

Gdy patrzę na pojedynczą kolumnę danych, interesują mnie wartości i informacja, do czego należą. Liczba dziesięć może oznaczać stan produktu, wynik pomiaru albo liczbę zamówień. pandas.Series przechowuje jednowymiarowe dane razem z indeksem, który pozwala odwołać się do konkretnych elementów. Przed obliczeniem sprawdzam oba te składniki. Sam wygląd liczb nie powie mi, czy porównuję właściwe obserwacje.

W tym haśle używam małych danych syntetycznych. Przykłady dotyczą pandas 3.x i opierają się na dokumentacji, bez deklaracji ich uruchomienia. Skupiam się na tworzeniu, wyborze i prostych działaniach. Pełne zestawienie metod znajdziesz w dokumentacji Series. Najważniejsza poprawka względem dawnego tekstu dotyczy rozróżnienia etykiety i pozycji przy odczycie elementu.

Utworzenie Series z listy i słownika

Zaczynam od trzech wartości. Nadaję im etykiety produktów i jawny typ Int64, czyli typ całkowity pandas obsługujący brakujące wartości. Wielka litera w nazwie typu ma znaczenie. Dzięki jawnemu wyborowi nie muszę opierać wyjaśnienia na automatycznym wnioskowaniu typu po zmianie danych.

import pandas as pd

stock = pd.Series(
    [10, 20, 30],
    index=['x', 'y', 'z'],
    dtype='Int64',
    name='units',
)
Etykieta Wartość
x 10
y 20
z 30

Lista wartości i podany indeks mają taką samą długość. Bez własnego indeksu pandas tworzy domyślny RangeIndex, zaczynający się od zera. Etykiety takiego indeksu mogą wyglądać jak pozycje, co łatwo utrwala błędny nawyk. Własne oznaczenia produktów pomagają zobaczyć różnicę. Parametr name opisuje serię; nie zmienia sposobu przypisania etykiet do wartości.

Można też podać słownik:

from_dict = pd.Series({'x': 10, 'y': 20, 'z': 30}, dtype='Int64')

Klucze słownika stają się indeksem, a wartości danymi. Gdy dodatkowo podajesz index, konstruktor wybiera dane według wskazanych etykiet. Przy nowym przykładzie najpierw ustalam, czy indeks ma identyfikować produkt, datę czy po prostu kolejność. Ta decyzja wpływa później na wybieranie i wyrównywanie danych.

loc wybiera etykietę, iloc wybiera pozycję

Dokumentacja loc opisuje wybór po etykietach. Dokumentacja iloc dotyczy pozycji całkowitych. W naszym przykładzie etykieta y znajduje się na drugiej pozycji, której numer wynosi jeden, ponieważ pozycje liczymy od zera.

stock.loc['y']     # 20
stock.iloc[0]      # 10
stock.iloc[1]      # 20
stock.loc[['z', 'x']]

Ostatni wybór daje serię z etykietami z, x i wartościami odpowiednio 30 oraz 10. Kolejność wynika z przekazanej listy etykiet. Brak żądanej etykiety w takim wyborze prowadzi do KeyError. Przy pojedynczej pozycji wykraczającej poza zakres iloc zgłasza IndexError. Wycinki mają własne zasady, dlatego nie przenoszę tego opisu automatycznie na każdy rodzaj selekcji.

Dlaczego dawny przykład s[0] jest błędny

Dla naszego stock wyrażenie stock[0] szuka etykiety zero. Taka etykieta nie występuje, więc w pandas 3.x otrzymasz KeyError. Zmiana od pandas 3.0 ujednoliciła traktowanie kluczy całkowitych jako etykiet. Dawny odczyt pierwszej wartości przez ten zapis przy indeksie tekstowym nie jest aktualnym wzorcem.

Ja zapisuję intencję wprost: loc dla etykiety, iloc dla pozycji. Jeżeli indeks zawiera liczby, ta zasada nadal obowiązuje. Etykieta dziesięć nie znaczy dziesiątej pozycji. Przy zmianie kolejności danych odczyt pozycyjny może wskazywać inny produkt, choć etykieta produktu pozostanie taka sama. Warto wybrać metodę według znaczenia zadania, a nie wygody krótszego zapisu.

Indeks może zawierać powtórzenia

Indeks Series nie musi być unikalny. Dopuszczalny jest na przykład taki obiekt:

repeated = pd.Series([10, 20], index=['x', 'x'], dtype='Int64')
repeated.loc['x']

Wybór zwraca obie wartości z etykietą x. To ważna różnica względem oczekiwania, że każda etykieta daje pojedynczą liczbę. Dawne zdanie o obowiązkowo unikalnym indeksie było błędne. Jeśli w Twoich danych indeks powinien identyfikować jeden produkt, powtórzenie wymaga wyjaśnienia: może oznaczać błąd albo kilka obserwacji tego samego produktu.

Nie usuwam duplikatów tylko po to, żeby odczyt stał się wygodniejszy. Najpierw ustalam znaczenie obserwacji. Dwie wartości mogą dotyczyć różnych magazynów albo różnych dni. Wtedy właściwe może być przechowywanie dodatkowego klucza lub jawne agregowanie. Samo nadanie etykiety nie tworzy gwarancji poprawności modelu danych.

Pojemniki na dwóch szynach są połączone według pasujących oznaczeń geometrycznych

Filtrowanie zachowuje etykiety

Warunek porównania tworzy maskę, którą można zastosować do tej samej serii:

selected = stock.loc[stock > 15]

Dla danych wejściowych zostają y z wartością 20 oraz z z wartością 30. Ich etykiety nie zamieniają się automatycznie na zero i jeden. Jeśli po filtrowaniu potrzebuję pierwszej pozostałej wartości, wybieram selected.iloc[0]. Wybór selected.loc['y'] nadal odnosi się do konkretnego produktu, niezależnie od tego, gdzie znajduje się on w kolejności.

Przy masce pochodzącej z innego obiektu trzeba dodatkowo rozumieć dopasowanie indeksu. W tym przykładzie tworzę warunek bezpośrednio z stock, więc nie dokładam problemu cudzej kolejności. Kiedy wynik filtra wygląda podejrzanie, sprawdzam także etykiety i braki, zamiast patrzeć wyłącznie na liczbę zwróconych elementów.

Dodawanie wyrównuje dane po indeksie

Druga seria ma etykiety w odwrotnej kolejności:

extra = pd.Series([7, 5], index=['z', 'y'], dtype='Int64')
total = stock + extra
Etykieta stock extra total
x 10 brak brak
y 20 5 25
z 30 7 37

Pandas dopasowuje y do y i z do z. Nie dodaje pierwszej wartości jednej serii do pierwszej wartości drugiej. Dla x brakuje składnika po prawej, więc zwykłe dodawanie daje brak. Przy wybranym typie całkowitym obsługującym braki reprezentuje go pd.NA. Tabela pokazuje znaczenie wyniku, bez uzależniania wyjaśnienia od formatowania wydruku.

Metoda Series.add pozwala podać fill_value=0. W tym przykładzie stock.add(extra, fill_value=0) da 10 dla x, 25 dla y i 37 dla z. Stosuję ten wariant dopiero po ustaleniu, że brak dodatkowej wartości naprawdę oznacza zero. Brak informacji o dostawie i potwierdzony brak dostawy mogą wymagać różnych interpretacji.

Brak wartości a suma

Zastąpmy środkową obserwację brakiem:

incomplete = pd.Series([10, pd.NA, 30], dtype='Int64')
incomplete.isna()
incomplete.sum()
incomplete.sum(min_count=3)

Maska isna() wskazuje brak na środkowej pozycji. Domyślna suma pomija brakujące dane i daje 40. Ostatnie wywołanie wymaga trzech niebrakujących obserwacji, a mamy dwie, więc wynikiem jest brak. Zachowanie skipna i min_count opisuje dokumentacja sum.

W raporcie chcę wiedzieć, czy 40 jest pełnym wynikiem, czy sumą znanej części. Sama poprawna arytmetyka tego nie rozstrzyga. Ustalam wymaganą kompletność i pokazuję ją razem z wynikiem albo wymuszam odpowiedni próg. Nie zamieniam braków na zero bez decyzji o ich znaczeniu.

Ćwiczenie i następny krok

Zmień kolejność etykiet extra, zachowując przypisane im wartości. Przed sięgnięciem po narzędzie przewidź, czy zmieni się wynik dodawania dla y. Potem rozważ inną etykietę, której nie ma w stock: jaki brak pojawi się po wyrównaniu? Celem ćwiczenia jest rozdzielenie kolejności od tożsamości obserwacji. Nie trzeba do tego dużego zbioru danych.

Gdy przechodzisz od jednej serii do tabeli, zajrzyj do hasła o DataFrame. Łączenie danych po kluczach opisuję przy pandas.merge. W obu przypadkach wraca to samo pytanie: co dokładnie identyfikuje obserwację i czy wynik zachowuje jej znaczenie?

Najważniejsze w pigułce

Czy s[0] zawsze zwraca pierwszy element Series?

Nie. Od pandas 3.0 całkowity klucz w Series jest etykietą. Pierwszą pozycję wybierasz przez s.iloc[0], a etykietę 0 przez s.loc[0], jeśli istnieje.

Czy indeks Series musi być unikalny?

Nie. Powtórzone etykiety są dozwolone. Odczyt loc dla takiej etykiety może zwrócić kilka wartości, więc nie zakładaj automatycznie wyniku skalarnego.

Dlaczego dodawanie dwóch serii daje braki?

Dane są wyrównywane po etykietach indeksu. Jeśli etykieta występuje tylko po jednej stronie, zwykłe dodawanie daje brak wyniku dla tej etykiety.

Przy pracy nad artykułem użyto w zautomatyzowany sposób sztucznej inteligencji. Jeśli też chcesz tak umieć, sprawdź ten projekt.

Kajo Rudziński

Newsletter KajoData

Zostańmy w kontakcie.

Piszę o analizie danych, uczeniu się i zmianie pracy. Dzielę się tym, co pomaga mi ogarniać technologię i zawodowe decyzje. Jeśli lubisz ten sposób myślenia, zapraszam do mojego newslettera.