Kategoryczny typ danych
Konwertuj kolumny tekstowe o małej liczbie unikatowych wartości na pandas Categorical, aby zmniejszyć zużycie pamięci i przyspieszyć operacje groupby.
Kategoryczny typ danych to bezpłatna lekcja Pandas & NumPy Academy na CoddyKit. To lekcja 3 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.
Czym jest dtype Categorical?
Typ Categorical dtype w Pandas jest przeznaczony dla kolumn zawierających ograniczony zbiór odrębnych wartości (o małej liczności), takich jak płeć, status, region lub kategoria produktu. Zamiast przechowywać pełny ciąg znaków w każdym wierszu, Pandas przechowuje unikatowe wartości (nazywane kategoriami) tylko raz i używa kodu całkowitego w każdym wierszu, aby się do nich odwoływać. Działa to podobnie do tabel słownikowych lub typów wyliczeniowych używanych w bazach danych.
import pandas as pd
df = pd.DataFrame({
'region': ['North', 'South', 'East', 'North', 'East', 'South'] * 1000
})
# Without Categorical: object dtype stores every string
print('object dtype memory:', df['region'].memory_usage(deep=True))
# With Categorical: only stores 3 unique values + integer codes
df['region_cat'] = df['region'].astype('category')
print('category dtype memory:', df['region_cat'].memory_usage(deep=True))Tworzenie obiektu Series typu Categorical
Aby przekonwertować kolumnę na typ Categorical, wywołaj .astype('category') dla dowolnego obiektu Series. Powstały obiekt Series typu Categorical przechowuje unikatowe wartości jako .cat.categories, a kody pozycji całkowitych jako .cat.codes. Można również utworzyć obiekt Categorical bezpośrednio za pomocą pd.Categorical(), określając z góry kategorie i ich kolejność.
import pandas as pd
s = pd.Series(['low', 'high', 'med', 'high', 'low'])
s_cat = s.astype('category')
print(s_cat.cat.categories) # Index(['high', 'low', 'med'], dtype='object')
print(s_cat.cat.codes) # integer codes per row
# 0 1
# 1 0
# 2 2
# 3 0
# 4 1Oszczędność pamięci dzięki Categorical
Oszczędność pamięci wynikająca z użycia dtype Categorical zależy od współczynnika liczności (liczba unikatowych wartości ÷ liczba wszystkich wierszy). W kolumnie zawierającej 1 000 000 wierszy i 5 unikatowych ciągów znaków dtype object przechowuje 1 milion wskaźników do ciągów (~50+ bajtów każdy), podczas gdy Categorical przechowuje tylko 5 ciągów oraz 1 milion 8-bitowych liczb całkowitych. Oszczędność może wynosić od 5 do 20 razy, zmniejszając rozmiar kolumny z 50 MB do 2–5 MB.
import pandas as pd
import numpy as np
n = 1_000_000
statuses = np.random.choice(['active', 'inactive', 'pending'], size=n)
df = pd.DataFrame({'status': statuses})
object_mem = df['status'].memory_usage(deep=True) / 1e6
df['status'] = df['status'].astype('category')
cat_mem = df['status'].memory_usage(deep=True) / 1e6
print(f'Object dtype: {object_mem:.1f} MB')
print(f'Category dtype: {cat_mem:.1f} MB')
print(f'Reduction: {object_mem/cat_mem:.1f}x')Uporządkowany Categorical do tworzenia rankingów
Czasami kategorie mają naturalny porządek — np. „low” < „medium” < „high” albo rozmiary koszulek XS < S < M < L < XL. Uporządkowany Categorical odwzorowuje ten ranking, umożliwiając wykonywanie sensownych operatorów porównania (<, >= itd.) i zapewniając sortowanie wyników groupby zgodnie z właściwą kolejnością znaczeniową, a nie alfabetycznie.
import pandas as pd
df = pd.DataFrame({
'priority': ['high', 'low', 'medium', 'high', 'low']
})
priority_type = pd.CategoricalDtype(
categories=['low', 'medium', 'high'],
ordered=True
)
df['priority'] = df['priority'].astype(priority_type)
# Comparison now works correctly
print(df['priority'] >= 'medium')
# 0 True
# 1 False
# 2 True
# 3 True
# 4 FalseSortowanie uporządkowanych obiektów Categorical
Podczas sortowania uporządkowanej kolumny Categorical Pandas używa zdefiniowanej kolejności kategorii zamiast kolejności alfabetycznej. Oznacza to, że 'low' występuje przed 'medium', a 'medium' przed 'high', niezależnie od tego, jak te wartości zostałyby uporządkowane jako ciągi znaków. Ma to kluczowe znaczenie przy tworzeniu poprawnie uporządkowanych tabel podsumowań i wykresów.
import pandas as pd
df = pd.DataFrame({
'severity': pd.Categorical(
['high', 'low', 'medium', 'low', 'high'],
categories=['low', 'medium', 'high'],
ordered=True
),
'count': [5, 20, 8, 15, 3]
})
# sort_values respects the categorical order
print(df.sort_values('severity')[['severity', 'count']])
# severity count
# 1 low 20
# 3 low 15
# 2 medium 8
# 0 high 5
# 4 high 3Szybkość GroupBy z Categorical
Pandas może optymalizować operacje groupby() na kolumnach Categorical, ponieważ z góry zna wszystkie możliwe grupy. Dzięki temu operacja groupby może być od 2 do 5 razy szybsza w przypadku dużych obiektów DataFrame. Ponadto groupby na kolumnie Categorical zwraca wszystkie kategorie — także te puste — dzięki czemu tabele podsumowań zawsze zawierają wiersz dla każdej oczekiwanej grupy, nawet jeśli niektóre grupy mają zero obserwacji.
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'region': pd.Categorical(
np.random.choice(['North', 'South', 'East', 'West'], 10),
categories=['North', 'South', 'East', 'West']
),
'sales': np.random.randint(100, 1000, 10)
})
# observed=False shows ALL categories even empty ones
print(df.groupby('region', observed=False)['sales'].sum())Dodawanie i usuwanie kategorii
Do zarządzania listą kategorii służy akcesor .cat. .cat.add_categories() dodaje nowe dozwolone wartości (co jest przydatne przed wstawieniem nowych danych), a .cat.remove_unused_categories() usuwa etykiety kategorii, którym nie odpowiadają żadne wiersze — jest to przydatne po filtrowaniu. Nie można przypisać wartości, której nie ma wśród kategorii, bez wcześniejszego jej dodania.
import pandas as pd
s = pd.Categorical(['a', 'b', 'a'], categories=['a', 'b', 'c'])
s = pd.Series(s)
print(s.cat.categories) # Index(['a', 'b', 'c'], dtype='object')
print(s.value_counts()) # a:2, b:1, c:0
# Remove unused category 'c'
s = s.cat.remove_unused_categories()
print(s.cat.categories) # Index(['a', 'b'], dtype='object')Zmiana nazw etykiet kategorii
.cat.rename_categories() umożliwia zmianę etykiet kategorii bez zmiany bazowych kodów. Jest to przydatne, gdy chcą Państwo wyświetlać bardziej przyjazne nazwy (np. 'M' → 'Male') albo poprawić niespójną wielkość liter w etykietach bez konwersji z powrotem do typu object i ponownego mapowania. Metoda przyjmuje listę (pozycjonalnie) albo słownik (wybiórczo).
import pandas as pd
s = pd.Series(pd.Categorical(['M', 'F', 'M', 'F'], categories=['M', 'F']))
# Rename using a dict
s = s.cat.rename_categories({'M': 'Male', 'F': 'Female'})
print(s)
# 0 Male
# 1 Female
# 2 Male
# 3 Female
print(s.cat.categories) # Index(['Male', 'Female'], dtype='object')Kiedy NIE używać Categorical
Typ danych Categorical jest złym wyborem przy dużej liczbie kategorii — jeśli niemal każdy wiersz ma unikalną wartość (np. identyfikator użytkownika, komentarz wprowadzony dowolnym tekstem lub UUID), indeks kategorii jest prawie tak duży jak dane oryginalne, więc nie zapewnia oszczędności pamięci. Orientacyjna zasada mówi, aby używać Categorical tylko wtedy, gdy liczba unikalnych wartości jest mniejsza niż około 50% całkowitej liczby wierszy, a szczególnie wtedy, gdy unikalnych wartości jest kilkadziesiąt lub kilkaset.
import pandas as pd
import numpy as np
df = pd.DataFrame({'user_id': range(1_000_000)})
# High-cardinality: every value is unique — don't use Categorical
object_mem = df['user_id'].astype(str).memory_usage(deep=True) / 1e6
cat_mem = df['user_id'].astype(str).astype('category').memory_usage(deep=True) / 1e6
print(f'String: {object_mem:.1f} MB')
print(f'Category: {cat_mem:.1f} MB')
# Category is WORSE for high-cardinality data!Categorical w tabelach przestawnych i Groupby
Użycie Categorical zapewnia spójny kształt wyniku operacji groupby i tabel przestawnych. Bez Categorical grupy, które akurat nie mają żadnych obserwacji, są po cichu pomijane w wyniku — może to prowadzić do niezgodności wyników podczas porównywania różnych wycinków danych. W przypadku Categorical i observed=False wszystkie grupy są zawsze uwzględniane w wyniku.
import pandas as pd
df = pd.DataFrame({
'quarter': pd.Categorical(
['Q1', 'Q1', 'Q3'],
categories=['Q1', 'Q2', 'Q3', 'Q4']
),
'revenue': [100, 200, 300]
})
# observed=False includes Q2 and Q4 even though they have no rows
result = df.groupby('quarter', observed=False)['revenue'].sum()
print(result)
# quarter
# Q1 300
# Q2 0
# Q3 300
# Q4 0Categorical a uczenie maszynowe
Wiele estymatorów scikit-learn oczekuje danych liczbowych. Aby przekonwertować kolumnę Categorical na liczby na potrzeby modelu, należy użyć .cat.codes (kodowanie etykiet) albo pd.get_dummies() (kodowanie one-hot). Kodowanie one-hot nie sugeruje uporządkowanej zależności między kategoriami. W przypadku uporządkowanych kategorii, takich jak poziomy priorytetu, odpowiednie jest kodowanie etykiet (bezpośrednie użycie kodów), ponieważ zachowuje ono informacje o kolejności.
import pandas as pd
df = pd.DataFrame({
'colour': pd.Categorical(['red', 'blue', 'green', 'red'])
})
# One-hot encode for unordered categories
one_hot = pd.get_dummies(df['colour'], prefix='colour')
print(one_hot)
# colour_blue colour_green colour_red
# 0 0 0 1
# 1 1 0 0
# 2 0 1 0
# 3 0 0 1Szybkie sprawdzenie
Sprawdź swoje rozumienie typu danych Categorical.
Podsumowanie lekcji
W tej lekcji poznali Państwo: typ danych Categorical przechowuje unikalne wartości tylko raz i używa kodów całkowitych dla poszczególnych wierszy, zapewniając dużą oszczędność pamięci w kolumnach o małej liczbie kategorii; uporządkowany Categorical obsługuje znaczące porównania i poprawne sortowanie; natomiast groupby z observed=False uwzględnia wszystkie kategorie, także puste. Należy unikać Categorical w kolumnach o dużej liczbie kategorii. W następnej części poprawnie przeanalizujemy ciągi dat za pomocą pd.to_datetime().
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 „Kategoryczny typ danych” jest bezpłatna?
Tak — pełny tekst „Kategoryczny typ danych” 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 „Kategoryczny typ danych”?
Konwertuj kolumny tekstowe o małej liczbie unikatowych wartości na pandas Categorical, aby zmniejszyć zużycie pamięci i przyspieszyć operacje groupby. Ć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 3 z 4.
Ile czasu zajmuje lekcja „Kategoryczny typ danych”?
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
- Sprawdzanie typów danych kolumn
- Rzutowanie za pomocą astype()
- Kategoryczny typ danych
- Prawidłowe analizowanie dat