Asignar tipos de columnas de marcos de datos de pandas

110

Quiero establecer la dtypes de varias columnas en pd.Dataframe(tengo un archivo que tuve que analizar manualmente en una lista de listas, ya que el archivo no era apto para pd.read_csv)

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

yo obtengo

ValueError: entry not a 2- or 3- tuple

La única forma en que puedo configurarlos es recorriendo cada variable de columna y volviendo a lanzar con 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 una forma mejor?

hatmatrix
fuente
Esta sería quizás una buena solicitud de error / función , actualmente no estoy seguro de qué está haciendo dtype arg (puede pasarle un escalar, pero no es estricto) ...
Andy Hayden
2
FYI: df = pd.DataFrame([['a','1'],['b','2']], dtype='int', columns=['x','y'])"funciona" ... pero: s
Andy Hayden
1
Sí, "funciona" de hecho; impredeciblemente ...
hatmatrix
Este problema de GitHub pronto puede volverse relevante: github.com/pydata/pandas/issues/9287
Amelio Vazquez-Reina

Respuestas:

65

Desde 0.17, debe usar las conversiones explícitas:

pd.to_datetime, pd.to_timedelta and pd.to_numeric

(Como se menciona a continuación, no más "magia", convert_objectsse ha desaprobado en 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

Puede aplicarlos a cada columna que desee convertir:

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

y confirme que el dtype esté actualizado.


RESPUESTA ANTIGUA / DESAPARECIDA para pandas 0.12 - 0.16: Puede usar convert_objectspara inferir mejores dtypes:

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! (Es triste verlo obsoleto).

Andy Hayden
fuente
2
como type.converten R un poco; agradable, pero deja a uno deseando especificaciones explícitas en algunos casos.
hatmatrix
1
Tenga cuidado si tiene una columna que debe ser una cadena, pero contiene al menos un valor que podría convertirse en un int. Todo lo que se necesita es un valor y todo el campo se convierte a float64
Michael David Watson
18
Noté que convert_objects()ha quedado obsoleto ... no estoy seguro de qué lo reemplazó?
joe desde el
6
Para volver a inferir tipos de datos para columnas de objetos, use DataFrame.infer_objects ()
James Tobin
1
@smci okay, lo he editado. Hay un montón de respuestas obsoletas, necesito encontrar una manera de encontrarlas todas.
Andy Hayden
62

Para aquellos que vienen de Google (etc.) como yo:

convert_objects ha quedado obsoleto desde 0.17; si lo usa, recibirá una advertencia como esta:

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

Deberías hacer algo como lo siguiente:

Jack Yates
fuente
Si arrojó algunos ejemplos de pd.to_datetime, to_timedelta, to_numericesto, debería ser la respuesta aceptada.
smci
41

puede establecer los tipos explícitamente con pandas DataFrame.astype(dtype, copy=True, raise_on_error=True, **kwargs)y pasar en un diccionario con los dtypes que deseedtype

aquí hay un ejemplo:

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"})

ahora puedes ver que ha cambiado

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

Otra forma de establecer los tipos de columna es construir primero una matriz de registros numpy con los tipos deseados, completarla y luego pasarla a un constructor de 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
fuente
0

enfrenta un problema similar al suyo. En mi caso, tengo miles de archivos de los registros de Cisco que necesito analizar manualmente.

Para ser flexible con los campos y tipos, he probado con éxito utilizando StringIO + read_cvs, que de hecho acepta un dict para la especificación dtype.

Normalmente obtengo cada uno de los archivos (5k-20k líneas) en un búfer y creo los diccionarios dtype de forma dinámica.

Eventualmente concatené (con categórico ... gracias a 0.19) estos marcos de datos en un marco de datos grande que volco en hdf5.

Algo en estas líneas

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

No es muy pitónico ... pero hace el trabajo

Espero eso ayude.

JC

Julián C
fuente
0

Es mejor usar np.arrays escritos y luego pasar los datos y los nombres de las columnas como un diccionario.

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
fuente