Cómo verificar el tipo de columna en los pandas de Python

130

Necesito usar diferentes funciones para tratar columnas numéricas y columnas de cadena. Lo que estoy haciendo ahora es realmente tonto:

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

¿Hay alguna forma más elegante de hacer esto? P.ej

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
fuente
2
stringno es un dtype
David Robinson

Respuestas:

123

Puede acceder al tipo de datos de una columna 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])
David Robinson
fuente
1
Hola David, ¿Puedes comentar por qué incluiste == np.float64? ¿No estamos tratando de convertirnos en carrozas? Gracias.
Ryan Chase
@RyanChase El OP en esta pregunta nunca dijo que se estaba convirtiendo en flotantes, solo necesitaba saber si usar una función (no especificada) treat_numeric. Como él incluyó agg.dtypes==np.float64como opción, yo también.
David Robinson
3
Hay más tipos numéricos en numpy que estos dos, todo numberaquí: docs.scipy.org/doc/numpy-1.13.0/reference/arrays.scalars.html La solución general esis_numeric_dtype(agg[y])
Attila Tanyi
94

En pandas 0.20.2puedes hacer:

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

Entonces su código se convierte en:

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
fuente
1
¿Hay alguna alternativa para las versiones anteriores de pandas? Me sale el error: Ningún módulo llamado api.types.
rph el
2
pandas.core.common.is_numeric_dtypeha existido desde Pandas 0.13, y hace lo mismo, pero fue desaprobado a favor pandas.api.types.is_numeric_dtypeen 0.19, creo
Migwell
Es la respuesta más nativa. Pero uno debe tener en cuenta algunas advertencias aquí.
BeforeFlight
46

Sé que esto es un poco viejo pero con pandas 19.02, puedes hacer:

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

Miguel
fuente
1
buena respuesta, aunque probablemente lo haría include[np.number](para incluir también ints y flotantes de 32 bits) para la primera línea y exclude[object]para la segunda línea. Las cadenas son objetos en lo que respecta a los tipos de letra. De hecho, incluir 'string' con objeto me da un error.
JohnE
1
parece que "string" ya no es compatible, se debe usar "object" en su lugar. Pero definitivamente la respuesta correcta :)
Bertrand
También debe notarse que 'period'dtype está subiendo NotImplementedErrorpor ahora (pandas 0.24.2). Por lo tanto, uno puede necesitar un procesamiento posterior hecho a mano.
Antes del vuelo del
21

El título de la pregunta formulada es general, pero el caso de uso de los autores indicado en el cuerpo de la pregunta es específico. Por lo tanto, se pueden utilizar otras respuestas.

Pero para responder completamente a la pregunta del título , debe aclararse que parece que todos los enfoques pueden fallar en algunos casos y requerir un poco de revisión. Los revisé todos (y algunos adicionales) para disminuir el orden de confiabilidad (en mi opinión):

1. Comparación de tipos directamente a través de ==(respuesta aceptada).

A pesar del hecho de que esta es una respuesta aceptada y tiene la mayoría de los votos a favor, creo que este método no debería usarse en absoluto. Porque, de hecho, este enfoque se desaconseja en Python como se menciona varias veces aquí .
Pero si todavía lo quieren usar - debe ser consciente de algunas dtypes pandas-específicos como pd.CategoricalDType, pd.PeriodDtypeo pd.IntervalDtype. Aquí hay que usar extra type( )para reconocer dtype correctamente:

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

Otra advertencia aquí es que ese tipo debe señalarse con precisión:

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

Este método no se ha mencionado en las respuestas hasta ahora.

Así que si la comparación directa de los tipos no es una buena idea - vamos a tratar función integrada de Python para este propósito, a saber - isinstance().
Falla solo al principio, porque supone que tenemos algunos objetos, pero pd.Serieso pd.DataFramepodemos usarlos como contenedores vacíos con dtypeobjetos predefinidos pero sin ningún objeto:

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

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

Pero si de alguna manera se supera este problema y quiere acceder a cada objeto, por ejemplo, en la primera fila y comprueba su tipo de letra de esta manera:

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

Será engañoso en el caso de un tipo mixto de datos en una sola columna:

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

Y por último, pero no menos importante: este método no puede reconocer directamente Categorydtype. Como se indica en los documentos :

Devolver un solo artículo de datos categóricos también devolverá el valor, no un categórico de longitud "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')

Entonces este método también es casi inaplicable.

3. df.dtype.kindenfoque.

Este método aún puede funcionar con vacío pd.Serieso pd.DataFramestiene otros problemas.

Primero: no puede diferir algunos tipos:

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

En segundo lugar, lo que en realidad todavía no está claro para mí, incluso regresa en algunos tipos . Ninguno .

4. df.select_dtypesenfoque.

Esto es casi lo que queremos. Este método está diseñado dentro de los pandas para que maneje la mayoría de los casos de esquina mencionados anteriormente: marcos de datos vacíos, difiere bien los tipos de numpy o específicos de pandas. Funciona bien con un solo tipo de letra .select_dtypes('bool'). Puede usarse incluso para seleccionar grupos de columnas basados ​​en 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')

Así, como se indica en los documentos :

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

Puede pensar que aquí vemos los primeros resultados inesperados (solía ser para mí: pregunta ) - TimeDeltase incluye en la salida DataFrame. Pero, como se respondió al contrario, debería ser así, pero uno debe ser consciente de ello. Tenga en cuenta que boolse omite dtype, que también puede ser indeseable para alguien, pero se debe booly numberestá en diferentes " subárboles " de dtypes numpy. En caso de bool, podemos usar test.select_dtypes(['bool'])aquí.

La siguiente restricción de este método es que para la versión actual de pandas (0.24.2), este código: test.select_dtypes('period')se elevará NotImplementedError.

Y otra cosa es que no puede diferenciar cadenas de otros objetos:

test.select_dtypes('object')

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

Pero esto es, primero, ya mencionado en los documentos. Y segundo, no es el problema de este método, sino la forma en que se almacenan las cadenas DataFrame. Pero de todos modos este caso tiene que tener algún procesamiento posterior.

5. df.api.types.is_XXX_dtypeenfoque.

Se supone que esta es la forma más robusta y nativa de lograr el reconocimiento de dtype (supongo que la ruta del módulo donde residen las funciones dice). Y funciona casi perfectamente, pero todavía tiene al menos una advertencia y aún tiene que distinguir de alguna manera las columnas de cadena .

Además, esto puede ser subjetivo, pero este enfoque también tiene más numberprocesamiento de grupos de tipos de dty 'comprensibles para humanos ' en comparación con .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 timedeltay boolestá incluido. Perfecto.

Mi canalización explota exactamente esta funcionalidad en este momento, más un poco de procesamiento manual posterior.

Salida.

Espero haber podido argumentar el punto principal: que todos los enfoques discutidos pueden usarse, pero solo pd.DataFrame.select_dtypes()y pd.api.types.is_XXX_dtyperealmente deberían considerarse como los aplicables.

Antes del vuelo
fuente
1
Gran y bien formulada respuesta. :-)
Oliver
7

Si desea marcar el tipo de una columna de marco de datos como una cadena, puede hacer lo siguiente:

df['A'].dtype.kind

Un ejemplo:

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 respuesta para tu código:

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
fuente
4

Para imprimir bastante los tipos de datos de columna

Para verificar los tipos de datos después de, por ejemplo, una importación desde un archivo

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

Salida ilustrativa:

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