So überprüfen Sie den D-Typ einer Spalte in Python-Pandas

130

Ich muss verschiedene Funktionen verwenden, um numerische Spalten und Zeichenfolgenspalten zu behandeln. Was ich jetzt mache, ist wirklich dumm:

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

Gibt es eine elegantere Möglichkeit, dies zu tun? Z.B

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
quelle
2
stringist kein dtype
David Robinson

Antworten:

121

Sie können auf den Datentyp einer Spalte zugreifen mit 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
quelle
1
Hallo David, kannst du kommentieren, warum du == np.float64 aufgenommen hast? Versuchen wir nicht, in Floats umzuwandeln? Vielen Dank.
Ryan Chase
@RyanChase Das OP in dieser Frage hat nie gesagt, dass er in Floats konvertiert. Er musste nur wissen, ob eine (nicht spezifizierte) treat_numericFunktion verwendet werden soll. Da er agg.dtypes==np.float64als Option aufgenommen hat, habe ich es auch getan.
David Robinson
3
Es gibt mehr numerische Typen in numpy als diese beiden, alles numberhier unter : docs.scipy.org/doc/numpy-1.13.0/reference/arrays.scalars.html Die allgemeine Lösung lautetis_numeric_dtype(agg[y])
Attila Tanyi
94

In können pandas 0.20.2Sie tun:

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

So wird Ihr Code:

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
quelle
1
Gibt es eine Alternative für ältere Pandas-Versionen? Ich erhalte die Fehlermeldung: Kein Modul mit dem Namen api.types.
rph
2
pandas.core.common.is_numeric_dtypeexistiert seit Pandas 0.13, und es macht das gleiche, aber es wurde zugunsten von pandas.api.types.is_numeric_dtype
0.19
Es ist die native Antwort. Aber man sollte sich hier einiger Einschränkungen bewusst sein .
BeforeFlight
46

Ich weiß, dass dies ein alter Thread ist, aber mit Pandas 19.02 können Sie Folgendes tun:

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

Mike
quelle
1
Gute Antwort, obwohl ich es wahrscheinlich tun würde include[np.number](um auch Ints und 32-Bit-Floats einzuschließen) für die erste Zeile und exclude[object]für die zweite Zeile. Strings sind Objekte für dtypes. Tatsächlich gibt mir das Einfügen von 'string' in object einen Fehler.
JohnE
1
Anscheinend wird "string" nicht mehr unterstützt, stattdessen muss "object" verwendet werden. Aber definitiv die richtige Antwort :)
Bertrand
Es sollte auch beachtet werden, dass 'period'dtype vorerst erhöht NotImplementedError(pandas 0.24.2). Man muss also vielleicht eine handgemachte Nachbearbeitung brauchen.
BeforeFlight
20

Der Titel der gestellten Frage ist allgemein, aber der im Hauptteil der Frage angegebene Anwendungsfall der Autoren ist spezifisch. Es können also auch andere Antworten verwendet werden.

Aber um in vollem Umfang die Antwort Titelfrage sollte klargestellt werden , dass es scheint , als ob alle Ansätze können nicht in einigen Fällen und einig Nacharbeit erfordern. Ich habe alle (und einige zusätzliche) in abnehmender Reihenfolge der Zuverlässigkeit überprüft (meiner Meinung nach):

1. Typen direkt über vergleichen ==(akzeptierte Antwort).

Trotz der Tatsache, dass dies eine akzeptierte Antwort ist und die meisten Upvotes zählen, denke ich, dass diese Methode überhaupt nicht angewendet werden sollte. Weil in der Tat von diesem Ansatz in Python abgeraten wird, wie hier mehrmals erwähnt .
Aber wenn man sich noch verwenden mag - soll wie von einigen Pandas spezifischen dtypes darüber im Klaren sein pd.CategoricalDType, pd.PeriodDtypeoder pd.IntervalDtype. Hier muss man extra type( )verwenden, um dtype richtig zu erkennen:

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

Eine weitere Einschränkung ist, dass der Typ genau hervorgehoben werden sollte:

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

Diese Methode wurde bisher in den Antworten nicht erwähnt.

Wenn ein direkter Vergleich von Typen keine gute Idee ist, können Sie zu diesem Zweck die integrierte Python-Funktion ausprobieren, nämlich - isinstance().
Es schlägt nur am Anfang fehl, da davon ausgegangen wird, dass wir einige Objekte haben, aber pd.Seriesoder pd.DataFramenur als leere Container mit vordefinierten, dtypeaber keinen Objekten darin verwendet werden können:

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

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

Aber wenn man dieses Problem irgendwie überwindet und auf jedes Objekt zugreifen möchte, zum Beispiel in der ersten Zeile, und seinen dtype wie folgt überprüft:

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

Bei gemischten Datentypen in einer Spalte ist dies irreführend:

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

Und last but not least - diese Methode kann Categorydtype nicht direkt erkennen . Wie in den Dokumenten angegeben :

Wenn Sie ein einzelnes Element aus kategorialen Daten zurückgeben, wird auch der Wert zurückgegeben, nicht eine Kategorie mit der Länge „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')

Diese Methode ist also auch fast nicht anwendbar.

3. df.dtype.kindAnsatz.

Diese Methode funktioniert möglicherweise noch mit leer pd.Seriesoder hat pd.DataFramesaber andere Probleme.

Erstens - es kann einige d-Typen nicht unterscheiden:

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

Zweitens, was für mich eigentlich noch unklar ist, gibt es sogar bei einigen dtypes None zurück .

4. df.select_dtypesAnsatz.

Das ist fast das, was wir wollen. Diese Methode wurde in Pandas entwickelt, um die meisten zuvor erwähnten Eckfälle zu behandeln - leere DataFrames, unterscheidet sich gut von numpy- oder pandas-spezifischen dtypes. Es funktioniert gut mit einzelnen dtype wie .select_dtypes('bool'). Es kann sogar zum Auswählen von Spaltengruppen basierend auf dtype verwendet werden:

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

Wie in den Dokumenten angegeben :

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

Man könnte denken, dass wir hier erste unerwartete (für mich früher: Frage ) Ergebnisse sehen - TimeDeltasind in der Ausgabe enthalten DataFrame. Aber wie im Gegenteil beantwortet , sollte es so sein, aber man muss sich dessen bewusst sein. Beachten Sie, dass booldtype übersprungen wird, was für jemanden möglicherweise auch unerwünscht ist, aber auf verschiedene " Teilbäume " von numpy dtypes zurückzuführen ist boolund numbersich in diesen befindet. Im Falle von Bool können wir hier verwenden.test.select_dtypes(['bool'])

Die nächste Einschränkung dieser Methode besteht darin, dass für die aktuelle Version von Pandas (0.24.2) dieser Code: ausgelöst test.select_dtypes('period')wird NotImplementedError.

Und eine andere Sache ist, dass es nicht möglich ist, Zeichenfolgen von anderen Objekten zu unterscheiden:

test.select_dtypes('object')

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

Aber das ist erstens - bereits erwähnt in der Dokumentation. Und zweitens - ist nicht das Problem dieser Methode, sondern die Art und Weise, wie Zeichenfolgen gespeichert werden DataFrame. Aber trotzdem muss dieser Fall eine Nachbearbeitung haben.

5. df.api.types.is_XXX_dtypeAnsatz.

Dieser soll der robusteste und nativste Weg sein, um eine dtype-Erkennung zu erreichen (der Pfad des Moduls, in dem sich die Funktionen befinden, sagt von selbst), wie ich vermute. Und es funktioniert fast perfekt, hat aber immer noch mindestens eine Einschränkung und muss trotzdem irgendwie String-Spalten unterscheiden .

Außerdem mag dies subjektiv sein, aber dieser Ansatz hat auch eine "vom Menschen verständliche" numberVerarbeitung von dtypes-Gruppen im Vergleich zu .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

Nein timedeltaund boolist enthalten. Perfekt.

Meine Pipeline nutzt zu diesem Zeitpunkt genau diese Funktionalität sowie ein wenig Nachbearbeitung.

Ausgabe.

Ich hoffe, ich konnte den Hauptpunkt argumentieren - dass alle diskutierten Ansätze verwendet werden können, aber nur pd.DataFrame.select_dtypes()und pd.api.types.is_XXX_dtypewirklich als die anwendbaren betrachtet werden sollten.

BeforeFlight
quelle
1
Tolle und gut formulierte Antwort. :-)
Oliver
7

Wenn Sie den Typ einer Datenrahmenspalte als Zeichenfolge markieren möchten, haben Sie folgende Möglichkeiten:

df['A'].dtype.kind

Ein Beispiel:

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

Die Antwort für Ihren Code:

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])
Tom
quelle
4

Zum hübschen Drucken der Spaltendatentypen

So überprüfen Sie die Datentypen beispielsweise nach einem Import aus einer Datei

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

Illustrative Ausgabe:

Type     Column Name                    Example Value
-----------------------------------------------------
int64    Age                            49
object   Attrition                      No
object   BusinessTravel                 Travel_Frequently
float64  DailyRate                      279.0
ePi272314
quelle