Atribuir dtypes de coluna de dataframe ao pandas

110

Quero definir os dtypes de várias colunas em pd.Dataframe(tenho um arquivo que tive que analisar manualmente em uma lista de listas, pois o arquivo não era acessível pd.read_csv)

import pandas as pd
print pd.DataFrame([['a','1'],['b','2']],
                   dtype={'x':'object','y':'int'},
                   columns=['x','y'])

eu recebo

ValueError: entry not a 2- or 3- tuple

A única maneira de defini-los é percorrendo cada variável de coluna e reformulando com astype.

dtypes = {'x':'object','y':'int'}
mydata = pd.DataFrame([['a','1'],['b','2']],
                      columns=['x','y'])
for c in mydata.columns:
    mydata[c] = mydata[c].astype(dtypes[c])
print mydata['y'].dtype   #=> int64

Existe uma maneira melhor?

hatmatriz
fonte
Talvez seja uma boa solicitação de bug / recurso , atualmente não tenho certeza do que dtype arg está fazendo (você pode passar um escalar, mas não é estrito) ...
Andy Hayden
2
Para sua informação: df = pd.DataFrame([['a','1'],['b','2']], dtype='int', columns=['x','y'])"funciona" ... mas: s
Andy Hayden
1
Sim, "funciona" de fato; imprevisivelmente ...
hatmatrix
Este problema do GitHub pode se tornar relevante em breve: github.com/pydata/pandas/issues/9287
Amelio Vazquez-Reina

Respostas:

65

Desde 0,17, você deve usar as conversões explícitas:

pd.to_datetime, pd.to_timedelta and pd.to_numeric

(Como mencionado abaixo, não há mais "mágica", convert_objectsfoi descontinuado em 0.17)

df = pd.DataFrame({'x': {0: 'a', 1: 'b'}, 'y': {0: '1', 1: '2'}, 'z': {0: '2018-05-01', 1: '2018-05-02'}})

df.dtypes

x    object
y    object
z    object
dtype: object

df

   x  y           z
0  a  1  2018-05-01
1  b  2  2018-05-02

Você pode aplicá-los a cada coluna que deseja converter:

df["y"] = pd.to_numeric(df["y"])
df["z"] = pd.to_datetime(df["z"])    
df

   x  y          z
0  a  1 2018-05-01
1  b  2 2018-05-02

df.dtypes

x            object
y             int64
z    datetime64[ns]
dtype: object

e confirme se o dtype está atualizado.


RESPOSTA ANTIGA / DESCONTINUADA para pandas 0,12 - 0,16: Você pode usar convert_objectspara inferir melhores dtipos:

In [21]: df
Out[21]: 
   x  y
0  a  1
1  b  2

In [22]: df.dtypes
Out[22]: 
x    object
y    object
dtype: object

In [23]: df.convert_objects(convert_numeric=True)
Out[23]: 
   x  y
0  a  1
1  b  2

In [24]: df.convert_objects(convert_numeric=True).dtypes
Out[24]: 
x    object
y     int64
dtype: object

Magia! (É triste ver seu uso suspenso.)

Andy Hayden
fonte
2
como type.convertem R um pouco; bom, mas deixa alguém desejando especificações explícitas em alguns casos.
hatmatrix
1
Tenha cuidado se você tiver uma coluna que precisa ser uma string, mas contém pelo menos um valor que pode ser convertido em um int. Basta um valor e todo o campo é convertido em float64
Michael David Watson
18
Percebi convert_objects()que foi descontinuado ... não tenho certeza do que o substituiu?
joefromct
6
Para inferir novamente os tipos de dados para colunas de objeto, use DataFrame.infer_objects ()
James Tobin
1
@smci ok, eu editei. Há um monte de respostas obsoletas, preciso descobrir uma maneira de encontrar todas elas.
Andy Hayden
62

Para aqueles que vêm do Google (etc.) como eu:

convert_objects está obsoleto desde 0.17 - se você usá-lo, receberá um aviso como este:

FutureWarning: convert_objects is deprecated.  Use the data-type specific converters 
pd.to_datetime, pd.to_timedelta and pd.to_numeric.

Você deve fazer algo como o seguinte:

Jack Yates
fonte
Se você deu alguns exemplos, pd.to_datetime, to_timedelta, to_numericessa deve ser a resposta aceita.
smci
41

você pode definir os tipos explicitamente com os pandas DataFrame.astype(dtype, copy=True, raise_on_error=True, **kwargs)e passar em um dicionário com os tipos que você desejadtype

aqui está um exemplo:

import pandas as pd
wheel_number = 5
car_name = 'jeep'
minutes_spent = 4.5

# set the columns
data_columns = ['wheel_number', 'car_name', 'minutes_spent']

# create an empty dataframe
data_df = pd.DataFrame(columns = data_columns)
df_temp = pd.DataFrame([[wheel_number, car_name, minutes_spent]],columns = data_columns)
data_df = data_df.append(df_temp, ignore_index=True) 

In [11]: data_df.dtypes
Out[11]:
wheel_number     float64
car_name          object
minutes_spent    float64
dtype: object

data_df = data_df.astype(dtype= {"wheel_number":"int64",
        "car_name":"object","minutes_spent":"float64"})

agora você pode ver que mudou

In [18]: data_df.dtypes
Out[18]:
wheel_number       int64
car_name          object
minutes_spent    float64
Lauren
fonte
13

Outra maneira de definir os tipos de coluna é primeiro construir uma matriz de registros numpy com os tipos desejados, preenchê-la e depois passá-la para um construtor DataFrame.

import pandas as pd
import numpy as np    

x = np.empty((10,), dtype=[('x', np.uint8), ('y', np.float64)])
df = pd.DataFrame(x)

df.dtypes ->

x      uint8
y    float64
Kaushik Ghose
fonte
0

enfrentando problema semelhante a você. No meu caso, tenho 1000 arquivos de logs da Cisco que preciso analisar manualmente.

Para ser flexível com campos e tipos, testei com sucesso usando StringIO + read_cvs que de fato aceita um dict para a especificação dtype.

Normalmente coloco cada um dos arquivos (5k-20k linhas) em um buffer e crio os dicionários dtype dinamicamente.

Eventualmente concateno (com categóricos ... graças ao 0,19) esses dataframes em um grande frame de dados que despejo em hdf5.

Algo nesse sentido

import pandas as pd
import io 

output = io.StringIO()
output.write('A,1,20,31\n')
output.write('B,2,21,32\n')
output.write('C,3,22,33\n')
output.write('D,4,23,34\n')

output.seek(0)


df=pd.read_csv(output, header=None,
        names=["A","B","C","D"],
        dtype={"A":"category","B":"float32","C":"int32","D":"float64"},
        sep=","
       )

df.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 4 columns):
A    5 non-null category
B    5 non-null float32
C    5 non-null int32
D    5 non-null float64
dtypes: category(1), float32(1), float64(1), int32(1)
memory usage: 205.0 bytes
None

Não é muito python .... mas faz o trabalho

Espero que ajude.

JC

Julian C
fonte
0

É melhor usar np.arrays digitado e, em seguida, passar os dados e os nomes das colunas como um dicionário.

import numpy as np
import pandas as pd
# Feature: np arrays are 1: efficient, 2: can be pre-sized
x = np.array(['a', 'b'], dtype=object)
y = np.array([ 1 ,  2 ], dtype=np.int32)
df = pd.DataFrame({
   'x' : x,    # Feature: column name is near data array
   'y' : y,
   }
 )
Clem Wang
fonte