comment vérifier le dtype d'une colonne dans les pandas python

134

J'ai besoin d'utiliser différentes fonctions pour traiter les colonnes numériques et les colonnes de chaînes. Ce que je fais maintenant est vraiment stupide:

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

Y a-t-il une manière plus élégante de faire cela? Par exemple

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
la source
2
stringn'est pas un dtype
David Robinson

Réponses:

124

Vous pouvez accéder au type de données d'une colonne avec 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
la source
1
Salut David, Pouvez-vous nous expliquer pourquoi vous avez inclus == np.float64? N'essayons-nous pas de nous convertir en flotteurs? Merci.
Ryan Chase
@RyanChase L'OP dans cette question n'a jamais dit qu'il se convertissait en flotteurs, il avait juste besoin de savoir s'il devait utiliser une fonction (non spécifiée) treat_numeric. Puisqu'il a inclus agg.dtypes==np.float64en option, je l'ai fait aussi.
David Robinson
3
Il y a plus de types numériques dans numpy que ces deux, tout en numberdessous: docs.scipy.org/doc/numpy-1.13.0/reference/arrays.scalars.html La solution générale estis_numeric_dtype(agg[y])
Attila Tanyi
96

En pandas 0.20.2vous pouvez faire:

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

Ainsi votre code devient:

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
la source
1
Existe-t-il une alternative aux anciennes versions de pandas? J'obtiens l'erreur: aucun module nommé api.types.
rph
2
pandas.core.common.is_numeric_dtypeexiste depuis Pandas 0.13, et il fait la même chose, mais il a été déconseillé en faveur de la version pandas.api.types.is_numeric_dtype0.19, je pense
Migwell
C'est la réponse la plus native. Mais il faut être conscient de certaines mises en garde ici.
BeforeFlight
46

Je sais que c'est un peu un vieux fil mais avec pandas 19.02, vous pouvez faire:

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
la source
1
bonne réponse même si je le ferais probablement include[np.number](pour inclure également les entiers et les flottants 32 bits) pour la première ligne et exclude[object]pour la deuxième ligne. Les chaînes sont des objets en ce qui concerne les dtypes. En fait, inclure 'string' avec object me donne une erreur.
JohnE
1
semble que "string" n'est plus supporté, "object" doit être utilisé à la place. Mais définitivement la bonne réponse :)
Bertrand
Il faut également noter que 'period'dtype augmente NotImplementedErrorpour l'instant (pandas 0.24.2). Donc, on peut avoir besoin d'un post-traitement à la main.
BeforeFlight
21

Le titre de la question posée est général, mais le cas d'utilisation des auteurs indiqué dans le corps de la question est spécifique. Donc, toutes les autres réponses peuvent être utilisées.

Mais pour répondre pleinement à la question du titre, il convient de préciser qu'il semble que toutes les approches peuvent échouer dans certains cas et nécessiter quelques retouches. Je les ai tous examinés (et certains supplémentaires) par ordre décroissant de fiabilité (à mon avis):

1. Comparaison des types directement via ==(réponse acceptée).

Malgré le fait que cette réponse est acceptée et que la plupart des votes positifs comptent, je pense que cette méthode ne devrait pas du tout être utilisée. Car en fait cette approche est déconseillée en python comme mentionné à plusieurs reprises ici .
Mais si l' on veut encore utiliser - devrait être au courant de certains comme dtypes pandas géants-spécifiques de pd.CategoricalDType, pd.PeriodDtypeou pd.IntervalDtype. Ici, il faut utiliser un supplément type( )pour reconnaître correctement 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

Une autre mise en garde ici est que ce type doit être souligné avec précision:

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

Cette méthode n'a pas encore été mentionnée dans les réponses.

Donc, si la comparaison directe des types n'est pas une bonne idée - essayons la fonction python intégrée à cet effet, à savoir - isinstance().
Cela échoue juste au début, car suppose que nous avons des objets, mais pd.Seriesou pd.DataFramepeuvent être utilisés comme de simples conteneurs vides avec des dtypeobjets prédéfinis mais pas d'objets:

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

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

Mais si l'on surmonte ce problème d'une manière ou d'une autre, et veut accéder à chaque objet, par exemple, dans la première ligne et vérifie son dtype comme quelque chose comme ça:

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

Ce sera trompeur dans le cas de type mixte de données dans une seule colonne:

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

Et le dernier mais non le moindre - cette méthode ne peut pas reconnaître directement Categorydtype. Comme indiqué dans la documentation :

Le renvoi d'un seul élément à partir de données catégorielles renverra également la valeur, et non une catégorie de longueur «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')

Donc, cette méthode est également presque inapplicable.

3. df.dtype.kindapproche.

Cette méthode peut encore fonctionner avec vide pd.Seriesou pd.DataFramesmais présente d'autres problèmes.

Premièrement, il est impossible de différer certains types de 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

Deuxièmement, ce qui n'est pas encore clair pour moi, il revient même sur certains dtypes Aucun .

4. df.select_dtypesapproche.

C'est presque ce que nous voulons. Cette méthode est conçue à l'intérieur des pandas afin de gérer la plupart des cas de coin mentionnés précédemment - les DataFrames vides, diffère bien des dtypes numpy ou spécifiques aux pandas. Cela fonctionne bien avec un seul dtype comme .select_dtypes('bool'). Il peut être utilisé même pour sélectionner des groupes de colonnes en fonction de 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')

Comme tel, comme indiqué dans la documentation :

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 peut penser que nous voyons ici les premiers résultats inattendus (auparavant pour moi: question ) - TimeDeltaest inclus dans la sortie DataFrame. Mais comme il a répondu au contraire, il devrait en être ainsi, mais il faut en être conscient. Notez que booldtype est ignoré, ce qui peut également être indésirable pour quelqu'un, mais cela est dû à boolet se numbertrouve dans différents " sous-arbres " de dtypes numpy. Dans le cas de bool, nous pouvons utiliser test.select_dtypes(['bool'])ici.

La prochaine restriction de cette méthode est que pour la version actuelle de pandas (0.24.2), ce code: test.select_dtypes('period')augmentera NotImplementedError.

Et une autre chose est qu'il ne peut pas différer les chaînes des autres objets:

test.select_dtypes('object')

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

Mais c'est, d'abord - déjà mentionné dans la documentation. Et deuxièmement, ce n'est pas le problème de cette méthode, mais plutôt la façon dont les chaînes sont stockées DataFrame. Mais de toute façon, ce cas doit avoir un post-traitement.

5. df.api.types.is_XXX_dtypeapproche.

Celui-ci est destiné à être le moyen le plus robuste et le plus natif de réaliser la reconnaissance de type dtype (chemin du module où les fonctions résident dit par lui-même) comme je suppose. Et cela fonctionne presque parfaitement, mais il reste au moins une mise en garde et doit encore distinguer les colonnes de chaînes .

En outre, cela peut être subjectif, mais cette approche a également un numbertraitement de groupe dtypes plus `` compréhensible par l' homme '' par rapport à .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

Non timedeltaet boolest inclus. Parfait.

Mon pipeline exploite exactement cette fonctionnalité à ce moment, plus un peu de traitement post-manuel.

Production.

J'espère avoir pu argumenter le point principal - que toutes les approches discutées peuvent être utilisées, mais seulement pd.DataFrame.select_dtypes()et pd.api.types.is_XXX_dtypedevraient être vraiment considérées comme les approches applicables.

Avant le vol
la source
1
Réponse géniale et bien formulée. :-)
Oliver
8

Si vous souhaitez marquer le type d'une colonne de dataframe comme une chaîne, vous pouvez faire:

df['A'].dtype.kind

Un exemple:

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 réponse à votre 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])
À M
la source
4

Pour bien imprimer les types de données de colonne

Pour vérifier les types de données après, par exemple, une importation depuis un fichier

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

Sortie illustrative:

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