Alterar o tipo de dados das colunas no Pandas


806

Quero converter uma tabela, representada como uma lista de listas, em um Pandas DataFrame. Como um exemplo extremamente simplificado:

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a)

Qual é a melhor maneira de converter as colunas nos tipos apropriados, neste caso as colunas 2 e 3 em flutuadores? Existe uma maneira de especificar os tipos durante a conversão para o DataFrame? Ou é melhor criar o DataFrame primeiro e depois percorrer as colunas para alterar o tipo de cada coluna? Idealmente, eu gostaria de fazer isso de uma maneira dinâmica, porque pode haver centenas de colunas e não quero especificar exatamente quais colunas são de qual tipo. Tudo o que posso garantir é que cada coluna contenha valores do mesmo tipo.


Eu já vi abordagens para converter todas as colunas e abordagens para converter colunas nomeadas especificamente, mas e quanto a determinadas colunas que atendem a uma determinada condição quando você não pode listar 100 colunas que deseja converter de uma só vez? Estou pensando, por exemplo, em todas as float64 -> float32 ou outras táticas de economia de memória.
demongolem 25/03

@ demongolem: você pode fazer algo como df.apply(pd.to_numeric, downcast="integer", errors="ignore")fazer o downcast de colunas inteiras para o menor (inteiro) dtype que conterá os valores.
Alex Riley

Respostas:


1193

Você tem três opções principais para converter tipos em pandas:

  1. to_numeric()- fornece funcionalidade para converter com segurança tipos não numéricos (por exemplo, strings) em um tipo numérico adequado. (Veja também to_datetime()e to_timedelta().)

  2. astype()- converta (quase) qualquer tipo para (quase) qualquer outro tipo (mesmo que não seja necessariamente sensato fazê-lo). Também permite converter para tipos categoriais (muito útil).

  3. infer_objects() - um método utilitário para converter colunas de objetos contendo objetos Python em um tipo de panda, se possível.

Continue lendo para obter explicações mais detalhadas e o uso de cada um desses métodos.


1 to_numeric()

A melhor maneira de converter uma ou mais colunas de um DataFrame em valores numéricos é usar pandas.to_numeric() .

Esta função tentará alterar objetos não numéricos (como seqüências de caracteres) em números inteiros ou números de ponto flutuante, conforme apropriado.

Uso básico

A entrada para to_numeric()é uma série ou uma única coluna de um DataFrame.

>>> s = pd.Series(["8", 6, "7.5", 3, "0.9"]) # mixed string and numeric values
>>> s
0      8
1      6
2    7.5
3      3
4    0.9
dtype: object

>>> pd.to_numeric(s) # convert everything to float values
0    8.0
1    6.0
2    7.5
3    3.0
4    0.9
dtype: float64

Como você pode ver, uma nova série é retornada. Lembre-se de atribuir esta saída a um nome de variável ou coluna para continuar usando:

# convert Series
my_series = pd.to_numeric(my_series)

# convert column "a" of a DataFrame
df["a"] = pd.to_numeric(df["a"])

Você também pode usá-lo para converter várias colunas de um DataFrame por meio do apply()método:

# convert all columns of DataFrame
df = df.apply(pd.to_numeric) # convert all columns of DataFrame

# convert just columns "a" and "b"
df[["a", "b"]] = df[["a", "b"]].apply(pd.to_numeric)

Desde que todos os seus valores possam ser convertidos, é provavelmente tudo o que você precisa.

Manipulação de erros

Mas e se alguns valores não puderem ser convertidos em um tipo numérico?

to_numeric()também usa um errorsargumento de palavra - chave que permite forçar a inclusão de valores não numéricos NaNou simplesmente ignorar as colunas que contêm esses valores.

Aqui está um exemplo usando uma série de strings sque possui o objeto dtype:

>>> s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
>>> s
0         1
1         2
2       4.7
3    pandas
4        10
dtype: object

O comportamento padrão é aumentar se não puder converter um valor. Nesse caso, ele não pode lidar com a string 'pandas':

>>> pd.to_numeric(s) # or pd.to_numeric(s, errors='raise')
ValueError: Unable to parse string

Em vez de falhar, podemos querer que 'pandas' sejam considerados um valor numérico ausente / ruim. Podemos coagir valores inválidos da NaNseguinte maneira usando o errorsargumento de palavra - chave:

>>> pd.to_numeric(s, errors='coerce')
0     1.0
1     2.0
2     4.7
3     NaN
4    10.0
dtype: float64

A terceira opção para errorsé apenas ignorar a operação se um valor inválido for encontrado:

>>> pd.to_numeric(s, errors='ignore')
# the original Series is returned untouched

Essa última opção é particularmente útil quando você deseja converter todo o seu DataFrame, mas não sabe quais de nossas colunas podem ser convertidas de maneira confiável em um tipo numérico. Nesse caso, basta escrever:

df.apply(pd.to_numeric, errors='ignore')

A função será aplicada a cada coluna do DataFrame. As colunas que podem ser convertidas em um tipo numérico serão convertidas, enquanto as colunas que não puderem (por exemplo, que contêm strings ou datas que não sejam dígitos) serão deixadas em paz.

Downcasting

Por padrão, a conversão com to_numeric()fornecerá um int64oufloat64 dtype (ou qualquer largura inteira nativa da sua plataforma).

Isso é geralmente o que você deseja, mas e se você quiser economizar um pouco de memória e usar um tipo mais compacto, como float32ouint8 ?

to_numeric()oferece a opção de fazer downcast para 'número inteiro', 'assinado', 'não assinado', 'flutuante'. Aqui está um exemplo para uma série simples sde tipo inteiro:

>>> s = pd.Series([1, 2, -7])
>>> s
0    1
1    2
2   -7
dtype: int64

Downcasting para 'número inteiro' usa o menor número possível possível que pode conter os valores:

>>> pd.to_numeric(s, downcast='integer')
0    1
1    2
2   -7
dtype: int8

O downcasting para 'float' escolhe de maneira semelhante um tipo flutuante menor que o normal:

>>> pd.to_numeric(s, downcast='float')
0    1.0
1    2.0
2   -7.0
dtype: float32

2) astype()

O astype()método permite que você seja explícito sobre o tipo que você deseja que seu DataFrame ou Series tenha. É muito versátil, pois você pode tentar ir de um tipo para outro.

Uso básico

Basta escolher um tipo: você pode usar um tipo NumPy (por exemplo np.int16), alguns tipos Python (por exemplo, bool) ou tipos específicos de pandas (como o tipo categórico).

Chame o método no objeto que você deseja converter e astype()tentará convertê-lo para você:

# convert all DataFrame columns to the int64 dtype
df = df.astype(int)

# convert column "a" to int64 dtype and "b" to complex type
df = df.astype({"a": int, "b": complex})

# convert Series to float16 type
s = s.astype(np.float16)

# convert Series to Python strings
s = s.astype(str)

# convert Series to categorical type - see docs for more details
s = s.astype('category')

Observe que eu disse "tente" - se astype()não souber converter um valor na série ou no DataFrame, isso gerará um erro. Por exemplo, se você tiver um valor NaNou, infreceberá um erro ao tentar convertê-lo em um número inteiro.

No pandas 0.20.0, esse erro pode ser suprimido passando errors='ignore' . Seu objeto original será devolvido intocado.

Seja cuidadoso

astype()é poderoso, mas às vezes converte valores "incorretamente". Por exemplo:

>>> s = pd.Series([1, 2, -7])
>>> s
0    1
1    2
2   -7
dtype: int64

Estes são números inteiros pequenos. Então, que tal converter para um tipo de 8 bits não assinado para economizar memória?

>>> s.astype(np.uint8)
0      1
1      2
2    249
dtype: uint8

A conversão funcionou, mas o -7 foi arredondado para se tornar 249 (ou seja, 2 8 - 7)!

Tentar fazer o downcast usando pd.to_numeric(s, downcast='unsigned')pode ajudar a evitar esse erro.


3) infer_objects()

A versão 0.21.0 do pandas introduziu o método infer_objects()para converter colunas de um DataFrame que possuem um tipo de dados de objeto em um tipo mais específico (conversões flexíveis).

Por exemplo, aqui está um DataFrame com duas colunas do tipo de objeto. Um contém inteiros reais e o outro contém cadeias que representam números inteiros:

>>> df = pd.DataFrame({'a': [7, 1, 5], 'b': ['3','2','1']}, dtype='object')
>>> df.dtypes
a    object
b    object
dtype: object

Usando infer_objects(), você pode alterar o tipo de coluna 'a' para int64:

>>> df = df.infer_objects()
>>> df.dtypes
a     int64
b    object
dtype: object

A coluna 'b' foi deixada sozinha, pois seus valores eram cadeias de caracteres, não números inteiros. Se você quiser tentar forçar a conversão de ambas as colunas em um tipo inteiro, use-o df.astype(int).


8
Além disso, diferentemente de .astype (float), isso converterá seqüências de caracteres em NaNs em vez de gerar um erro
Rob

11
.convert_objectsestá obsoleto desde 0.17- use em df.to_numericvez disso
Matti Lyra

4
Obrigado - eu devo atualizar esta resposta. Talvez valha a pena notar isso pd.to_numerice seus métodos complementares funcionarão apenas em uma coluna por vez, ao contrário convert_objects. A discussão sobre uma função de substituição na API parece estar em andamento ; Espero que um método que funcione em todo o DataFrame permaneça porque é muito útil.
Alex Riley

Qual é a melhor maneira de converter todas as colunas que estão atualmente, digamos, int64em int32?
RoyalTS 23/09/16

4
@RoyalTS: provavelmente o melhor para usar astype(como na outra resposta), ie .astype(numpy.int32).
Alex Riley

447

Que tal agora?

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])
df
Out[16]: 
  one  two three
0   a  1.2   4.2
1   b   70  0.03
2   x    5     0

df.dtypes
Out[17]: 
one      object
two      object
three    object

df[['two', 'three']] = df[['two', 'three']].astype(float)

df.dtypes
Out[19]: 
one       object
two      float64
three    float64

10
Sim! pd.DataFrametem um dtypeargumento que pode permitir que você faça o que você está procurando. df = pd.DataFrame (a, colunas = ['um', 'dois', 'três'], dtype = float) Em [2]: df.dtypes Out [2]: um objeto dois float64 três float64 dtype: object
Hernamesbarbara #

17
Quando tento como sugerido, recebo um aviso SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_index,col_indexer] = value instead. Isso pode ter sido introduzido em uma versão mais recente do pandas e, como resultado, não vejo nada de errado, mas me pergunto o que é esse aviso. Qualquer ideia?
laranja

2
@orange o aviso é alertar os usuários sobre comportamentos potencialmente confusos com operações encadeadas e com os pandas retornando cópias em vez de editar quadros de dados. consulte stackoverflow.com/questions/20625582/… e relacionados.
A.Wan

19
Esse é um bom método, mas não funciona quando há NaN em uma coluna. Não tenho idéia por que NaN simplesmente não pode permanecer NaN ao lançar float para int: #ValueError: Cannot convert NA to integer
Vitaly Isaev

7
@GillBates sim, em um dicionário. df = pd.DataFrame(a, columns=['one', 'two', 'three'], dtype={'one': str, 'two': int, 'three': float}). Estou tendo dificuldade em encontrar a especificação para os valores "dtype" aceitos. Uma lista seria legal (atualmente eu faço dict(enumerate(my_list))).
FichteFoll 07/07

39

este código abaixo mudará o tipo de dados da coluna.

df[['col.name1', 'col.name2'...]] = df[['col.name1', 'col.name2'..]].astype('data_type')

no lugar do tipo de dados, você pode fornecer ao seu tipo de dados o que você deseja como str, float, int etc.


Lembre-se de que, ao aplicar isso em uma coluna que contém as cadeias `` `` True '' `` `` e `` `` False '' `` `usando o data_type bool, tudo é alterado para True.
H. Vabri

Esta opção também pode ser convertida para o tipo "categoria"
neves 22/09

17

Quando só preciso especificar colunas específicas e quero ser explícito, usei (por DOCS LOCATION ):

dataframe = dataframe.astype({'col_name_1':'int','col_name_2':'float64', etc. ...})

Então, usando a pergunta original, mas fornecendo nomes de colunas para ela ...

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['col_name_1', 'col_name_2', 'col_name_3'])
df = df.astype({'col_name_2':'float64', 'col_name_3':'float64'})

15

Aqui está uma função que usa como argumento um DataFrame e uma lista de colunas e coage todos os dados nas colunas para números.

# df is the DataFrame, and column_list is a list of columns as strings (e.g ["col1","col2","col3"])
# dependencies: pandas

def coerce_df_columns_to_numeric(df, column_list):
    df[column_list] = df[column_list].apply(pd.to_numeric, errors='coerce')

Então, para o seu exemplo:

import pandas as pd

def coerce_df_columns_to_numeric(df, column_list):
    df[column_list] = df[column_list].apply(pd.to_numeric, errors='coerce')

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['col1','col2','col3'])

coerce_df_columns_to_numeric(df, ['col2','col3'])

e se você quisesse usar índices de coluna em vez de nomes de coluna?
jvalenti

8

Que tal criar dois quadros de dados, cada um com diferentes tipos de dados para suas colunas e anexá-los juntos?

d1 = pd.DataFrame(columns=[ 'float_column' ], dtype=float)
d1 = d1.append(pd.DataFrame(columns=[ 'string_column' ], dtype=str))

Resultados

In[8}:  d1.dtypes
Out[8]: 
float_column     float64
string_column     object
dtype: object

Após a criação do quadro de dados, é possível preenchê-lo com variáveis ​​de ponto flutuante na 1ª coluna e seqüências de caracteres (ou qualquer tipo de dados desejado) na 2ª coluna.


4

pandas> = 1.0

Aqui está um gráfico que resume algumas das conversões mais importantes em pandas.

insira a descrição da imagem aqui

Conversões para string são triviais .astype(str)e não são mostradas na figura.

Conversões "rígidas" versus "flexíveis"

Observe que "conversões" nesse contexto pode se referir à conversão de dados de texto em seu tipo de dados real (conversão rígida) ou inferir tipos de dados mais apropriados para dados em colunas de objetos (conversão suave). Para ilustrar a diferença, dê uma olhada

df = pd.DataFrame({'a': ['1', '2', '3'], 'b': [4, 5, 6]}, dtype=object)
df.dtypes                                                                  

a    object
b    object
dtype: object

# Actually converts string to numeric - hard conversion
df.apply(pd.to_numeric).dtypes                                             

a    int64
b    int64
dtype: object

# Infers better data types for object data - soft conversion
df.infer_objects().dtypes                                                  

a    object  # no change
b     int64
dtype: object

# Same as infer_objects, but converts to equivalent ExtensionType
df.convert_dtypes().dtypes                                                     

1

Eu pensei que tinha o mesmo problema, mas na verdade tenho uma pequena diferença que facilita a solução do problema. Para outras pessoas que olham para essa pergunta, vale a pena verificar o formato da sua lista de entradas. No meu caso, os números são inicialmente flutuadores e não cordas, como na pergunta:

a = [['a', 1.2, 4.2], ['b', 70, 0.03], ['x', 5, 0]]

mas processando muito a lista antes de criar o quadro de dados, perco os tipos e tudo se torna uma string.

Criando o quadro de dados por meio de uma matriz numpy

df = pd.DataFrame(np.array(a))

df
Out[5]: 
   0    1     2
0  a  1.2   4.2
1  b   70  0.03
2  x    5     0

df[1].dtype
Out[7]: dtype('O')

fornece o mesmo quadro de dados da pergunta, em que as entradas nas colunas 1 e 2 são consideradas como cadeias. No entanto fazendo

df = pd.DataFrame(a)

df
Out[10]: 
   0     1     2
0  a   1.2  4.20
1  b  70.0  0.03
2  x   5.0  0.00

df[1].dtype
Out[11]: dtype('float64')

na verdade, fornece um quadro de dados com as colunas no formato correto


0

A partir do pandas 1.0.0, temos pandas.DataFrame.convert_dtypes. Você pode até controlar quais tipos converter!

In [40]: df = pd.DataFrame(
    ...:     {
    ...:         "a": pd.Series([1, 2, 3], dtype=np.dtype("int32")),
    ...:         "b": pd.Series(["x", "y", "z"], dtype=np.dtype("O")),
    ...:         "c": pd.Series([True, False, np.nan], dtype=np.dtype("O")),
    ...:         "d": pd.Series(["h", "i", np.nan], dtype=np.dtype("O")),
    ...:         "e": pd.Series([10, np.nan, 20], dtype=np.dtype("float")),
    ...:         "f": pd.Series([np.nan, 100.5, 200], dtype=np.dtype("float")),
    ...:     }
    ...: )

In [41]: dff = df.copy()

In [42]: df 
Out[42]: 
   a  b      c    d     e      f
0  1  x   True    h  10.0    NaN
1  2  y  False    i   NaN  100.5
2  3  z    NaN  NaN  20.0  200.0

In [43]: df.dtypes
Out[43]: 
a      int32
b     object
c     object
d     object
e    float64
f    float64
dtype: object

In [44]: df = df.convert_dtypes()

In [45]: df.dtypes
Out[45]: 
a      Int32
b     string
c    boolean
d     string
e      Int64
f    float64
dtype: object

In [46]: dff = dff.convert_dtypes(convert_boolean = False)

In [47]: dff.dtypes
Out[47]: 
a      Int32
b     string
c     object
d     string
e      Int64
f    float64
dtype: object

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.