come controllare il dtype di una colonna in python panda


134

Ho bisogno di utilizzare funzioni diverse per trattare colonne numeriche e colonne stringa. Quello che sto facendo ora è davvero stupido:

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])    

C'è un modo più elegante per farlo? Per esempio

for y in agg.columns:
    if(dtype(agg[y]) == 'string'):
          treat_str(agg[y])
    elif(dtype(agg[y]) != 'string'):
          treat_numeric(agg[y])

2
stringnon è un dtype
David Robinson

Risposte:


124

È possibile accedere al tipo di dati di una colonna con 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])

1
Ciao David, puoi commentare il motivo per cui hai incluso == np.float64? Non stiamo cercando di convertire in float? Grazie.
Ryan Chase

@ RyanChase L'OP in questa domanda non ha mai detto che si stava convertendo in float, aveva solo bisogno di sapere se usare una funzione (non specificata) treat_numeric. Dato che ha incluso agg.dtypes==np.float64come opzione, l'ho fatto anche io.
David Robinson

3
Ci sono più tipi numerici in numpy di questi due, tutto numberqui sotto : docs.scipy.org/doc/numpy-1.13.0/reference/arrays.scalars.html La soluzione generale èis_numeric_dtype(agg[y])
Attila Tanyi

96

In pandas 0.20.2puoi fare:

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

Quindi il tuo codice diventa:

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])

1
C'è qualche alternativa per le versioni precedenti dei panda? Ottengo l'errore: Nessun modulo denominato api.types.
rph

2
pandas.core.common.is_numeric_dtypeesiste da Pandas 0.13, e fa la stessa cosa, ma è stato deprecato a favore di pandas.api.types.is_numeric_dtype0.19, credo
Migwell

È la risposta più nativa. Ma si dovrebbe essere consapevoli di alcuni avvertimenti qui.
BeforeFlight

46

So che questo è un po 'un vecchio thread ma con i panda 19.02, puoi fare:

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


1
buona risposta anche se probabilmente farei include[np.number](per includere anche int e float a 32 bit) per la prima riga e exclude[object]per la seconda riga. Le stringhe sono oggetti per quanto riguarda i dtypes. In effetti, includere 'string' con object mi dà un errore.
JohnE

1
sembra che "stringa" non sia più supportata, al suo posto si deve usare "oggetto". Ma definitivamente la risposta giusta :)
Bertrand

Inoltre dovrebbe essere notato che 'period'dtype sta aumentando NotImplementedErrorper ora (panda 0.24.2). Quindi potrebbe essere necessaria una post-elaborazione fatta a mano.
BeforeFlight

21

Il titolo della domanda è generale, ma il caso d'uso dell'autore indicato nel corpo della domanda è specifico. Quindi qualsiasi altra risposta può essere utilizzata.

Ma per rispondere in modo completo alla domanda del titolo , dovrebbe essere chiarito che sembra che tutti gli approcci possano fallire in alcuni casi e richiedere qualche rielaborazione. Li ho esaminati tutti (e alcuni aggiuntivi) in ordine decrescente di affidabilità (a mio parere):

1. Confronto dei tipi direttamente tramite ==(risposta accettata).

Nonostante il fatto che questa sia una risposta accettata e abbia il maggior numero di voti positivi, penso che questo metodo non dovrebbe essere utilizzato affatto. Perché in effetti questo approccio è sconsigliato in Python come menzionato più volte qui .
Ma se si vuole ancora di usarlo - dovrebbe essere a conoscenza di alcune dtypes panda-specifici come pd.CategoricalDType, pd.PeriodDtypeo pd.IntervalDtype. Qui è necessario utilizzare extra type( )per riconoscere correttamente 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

Un altro avvertimento qui è che il tipo dovrebbe essere sottolineato con precisione:

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()approccio.

Finora questo metodo non è stato menzionato nelle risposte.

Quindi, se il confronto diretto dei tipi non è una buona idea, proviamo la funzione Python incorporata per questo scopo, vale a dire - isinstance().
Fallisce solo all'inizio, perché presume che abbiamo alcuni oggetti, ma pd.Serieso pd.DataFramepuò essere usato come solo contenitori vuoti con predefiniti dtypema nessun oggetto al suo interno:

s = pd.Series([], dtype=bool)
s

>>> Series([], dtype: bool)

Ma se in qualche modo si supera questo problema e si desidera accedere a ciascun oggetto, ad esempio, nella prima riga e controllare il suo dtype in questo modo:

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')

Sarebbe fuorviante nel caso di dati di tipo misto in una singola colonna:

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')

E, ultimo ma non meno importante, questo metodo non è in grado di riconoscere direttamente Categorydtype. Come indicato nei documenti :

Anche la restituzione di un singolo elemento da dati categoriali restituirà il valore, non un categorico di lunghezza "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')

Quindi anche questo metodo è quasi inapplicabile.

3. df.dtype.kind approccio.

Questo metodo può ancora funzionare con vuoto pd.Serieso pd.DataFramesma presenta altri problemi.

Primo: non è in grado di differire alcuni dtypes:

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

In secondo luogo, ciò che in realtà non è ancora chiaro per me, ritorna anche su alcuni tipi Nessuno .

4. df.select_dtypesapproccio.

Questo è quasi quello che vogliamo. Questo metodo è stato progettato all'interno dei panda in modo da gestire la maggior parte dei casi d'angolo menzionati in precedenza: DataFrame vuoti, differisce bene dai dtypes specifici di numpy o panda. Funziona bene con un singolo dtype come .select_dtypes('bool'). Può essere utilizzato anche per selezionare gruppi di colonne in base a 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')

In questo modo, come indicato nei documenti :

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

On può pensare che qui vediamo i primi risultati inaspettati (in passato per me: domanda ) - TimeDeltaè incluso nell'output DataFrame. Ma come risposta contraria dovrebbe essere così, ma bisogna esserne consapevoli. Nota che booldtype viene saltato, che potrebbe anche essere indesiderato per qualcuno, ma è dovuto boole numbersi trova in diversi " sottoalberi " di numpy dtypes. In caso di bool, possiamo usare test.select_dtypes(['bool'])qui.

La prossima restrizione di questo metodo è che per la versione corrente di panda (0.24.2), questo codice: test.select_dtypes('period')aumenterà NotImplementedError.

E un'altra cosa è che non è in grado di differenziare le stringhe da altri oggetti:

test.select_dtypes('object')

>>>     str     obj
>>> 0    s1     [1, 2, 3]
>>> 1    s2     [5435, 35, -52, 14]

Ma questo è il primo - già menzionato nei documenti. E secondo, non è il problema di questo metodo, piuttosto il modo in cui le stringhe vengono memorizzate DataFrame. Ma comunque questo caso deve avere un po 'di post-elaborazione.

5. df.api.types.is_XXX_dtypeapproccio.

Questo è inteso per essere il modo più robusto e nativo per ottenere il riconoscimento di dtype (il percorso del modulo in cui risiedono le funzioni dice da solo) come suppongo. E funziona quasi perfettamente, ma ha ancora almeno un avvertimento e deve ancora distinguere in qualche modo le colonne delle stringhe .

Inoltre, questo può essere soggettivo, ma questo approccio ha anche numberun'elaborazione di gruppo di tipi più ` ` comprensibili dall'uomo '' rispetto a .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

No timedeltaed boolè incluso. Perfetto.

La mia pipeline sfrutta esattamente questa funzionalità in questo momento, oltre a un po 'di elaborazione post-manuale.

Produzione.

Spero di essere stato in grado di argomentare il punto principale - che tutti gli approcci discussi possono essere utilizzati, ma solo pd.DataFrame.select_dtypes()e pd.api.types.is_XXX_dtypedovrebbero essere realmente considerati come quelli applicabili.


1
Risposta ottima e ben formulata. :-)
Oliver

8

Se vuoi contrassegnare il tipo di una colonna di dataframe come una stringa, puoi fare:

df['A'].dtype.kind

Un esempio:

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')

La risposta per il tuo codice:

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])

4

Per stampare abbastanza i tipi di dati delle colonne

Per controllare i tipi di dati dopo, ad esempio, un'importazione da un file

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]) )

Produzione illustrativa:

Type     Column Name                    Example Value
-----------------------------------------------------
int64    Age                            49
object   Attrition                      No
object   BusinessTravel                 Travel_Frequently
float64  DailyRate                      279.0
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.