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.

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.

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.
