Baza wiedzy / Python

pandas cut: przedziały, granice i brakujące kategorie

Wartość 25 może należeć do pierwszego przedziału, a 0 wypaść z podziału. Pokazuję pełny przykład i wyjaśniam, co dzieje się dokładnie na granicach.

Kajo RudzińskiOpublikowano Aktualizacja 5 min czytania

Trzy przedziały sortowni o różnej szerokości i klocek poza zakresem, żółto-granatowy pixel art.

Najwięcej pytań do cut mam zwykle przy liczbie, która wygląda zupełnie niewinnie: równej granicy. Jeśli tabela ma przedziały do 25, do 50 i do 100, trzeba jeszcze ustalić, gdzie dokładnie trafi 25. Inaczej czytelnik może odczytać etykietę inaczej niż program.

Ja zapisuję regułę przed pokazaniem kodu. Poniższe syntetyczne przykłady opieram na dokumentacji pandas 3.1.1 dostępnej w listopadzie 2026. Tabele pokazują przypisania wynikające z opisanych reguł. Cały zestaw jest mały, żeby każdą granicę dało się prześledzić bez zgadywania.

Stałe granice i domknięcie z prawej

pandas.cut przypisuje jednowymiarowe dane do przedziałów. Dla jawnej listy granic i domyślnego right=True przedziały są otwarte z lewej i domknięte z prawej. Zapis (0, 25] oznacza liczby większe od 0 i nie większe niż 25.

import pandas as pd

scores = pd.Series([5, 15, 25, 35, 45, 55, 65, 75, 85, 95])
edges = [0, 25, 50, 100]

groups = pd.cut(
    scores,
    bins=edges,
    labels=["Low", "Medium", "High"],
    right=True,
)

Mamy cztery granice, więc trzy przedziały. Etykiety są nazwami umownymi, a nie rzeczywistymi ocenami egzaminacyjnymi. Ich znaczenie trzeba opisać odbiorcy, zwłaszcza jeśli „High” ma później wpływać na wybór dalszej analizy.

score przedział etykieta
5 (0, 25] Low
15 (0, 25] Low
25 (0, 25] Low
35 (25, 50] Medium
45 (25, 50] Medium
55 (50, 100] High
65 (50, 100] High
75 (50, 100] High
85 (50, 100] High
95 (50, 100] High

Pierwsza grupa ma trzy obserwacje, druga dwie, trzecia pięć. Wartość 25 należy jeszcze do Low. Nie ma powodu, żeby przenosić ją do następnej grupy tylko dlatego, że w opisie słownym napisaliśmy „od 25”. Opis musi odpowiadać temu, które końce są włączone.

Co zmienia right=False

Przy right=False dla tych samych granic dostaję [0, 25), [25, 50) i [50, 100). Teraz lewa granica jest włączona, prawa wyłączona. Wartość 25 przechodzi do środkowej grupy, a 100 nie mieści się w ostatniej.

left_closed = pd.cut(
    pd.Series([0, 25, 50, 100]),
    bins=edges,
    labels=["Low", "Medium", "High"],
    right=False,
)
wartość domyślne right=True right=False
0 brak Low
25 Low Medium
50 Medium High
100 High brak

W tej tabeli wariant domyślny nie używa include_lowest. To ważne, bo właśnie ten argument pozwala osobno zdecydować o pierwszej dolnej granicy. Nie ustawiałbym right=False tylko po to, żeby złapać zero, bez sprawdzenia skutku dla górnego końca zakresu.

Jak włączyć pierwszą dolną granicę

Dla prawostronnego domknięcia i potrzeby uwzględnienia zera używam include_lowest=True:

including_zero = pd.cut(
    pd.Series([0, 25, 50, 100]),
    bins=edges,
    labels=["Low", "Medium", "High"],
    right=True,
    include_lowest=True,
)

Wtedy 0 i 25 otrzymają Low, 50 Medium, a 100 High. Etykiety ułatwiają opis, bo nie trzeba interpretować drobnej korekty lewej granicy w tekstowej reprezentacji interwału. Reguła biznesowa nadal powinna mówić wprost, że zero jest dopuszczone.

Podobny szczegół pojawia się przy ocenach. Jeśli granice to 0, 50, 70, 90 i 100, przy right=True wynik 90 należy do (70, 90]. Jeżeli tę grupę nazwiesz „dobry”, 90 dostanie właśnie tę etykietę. Najwyższa grupa zaczyna się powyżej 90, a nie od 90 włącznie.

Klocek przy słupku rozdzielającym sąsiednie kanały przedziałów, żółto-granatowy pixel art.

Liczba przedziałów zamiast listy granic

Możesz przekazać bins=4, żeby podzielić zakres danych na cztery części o równej szerokości. To nie gwarantuje równych liczebności. Dla głównego zbioru minimum wynosi 5, maksimum 95, więc podstawowy krok to 22,5.

automatic, automatic_edges = pd.cut(
    scores,
    bins=4,
    retbins=True,
)

Przy domyślnym prawym domknięciu dolny kraniec jest lekko rozszerzony, żeby uwzględnić minimum. Dla tej próbki granice wynoszą 4.91, 27.5, 50, 72.5 i 95. Rozkład jest następujący:

przedział liczba obserwacji
(4.91, 27.5] 3
(27.5, 50] 2
(50, 72.5] 2
(72.5, 95] 3

Maksimum nadal wynosi 95. Nie dopisuję granicy 100 tylko dlatego, że wygląda okrągło. Jawna lista edges z pierwszego przykładu i automatyczny podział to dwie różne reguły. W raporcie porównującym okresy wybrałbym stałe, uzgodnione granice, jeśli kategorie mają zachować znaczenie między miesiącami.

Kiedy potrzebujesz qcut

qcut wyznacza podział według kwantyli, z zamiarem uzyskania grup o zbliżonej liczebności. Szerokość takich przedziałów może być różna. Przy wielu powtórzeniach granice mogą się pokryć, co wymaga decyzji o błędzie albo usunięciu powtarzających się granic.

Nie obiecywałbym więc, że „automatyczny podział” zawsze oznacza równe grupy. Najpierw pytam, czy ważna jest stała wartość progu, szerokość przedziału czy pozycja obserwacji w rozkładzie. To różne cele. Hasło o kwartylach i IQR rozwija kontekst pozycji w uporządkowanych danych.

Brak kategorii wymaga wyjaśnienia

Dla jawnych granic wartości poza zakresem pozostają bez przypisanego przedziału. Brak wejściowy także nie dostaje zwykłej kategorii. Dokumentacja braków opisuje ich wykrywanie przez isna. W analizie rozdzieliłbym liczbę nieznaną od liczby znanej, ale niedopuszczonej przez progi.

boundary_scores = pd.Series([0, 25, 100, 105, None])
boundary_groups = pd.cut(
    boundary_scores,
    bins=edges,
    labels=["Low", "Medium", "High"],
    include_lowest=True,
)

Pierwsze trzy przypisania to Low, Low i High. Dla 105 oraz brakującej wartości wynik klasyfikacji jest brakujący, lecz przyczyny są różne. Jeśli automatycznie nazwiesz oba przypadki Low, stracisz informację o błędzie zakresu i o kompletności danych.

Przy kontroli liczebności pomocne jest boundary_groups.value_counts(dropna=False). Opis value_counts wyjaśnia, że domyślnie braki są pomijane. Chcę je zobaczyć, żeby suma sklasyfikowanych obserwacji nie udawała pełnego wejścia.

Etykiety, precyzja i powtarzające się granice

Liczba etykiet musi odpowiadać liczbie wynikowych przedziałów. labels=False zwraca ich numery zamiast nazw; takie numery są kodami grup, a nie oryginalnymi wynikami. Nie liczyłbym ich średniej tak, jakby różnica między kodem 0 i 1 miała tę samą jednostkę co dane wejściowe.

precision dotyczy reprezentacji etykiet granic. Nie służy do zaokrąglania wyników egzaminu przed klasyfikacją. Jeśli potrzebujesz wcześniej zmienić precyzję danych, potraktuj to jako osobną, jawną regułę. W przeciwnym razie liczba tuż pod progiem może zostać zinterpretowana inaczej niż zakłada odbiorca.

Powtórzone granice domyślnie powodują błąd. duplicates="drop" może zmniejszyć liczbę przedziałów, więc nie jest neutralnym sposobem wyciszenia problemu. Po zmianie trzeba ponownie uzgodnić etykiety i znaczenie grup. Ja wolałbym zauważyć błędną konfigurację przed publikacją niż dostać pozornie kompletną tabelę o innym podziale.

Ćwiczenie przed użyciem w raporcie

Dla granic 0, 25, 50, 100 wypisz najpierw przypisania 0, 24, 25, 26, 100 i 101. Zrób to dla prawego domknięcia z włączonym zerem. Oczekuję kolejno Low, Low, Low, Medium, High i braku. Potem zmień domknięcie i wyjaśnij szczególnie 25 oraz 100.

Gdy reguła jest jasna, możesz podsumować grupy przez pandas crosstab. W opisie raportu zachowaj granice i sposób obsługi braków. Sama nazwa „wysoki wynik” nie wystarczy komuś, kto ma później podjąć decyzję.

Najważniejsze w pigułce

Do którego przedziału trafia wartość równa granicy?

Przy domyślnym right=True przedziały są domknięte z prawej, więc dla granic 0, 25, 50, 100 wartość 25 trafia do (0, 25]. Przy right=False granica 25 należy do [25, 50).

Czy bins=4 tworzy cztery grupy o tej samej liczebności?

Nie. Tworzy przedziały o równej szerokości z uwzględnieniem rozszerzenia skrajnej granicy. Do podziału według kwantyli służy qcut, którego wynik może wymagać dodatkowej decyzji przy wielu powtarzających się wartościach.

Dlaczego po cut pojawia się brak kategorii?

Przyczyną może być brak wartości wejściowej, liczba poza podanymi granicami albo wykluczony kraniec przedziału. Sprawdź te przypadki osobno; brak przypisania nie oznacza automatycznie najniższej kategorii.

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.