Rangi i percentyle w grupach
Obliczaj rangi w obrębie grup za pomocą groupby().rank() i twórz przedziały percentylowe za pomocą pd.qcut na potrzeby porównań względnych.
Rangi i percentyle w grupach to bezpłatna lekcja Pandas & NumPy Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Pandas & NumPy Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Pandas & NumPy Academy zawiera 4 lekcji w sumie.
Dlaczego obliczać rangi w grupach?
Surowe wartości często mają mniejsze znaczenie niż rankingi względne. Przedstawiciel handlowy z miesięcznym przychodem w wysokości 50 000 USD jest świetnym pracownikiem, jeśli średnia wynosi 30 000 USD, ale słabym, jeśli średnia wynosi 80 000 USD. Obliczając rangi w grupach (np. rangę w każdym regionie lub kwartale), otrzymują Państwo znormalizowane porównanie uwzględniające różne wartości bazowe w grupach. Pandas ułatwia obliczanie rang w grupach za pomocą groupby().rank().
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'rep': ['Alice', 'Bob', 'Carol', 'Dave', 'Eve',
'Frank', 'Grace', 'Hank', 'Iris', 'Jake'],
'region': ['East']*5 + ['West']*5,
'sales': np.random.randint(30000, 100000, 10)
})
print(df.sort_values('region'))Series.rank() — podstawowe obliczanie rang
Series.rank() przypisuje rangę każdej wartości: ranga 1 oznacza najmniejszą wartość, a ranga n — największą. Parametr ascending=False odwraca kierunek, więc ranga 1 oznacza największą wartość. Wynikiem jest seria typu float, a nie integer, ponieważ remisy są domyślnie rozstrzygane przez uśrednienie rang remisujących wartości. Dla kolumny zawierającej 5 wartości rangi mieszczą się w zakresie od 1.0 do 5.0, ale przy remisach niektóre wartości mogą mieć wspólną rangę, np. 2.5.
import pandas as pd
s = pd.Series([80, 45, 90, 45, 70])
print('Values:', s.values)
print('Rank (ascending=True, default):', s.rank().values)
print('Rank (ascending=False — best=1):', s.rank(ascending=False).values)
# Note: two 45s share ranks 1 and 2 → both get 1.5Metody rozstrzygania remisów w rank()
Parametr method określa sposób obsługi remisujących wartości. Dostępne opcje to: 'average' (domyślna — remisujące wartości otrzymują średnią swoich rang), 'min' (wszystkie otrzymują najniższą rangę), 'max' (wszystkie otrzymują najwyższą rangę), 'first' (rangi są przydzielane według kolejności występowania — bez remisów) oraz 'dense' (bez luk w rangach — 1, 2, 3, 3, 4 pozostaje 1, 2, 3, 3, 4 zamiast 1, 2, 3, 3, 5). Metoda 'dense' jest najbardziej przydatna w rankingach typu percentylowego.
import pandas as pd
s = pd.Series([80, 45, 90, 45, 70])
result = pd.DataFrame({
'value': s,
'average': s.rank(method='average'),
'min': s.rank(method='min'),
'max': s.rank(method='max'),
'first': s.rank(method='first'),
'dense': s.rank(method='dense')
})
print(result)Obliczanie rang w grupach za pomocą groupby().rank()
groupby('group_col')['value_col'].rank() oblicza rangi niezależnie w każdej grupie. Ranga jest resetowana na początku każdej grupy — najlepszy pracownik w regionie East otrzymuje więc rangę 1 w East, a najlepszy pracownik w regionie West również rangę 1 w West. Dzięki temu groupby rank jest tak przydatne do sprawiedliwych porównań między grupami.
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'rep': list('ABCDEABCDE'),
'region': ['East']*5 + ['West']*5,
'sales': np.random.randint(30000, 100000, 10)
})
# Rank within each region (best = rank 1)
df['rank_in_region'] = df.groupby('region')['sales'].rank(ascending=False, method='min')
df_sorted = df.sort_values(['region', 'rank_in_region'])
print(df_sorted.to_string(index=False))Ranking percentylowy za pomocą rank(pct=True)
Ustawienie pct=True w rank() zwraca rangę percentylową: wartość od 0 do 1 określającą, jaka część wartości w grupie jest mniejsza lub równa bieżącej wartości. Ranga percentylowa 0.8 oznacza 80. percentyl — wartość jest większa niż 80% wszystkich wartości. Taka normalizacja umożliwia bezpośrednie porównywanie wartości z grup o różnej liczebności bez znajomości ich rzeczywistych rozmiarów.
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'name': list('ABCDEFGHIJ'),
'region': ['East']*5 + ['West']*5,
'score': np.random.randint(50, 100, 10)
})
# Percentile rank within region
df['pct_rank'] = df.groupby('region')['score'].rank(pct=True)
df['percentile'] = (df['pct_rank'] * 100).round(0).astype(int)
print(df.sort_values(['region', 'percentile'], ascending=[True, False]).to_string(index=False))pd.qcut: przedziały oparte na kwantylach
pd.qcut(series, q) dzieli wartości na q przedziałów o jednakowej częstości, tak aby każdy przedział zawierał w przybliżeniu taką samą liczbę obserwacji. W przeciwieństwie do pd.cut, które stosuje przedziały o jednakowej szerokości, pd.qcut dopasowuje granice przedziałów do rozkładu danych. Doskonale nadaje się to do tworzenia kwartylów (q=4), kwintyli (q=5) lub decyli (q=10) na potrzeby poziomów wyników.
import pandas as pd
import numpy as np
np.random.seed(42)
sales = pd.Series(np.random.exponential(scale=50000, size=100))
# Divide into quartiles
quartiles = pd.qcut(sales, q=4, labels=['Q1', 'Q2', 'Q3', 'Q4'])
result = pd.DataFrame({'sales': sales, 'quartile': quartiles})
print('Quartile counts (should be ~25 each):')
print(result['quartile'].value_counts().sort_index())
print('\nMean sales per quartile:')
print(result.groupby('quartile')['sales'].mean().round(0))pd.cut a pd.qcut
pd.cut(series, bins=n) tworzy przedziały o jednakowej szerokości (ten sam zakres, różna liczba obserwacji). pd.qcut(series, q=n) tworzy przedziały o jednakowej częstości (ta sama liczba obserwacji, różne zakresy). W przypadku danych o skośnym rozkładzie pd.cut tworzy niemal puste przedziały na krańcach, podczas gdy pd.qcut rozdziela obserwacje równomiernie. Należy wybrać pd.cut, gdy granice przedziałów mają naturalne znaczenie biznesowe (przedziały cenowe, grupy wiekowe), a pd.qcut — dla poziomów wyników, gdy potrzebne są grupy o takiej samej liczebności.
import pandas as pd
import numpy as np
np.random.seed(0)
# Right-skewed data
data = pd.Series(np.random.exponential(1, 100))
cut_result = pd.cut(data, bins=5).value_counts().sort_index()
qcut_result = pd.qcut(data, q=5).value_counts().sort_index()
print('Equal-width bins (pd.cut) — uneven counts:')
print(cut_result.to_string())
print('\nEqual-frequency bins (pd.qcut) — even counts:')
print(qcut_result.to_string())Tworzenie etykiet decyli za pomocą pd.qcut
pd.qcut(series, q=10, labels=range(1,11)) przypisuje każdą obserwację do odpowiedniego decyla (od 1 do 10). Jest to standardowa technika stosowana w analityce marketingowej (decyle wartości klienta), ocenie zdolności kredytowej (decyle ryzyka) oraz testach AB (decyle ruchu na potrzeby analizy kohortowej). Parametr labels może przyjmować dowolną listę o długości równej q — można użyć ciągów znaków, takich jak ['Bottom 10%', ..., 'Top 10%'], aby uzyskać czytelniejszy wynik.
import pandas as pd
import numpy as np
np.random.seed(7)
customer_value = pd.Series(np.random.exponential(500, 1000))
# Assign to deciles 1-10
decile_labels = [f'D{i}' for i in range(1, 11)]
customer_decile = pd.qcut(
customer_value,
q=10,
labels=decile_labels
)
result = pd.DataFrame({'value': customer_value, 'decile': customer_decile})
print('Mean customer value per decile:')
print(result.groupby('decile')['value'].mean().round(0))Przedziały percentylowe w grupach
Połącz groupby z pd.qcut za pomocą transform, aby utworzyć przedziały percentylowe w każdej grupie. Jest to przydatne, gdy chcą Państwo uszeregować klientów w każdym regionie, a nie globalnie — klient znajdujący się w najniższym globalnym decylu może należeć do najwyższego decyla w swoim regionie. Należy użyć groupby('group')['value'].transform(lambda x: pd.qcut(x, q=4, labels=['Q1','Q2','Q3','Q4'])).
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'customer': range(20),
'region': ['North']*10 + ['South']*10,
'spend': np.random.randint(100, 1000, 20)
})
# Quartile within each region
def quartile_label(x):
return pd.qcut(x, q=4, labels=['Q1','Q2','Q3','Q4'])
df['region_quartile'] = df.groupby('region')['spend'].transform(quartile_label)
print(df.sort_values(['region', 'region_quartile']).to_string(index=False))Top-N w grupach
Częste pytanie biznesowe brzmi: „kto jest 3 najlepszymi pracownikami w każdym regionie?”. Należy użyć groupby().rank(), a następnie odfiltrować wyniki według rangi: df[df['rank_col'] <= 3]. Działa to poprawnie dla grup o dowolnej liczebności i dobrze obsługuje remisy, zachowując więcej niż 3 wiersze, jeśli na granicy występują takie same rangi. Alternatywnie można użyć groupby().nlargest(n), które bezpośrednio zwraca n największych wartości w każdej grupie, bez dodawania kolumny z rangą.
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'rep': [f'Rep_{i}' for i in range(15)],
'region': ['East']*5 + ['West']*5 + ['North']*5,
'revenue': np.random.randint(40000, 120000, 15)
})
df['region_rank'] = df.groupby('region')['revenue'].rank(ascending=False, method='min')
print('Top 2 per region:')
top2 = df[df['region_rank'] <= 2].sort_values(['region', 'region_rank'])
print(top2[['rep', 'region', 'revenue', 'region_rank']].to_string(index=False))Łączenie rang i transformacji w celu normalizacji
Mogą Państwo użyć groupby().rank(pct=True) w połączeniu z transform, aby dodać kolumnę z rangą percentylową do oryginalnego DataFrame. Taka znormalizowana kolumna jest często bardziej użyteczna jako cecha modelu niż surowa wartość — nie zależy od skali i umożliwia porównania między grupami. W uczeniu maszynowym rangi percentylowe są prostą alternatywą dla standaryzacji (z-score), odporniejszą na wartości odstające.
import pandas as pd
import numpy as np
np.random.seed(1)
df = pd.DataFrame({
'product': np.random.choice(['A', 'B', 'C'], 30),
'score': np.random.randint(50, 100, 30)
})
# Percentile rank within each product group
df['global_pct'] = df['score'].rank(pct=True).round(3)
df['group_pct'] = df.groupby('product')['score'].rank(pct=True).round(3)
print(df.sort_values(['product', 'score']).head(12).to_string(index=False))Szybkie sprawdzenie
Sprawdź swoją wiedzę na temat operacji na rangach i percentylach z tego modułu.
Podsumowanie modułu
W tym module nauczyli się Państwo, że: Series.rank() oblicza rangi liczbowe z możliwością konfiguracji rozstrzygania remisów, groupby().rank() resetuje rangi w każdej grupie, umożliwiając sprawiedliwe porównania między grupami, pct=True przekształca rangi w percentyle (od 0 do 1), a pd.qcut tworzy przedziały o jednakowej częstości na potrzeby przypisywania kwartylowych, decylowych i percentylowych poziomów. Następnie omówimy wskazówki dotyczące wydajności Pandas — profilowanie, unikanie powolnych pętli i efektywne typy danych.
Ucz się Python dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 30
- Lekcje
- 120
Często zadawane pytania
Czy lekcja „Rangi i percentyle w grupach” jest bezpłatna?
Tak — pełny tekst „Rangi i percentyle w grupach” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Pandas & NumPy Academy, przejdź na CoddyKit PRO. Kurs Pandas & NumPy Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Rangi i percentyle w grupach”?
Obliczaj rangi w obrębie grup za pomocą groupby().rank() i twórz przedziały percentylowe za pomocą pd.qcut na potrzeby porównań względnych. Ćwiczysz Pandas & NumPy Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Pandas & NumPy Academy?
Nie wymagamy żadnego doświadczenia. Pandas & NumPy Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Rangi i percentyle w grupach”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Pandas & NumPy Academy?
Tak. Każda lekcja Pandas & NumPy Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Okna kroczące
- Okna rozszerzające
- Wykładniczo ważona średnia krocząca
- Rangi i percentyle w grupach