como verificar o tipo de uma coluna em pandas python

130

Eu preciso usar funções diferentes para tratar colunas numéricas e colunas de seqüência de caracteres. O que estou fazendo agora é realmente burro:

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

Existe uma maneira mais elegante de fazer isso? Por exemplo

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
fonte
2
stringnão é um tipo
David Robinson

Respostas:

123

Você pode acessar o tipo de dados de uma coluna com 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
fonte
1
Olá David, Você pode comentar por que você incluiu == np.float64? Não estamos tentando converter para carros alegóricos? Obrigado.
Ryan Chase
@RyanChase O OP nesta questão nunca disse que estava convertendo em carros alegóricos, ele só precisava saber se deveria usar uma treat_numericfunção (não especificada) . Desde que ele incluiu agg.dtypes==np.float64como uma opção, eu fiz também.
David Robinson
3
Existem mais tipos numéricos em numpy do que esses dois, tudo abaixo number: docs.scipy.org/doc/numpy-1.13.0/reference/arrays.scalars.html A solução geral éis_numeric_dtype(agg[y])
Attila Tanyi
94

Em pandas 0.20.2você pode fazer:

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

Portanto, seu código se torna:

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
fonte
1
Existe alguma alternativa para versões mais antigas de pandas? Eu recebo o erro: Nenhum módulo chamado api.types.
Rd #
2
pandas.core.common.is_numeric_dtypeexiste desde Pandas 0,13, e ele faz a mesma coisa, mas foi preterido em favor de pandas.api.types.is_numeric_dtype, em 0,19, eu acho
Migwell
É a resposta mais nativa. Mas é preciso estar ciente de algumas advertências aqui.
AntesFlight
46

Eu sei que isso é um pouco antigo, mas com o pandas 19.02, você pode fazer:

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
fonte
1
boa resposta, embora eu provavelmente faria include[np.number](para incluir também entradas e flutuadores de 32 bits) para a primeira linha e exclude[object]para a segunda linha. Strings são objetos no que diz respeito a tipos. De fato, incluir 'string' com objeto gera um erro.
Johne
1
parece que "string" não é mais suportado, "object" deve ser usado. Mas definitivamente a resposta certa :)
Bertrand
Também deve ser notado que o 'period'dtype está aumentando NotImplementedErrorpor enquanto (pandas 0.24.2). Portanto, pode ser necessário algum pós-processamento artesanal.
Antes do voo
21

O título da pergunta feita é geral, mas o caso de uso dos autores indicado no corpo da pergunta é específico. Portanto, qualquer outra resposta pode ser usada.

Mas, para responder totalmente à pergunta do título , deve ser esclarecido que parece que todas as abordagens podem falhar em alguns casos e exigir algum retrabalho. Eu revi todos eles (e alguns adicionais) na diminuição da ordem de confiabilidade (na minha opinião):

1. Comparando tipos diretamente via ==(resposta aceita).

Apesar do fato de essa resposta ser aceita e ter mais votos positivos, acho que esse método não deve ser usado. Porque, de fato, essa abordagem é desencorajada em python, como mencionado várias vezes aqui .
Mas se alguém ainda quiser usá-lo - deve estar ciente de alguns tipos específicos de pandas pd.CategoricalDType, como pd.PeriodDtype, ou pd.IntervalDtype. Aqui é preciso usar extra type( )para reconhecer o dtype corretamente:

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

Outra ressalva aqui é que esse tipo deve ser apontado com precisão:

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

Este método não foi mencionado nas respostas até agora.

Portanto, se a comparação direta de tipos não é uma boa idéia - vamos tentar a função python interna para esse fim, a saber - isinstance().
Ele não apenas no início, porque assume que temos alguns objetos, mas pd.Seriesou pd.DataFramepode ser usado como apenas recipientes vazios com pré-definidos dtype, mas não objetos nele:

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

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

Mas se alguém de alguma forma superar esse problema, e quiser acessar cada objeto, por exemplo, na primeira linha e verificar seu dtype da seguinte forma:

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

Isso será enganoso no caso de dados mistos em coluna única:

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 por último mas não menos importante - esse método não pode reconhecer diretamente o Categorytipo. Conforme declarado nos documentos :

A devolução de um único item a partir de dados categóricos também retornará o valor, não uma categórica de comprimento "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')

Portanto, esse método também é quase inaplicável.

3. df.dtype.kindabordagem.

Esse método ainda pode funcionar com vazio pd.Seriesou pd.DataFramescom outros problemas.

Primeiro - não é possível diferenciar alguns 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

Segundo, o que ainda não está claro para mim, ele ainda retorna com alguns tipos Não .

4. df.select_dtypesabordagem.

Isso é quase o que queremos. Esse método foi desenvolvido dentro dos pandas para lidar com a maioria dos casos de canto mencionados anteriormente - DataFrames vazios, difere bem os tipos numpy ou específicos de pandas. Funciona bem com um tipo único .select_dtypes('bool'). Pode ser usado mesmo para selecionar grupos de colunas com base no 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')

Assim, conforme declarado nos 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

Podemos pensar que aqui vemos os primeiros resultados inesperados (costumava ser para mim: pergunta ) - TimeDeltasão incluídos na saída DataFrame. Mas, como respondido em contrário, deve ser assim, mas é preciso estar ciente disso. Observe que o booldtype é ignorado, o que também pode ser indesejável para alguém, mas é devido a boole numberestá em " subárvores " diferentes de tipos numpy. No caso de bool, podemos usar test.select_dtypes(['bool'])aqui.

A próxima restrição desse método é que, para a versão atual do pandas (0.24.2), esse código: test.select_dtypes('period')será gerado NotImplementedError.

E outra coisa é que não é possível diferenciar strings de outros objetos:

test.select_dtypes('object')

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

Mas este é o primeiro - já mencionado na documentação. E segundo - não é o problema desse método, mas a maneira como as strings são armazenadas DataFrame. Mas de qualquer maneira, este caso precisa ter algum pós-processamento.

5. df.api.types.is_XXX_dtypeabordagem.

Este pretende ser a maneira mais robusta e nativa de obter reconhecimento de tipo (caminho do módulo em que as funções residem diz por si só), como suponho. E funciona quase perfeitamente, mas ainda tem pelo menos uma ressalva e ainda precisa distinguir de alguma forma as colunas de string .

Além disso, isso pode ser subjetivo, mas essa abordagem também possui um numberprocessamento de grupo de tipos de tipo 'compreensível ao ser humano' comparado com .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

Não timedeltae boolestá incluído. Perfeito.

Meu pipeline explora exatamente essa funcionalidade nesse momento, além de um pouco de processamento pós-mão.

Resultado.

Espero ter conseguido argumentar sobre o ponto principal - que todas as abordagens discutidas possam ser usadas, mas apenas pd.DataFrame.select_dtypes()e pd.api.types.is_XXX_dtypedevem ser realmente consideradas como as aplicáveis.

BeforeFlight
fonte
1
Resposta ótima e bem formulada. :-)
Oliver
7

Se você deseja marcar o tipo de uma coluna de quadro de dados como uma sequência, você pode:

df['A'].dtype.kind

Um exemplo:

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

A resposta para o seu 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
fonte
4

Para imprimir os tipos de dados da coluna

Para verificar os tipos de dados após, por exemplo, uma importação de um arquivo

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

Saída ilustrativa:

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