jak sprawdzić typ kolumny w pandach Pythona

135

Potrzebuję różnych funkcji do traktowania kolumn numerycznych i kolumn z ciągami. To, co teraz robię, jest naprawdę głupie:

allc = list((agg.loc[:, (agg.dtypes==np.float64)|(agg.dtypes==np.int)]).columns)
for y in allc:
    treat_numeric(agg[y])    

allc = list((agg.loc[:, (agg.dtypes!=np.float64)&(agg.dtypes!=np.int)]).columns)
for y in allc:
    treat_str(agg[y])    

Czy jest na to bardziej elegancki sposób? Na przykład

for y in agg.columns:
    if(dtype(agg[y]) == 'string'):
          treat_str(agg[y])
    elif(dtype(agg[y]) != 'string'):
          treat_numeric(agg[y])
James Bond
źródło
2
stringnie jest typem
David Robinson

Odpowiedzi:

124

Możesz uzyskać dostęp do typu danych kolumny za pomocą dtype:

for y in agg.columns:
    if(agg[y].dtype == np.float64 or agg[y].dtype == np.int64):
          treat_numeric(agg[y])
    else:
          treat_str(agg[y])
David Robinson
źródło
1
Cześć David, Czy możesz skomentować, dlaczego włączyłeś == np.float64? Czy nie próbujemy przekonwertować na pływaki? Dzięki.
Ryan Chase
@RyanChase OP w tym pytaniu nigdy nie powiedział, że konwertuje na zmiennoprzecinkowe, po prostu musiał wiedzieć, czy użyć (nieokreślonej) treat_numericfunkcji. Ponieważ włączył agg.dtypes==np.float64jako opcję, ja też to zrobiłem.
David Robinson
3
W numpy jest więcej typów numerycznych niż te dwa, wszystko poniżej number: docs.scipy.org/doc/numpy-1.13.0/reference/arrays.scalars.html Ogólne rozwiązanie tois_numeric_dtype(agg[y])
Attila Tanyi
96

W pandas 0.20.2możesz zrobić:

from pandas.api.types import is_string_dtype
from pandas.api.types import is_numeric_dtype

is_string_dtype(df['A'])
>>>> True

is_numeric_dtype(df['B'])
>>>> True

Twój kod staje się więc:

for y in agg.columns:
    if (is_string_dtype(agg[y])):
        treat_str(agg[y])
    elif (is_numeric_dtype(agg[y])):
        treat_numeric(agg[y])
danthelion
źródło
1
Czy jest jakaś alternatywa dla starszych wersji pand? Pojawia się błąd: brak modułu o nazwie api.types.
rph
2
pandas.core.common.is_numeric_dtypeistnieje od wersji 0.13 Pandy i robi to samo, ale został wycofany na korzyść wersji pandas.api.types.is_numeric_dtype0.19, jak sądzę
Migwell,
To najbardziej natywna odpowiedź. Należy jednak pamiętać o pewnych zastrzeżeniach .
Przed lotem
46

Wiem, że to trochę stary wątek, ale z pandami 19.02 możesz zrobić:

df.select_dtypes(include=['float64']).apply(your_function)
df.select_dtypes(exclude=['string','object']).apply(your_other_function)

http://pandas.pydata.org/pandas-docs/version/0.19.2/generated/pandas.DataFrame.select_dtypes.html

Mikrofon
źródło
1
dobra odpowiedź, chociaż prawdopodobnie zrobiłbym include[np.number](włączając również liczby int i 32-bitowe zmiennoprzecinkowe) dla pierwszej linii i exclude[object]drugiej linii. Łańcuchy są obiektami, jeśli chodzi o typy. W rzeczywistości dołączenie „string” do obiektu powoduje błąd.
JohnE,
1
Wygląda na to, że „ciąg znaków” nie jest już obsługiwany, zamiast tego należy użyć „obiektu”. Ale definitywnie dobra odpowiedź :)
Bertrand
Należy również zauważyć, że 'period'dtype podnosi NotImplementedErrorteraz (pandy 0.24.2). Więc może być potrzebne ręczne przetwarzanie końcowe.
Przed lotem
22

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.PeriodDtypelub 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.Serieslub pd.DataFramemogą być używane jako puste pojemniki z predefiniowanymi, dtypeale 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.kindpodejście.

Ta metoda może jednak działać z pustą pd.Serieslub pd.DataFramesma 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_dtypespodejś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 - TimeDeltasą uwzględniane w wynikach DataFrame. Ale jak odpowiedziałem przeciwnie, powinno tak być, ale trzeba być tego świadomym. Zauważ, że booldtype jest pomijany, co może być również niepożądane dla kogoś, ale jest to spowodowane booli numberznajduje 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_dtypepodejś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” numberprzetwarzanie 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 timedeltai booljest 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_dtypenależy je traktować jako mające zastosowanie.

Przed lotem
źródło
1
Świetna i dobrze sformułowana odpowiedź. :-)
Oliver
8

Jeśli chcesz oznaczyć typ kolumny dataframe jako ciąg, możesz zrobić:

df['A'].dtype.kind

Przykład:

In [8]: df = pd.DataFrame([[1,'a',1.2],[2,'b',2.3]])
In [9]: df[0].dtype.kind, df[1].dtype.kind, df[2].dtype.kind
Out[9]: ('i', 'O', 'f')

Odpowiedź na Twój kod:

for y in agg.columns:
    if(agg[y].dtype.kind == 'f' or agg[y].dtype.kind == 'i'):
          treat_numeric(agg[y])
    else:
          treat_str(agg[y])
Tomek
źródło
4

Aby ładnie wydrukować typy danych kolumn

Aby sprawdzić typy danych, na przykład po imporcie z pliku

def printColumnInfo(df):
    template="%-8s %-30s %s"
    print(template % ("Type", "Column Name", "Example Value"))
    print("-"*53)
    for c in df.columns:
        print(template % (df[c].dtype, c, df[c].iloc[1]) )

Przykładowe wyjście:

Type     Column Name                    Example Value
-----------------------------------------------------
int64    Age                            49
object   Attrition                      No
object   BusinessTravel                 Travel_Frequently
float64  DailyRate                      279.0
ePi272314
źródło