Tytuł zadawanego pytania jest ogólny, ale przypadek użycia przez autorów podany w treści pytania jest specyficzny. Można więc użyć innych odpowiedzi.
Aby jednak w pełni odpowiedzieć na tytułowe pytanie , należy wyjaśnić, że wydaje się, że wszystkie podejścia mogą w niektórych przypadkach zawodzić i wymagać pewnych poprawek. Przejrzałem je wszystkie (i kilka dodatkowych) w kolejności malejącej wiarygodności (moim zdaniem):
1. Porównywanie typów bezpośrednio przez ==
(zaakceptowana odpowiedź).
Pomimo tego, że jest to akceptowana odpowiedź i ma najwięcej głosów pozytywnych, myślę, że ta metoda w ogóle nie powinna być stosowana. Ponieważ w rzeczywistości takie podejście jest odradzane w Pythonie, jak kilkakrotnie wspomniano tutaj .
Ale jeśli nadal chcesz go używać - powinni być świadomi pewnych specyficznych dtypes pandy jak pd.CategoricalDType
, pd.PeriodDtype
lub pd.IntervalDtype
. Tutaj trzeba użyć extra type( )
, aby poprawnie rozpoznać dtype:
s = pd.Series([pd.Period('2002-03','D'), pd.Period('2012-02-01', 'D')])
s
s.dtype == pd.PeriodDtype # Not working
type(s.dtype) == pd.PeriodDtype # working
>>> 0 2002-03-01
>>> 1 2012-02-01
>>> dtype: period[D]
>>> False
>>> True
Kolejnym zastrzeżeniem jest to, że należy dokładnie wskazać ten typ:
s = pd.Series([1,2])
s
s.dtype == np.int64 # Working
s.dtype == np.int32 # Not working
>>> 0 1
>>> 1 2
>>> dtype: int64
>>> True
>>> False
2. isinstance()
podejście.
Do tej pory nie wspomniano o tej metodzie w odpowiedziach.
Jeśli więc bezpośrednie porównywanie typów nie jest dobrym pomysłem - wypróbujmy w tym celu wbudowaną funkcję Pythona, a mianowicie - isinstance()
.
To zawodzi dopiero na początku, ponieważ zakłada, że mamy jakieś obiekty, ale pd.Series
lub pd.DataFrame
mogą być używane jako puste pojemniki z predefiniowanymi, dtype
ale bez obiektów:
s = pd.Series([], dtype=bool)
s
>>> Series([], dtype: bool)
Ale jeśli ktoś w jakiś sposób przezwycięży ten problem i chce uzyskać dostęp do każdego obiektu, na przykład w pierwszym rzędzie i sprawdza jego typ w ten sposób:
df = pd.DataFrame({'int': [12, 2], 'dt': [pd.Timestamp('2013-01-02'), pd.Timestamp('2016-10-20')]},
index = ['A', 'B'])
for col in df.columns:
df[col].dtype, 'is_int64 = %s' % isinstance(df.loc['A', col], np.int64)
>>> (dtype('int64'), 'is_int64 = True')
>>> (dtype('<M8[ns]'), 'is_int64 = False')
Będzie to mylące w przypadku mieszanego rodzaju danych w jednej kolumnie:
df2 = pd.DataFrame({'data': [12, pd.Timestamp('2013-01-02')]},
index = ['A', 'B'])
for col in df2.columns:
df2[col].dtype, 'is_int64 = %s' % isinstance(df2.loc['A', col], np.int64)
>>> (dtype('O'), 'is_int64 = False')
I wreszcie - ta metoda nie może bezpośrednio rozpoznać typu Category
. Jak stwierdzono w dokumentach :
Zwrócenie pojedynczego elementu z danych kategorycznych również zwróci wartość, a nie kategorię o długości „1”.
df['int'] = df['int'].astype('category')
for col in df.columns:
df[col].dtype, 'is_int64 = %s' % isinstance(df.loc['A', col], np.int64)
>>> (CategoricalDtype(categories=[2, 12], ordered=False), 'is_int64 = True')
>>> (dtype('<M8[ns]'), 'is_int64 = False')
Więc ta metoda jest również prawie nie do zastosowania.
3. df.dtype.kind
podejście.
Ta metoda może jednak działać z pustą pd.Series
lub pd.DataFrames
ma inne problemy.
Po pierwsze - nie jest w stanie rozróżnić niektórych typów:
df = pd.DataFrame({'prd' :[pd.Period('2002-03','D'), pd.Period('2012-02-01', 'D')],
'str' :['s1', 's2'],
'cat' :[1, -1]})
df['cat'] = df['cat'].astype('category')
for col in df:
# kind will define all columns as 'Object'
print (df[col].dtype, df[col].dtype.kind)
>>> period[D] O
>>> object O
>>> category O
Po drugie, co właściwie nadal jest dla mnie niejasne, powraca nawet w niektórych typach Brak .
4. df.select_dtypes
podejście.
To jest prawie to, czego chcemy. Ta metoda została zaprojektowana w pandach, więc obsługuje większość wspomnianych wcześniej przypadków narożnych - puste ramki DataFrames, dobrze różni dtypy specyficzne dla numpy lub pandy. Działa dobrze z pojedynczym typem, takim jak .select_dtypes('bool')
. Może służyć nawet do wybierania grup kolumn na podstawie dtype:
test = pd.DataFrame({'bool' :[False, True], 'int64':[-1,2], 'int32':[-1,2],'float': [-2.5, 3.4],
'compl':np.array([1-1j, 5]),
'dt' :[pd.Timestamp('2013-01-02'), pd.Timestamp('2016-10-20')],
'td' :[pd.Timestamp('2012-03-02')- pd.Timestamp('2016-10-20'),
pd.Timestamp('2010-07-12')- pd.Timestamp('2000-11-10')],
'prd' :[pd.Period('2002-03','D'), pd.Period('2012-02-01', 'D')],
'intrv':pd.arrays.IntervalArray([pd.Interval(0, 0.1), pd.Interval(1, 5)]),
'str' :['s1', 's2'],
'cat' :[1, -1],
'obj' :[[1,2,3], [5435,35,-52,14]]
})
test['int32'] = test['int32'].astype(np.int32)
test['cat'] = test['cat'].astype('category')
W ten sposób, jak stwierdzono w dokumentach :
test.select_dtypes('number')
>>> int64 int32 float compl td
>>> 0 -1 -1 -2.5 (1-1j) -1693 days
>>> 1 2 2 3.4 (5+0j) 3531 days
Można pomyśleć, że tutaj widzimy pierwsze nieoczekiwane (jak dla mnie było: pytanie ) wyniki - TimeDelta
są uwzględniane w wynikach DataFrame
. Ale jak odpowiedziałem przeciwnie, powinno tak być, ale trzeba być tego świadomym. Zauważ, że bool
dtype jest pomijany, co może być również niepożądane dla kogoś, ale jest to spowodowane bool
i number
znajduje się w różnych „ poddrzewach ” numpy dtypes. W przypadku bool możemy użyć test.select_dtypes(['bool'])
tutaj.
Następnym ograniczeniem tej metody jest to, że dla aktualnej wersji pand (0.24.2) ten kod: test.select_dtypes('period')
podniesie NotImplementedError
.
Inną rzeczą jest to, że nie jest w stanie odróżnić ciągów od innych obiektów:
test.select_dtypes('object')
>>> str obj
>>> 0 s1 [1, 2, 3]
>>> 1 s2 [5435, 35, -52, 14]
Ale to jest po pierwsze - już wspomniane w dokumentacji. Po drugie - to nie problem tej metody, a raczej sposób, w jaki przechowywane są łańcuchy DataFrame
. Ale w każdym razie ten przypadek musi mieć trochę przetwarzania końcowego.
5. df.api.types.is_XXX_dtype
podejście.
Ten ma być najbardziej niezawodnym i natywnym sposobem na rozpoznanie typu (ścieżka modułu, w którym znajdują się funkcje, mówi sama o sobie), jak przypuszczam. I działa prawie idealnie, ale wciąż ma co najmniej jedno zastrzeżenie i nadal trzeba jakoś rozróżniać kolumny ciągów .
Poza tym może to być subiektywne, ale podejście to ma również bardziej „zrozumiałe dla człowieka” number
przetwarzanie grupowe typów w porównaniu z .select_dtypes('number')
:
for col in test.columns:
if pd.api.types.is_numeric_dtype(test[col]):
print (test[col].dtype)
>>> bool
>>> int64
>>> int32
>>> float64
>>> complex128
Nie timedelta
i bool
jest wliczony w cenę. Idealny.
Mój potok wykorzystuje dokładnie tę funkcjonalność w tej chwili, a także trochę przetwarzania ręcznego.
Wynik.
Mam nadzieję, że udało mi się przedstawić główną argumentację - że wszystkie omówione podejścia można wykorzystać, ale tylko pd.DataFrame.select_dtypes()
i pd.api.types.is_XXX_dtype
należy je traktować jako mające zastosowanie.
string
nie jest typem