Cambiar tipo de columna en pandas
Creé un DataFrame a partir de una lista de listas:
table = [
['a', '1.2', '4.2' ],
['b', '70', '0.03'],
['x', '5', '0' ],
]
df = pd.DataFrame(table)
¿Cómo convierto las columnas a tipos específicos? En este caso, quiero convertir las columnas 2 y 3 en flotantes.
¿Hay alguna manera de especificar los tipos al convertir la lista a DataFrame? ¿O es mejor crear el DataFrame primero y luego recorrer las columnas para cambiar el tipo de cada columna? Idealmente, me gustaría hacer esto de forma dinámica porque puede haber cientos de columnas y no quiero especificar exactamente qué columnas son de qué tipo. Todo lo que puedo garantizar es que cada columna contiene valores del mismo tipo.
Tienes cuatro opciones principales para convertir tipos en pandas:
to_numeric()
- proporciona funcionalidad para convertir de forma segura tipos no numéricos (por ejemplo, cadenas) a un tipo numérico adecuado. (Ver tambiénto_datetime()
yto_timedelta()
.)astype()
- convertir (casi) cualquier tipo a (casi) cualquier otro tipo (incluso si no es necesariamente sensato hacerlo). También te permite convertir a tipos categoriales (muy útil).infer_objects()
- un método de utilidad para convertir columnas de objetos que contienen objetos Python a un tipo pandas, si es posible.convert_dtypes()
- convertir columnas de DataFrame al "mejor tipo de formato posible" que admitapd.NA
(objeto de pandas para indicar un valor faltante).
Continúe leyendo para obtener explicaciones más detalladas y el uso de cada uno de estos métodos.
1.to_numeric()
La mejor manera de convertir una o más columnas de un DataFrame a valores numéricos es utilizar pandas.to_numeric()
.
Esta función intentará cambiar objetos no numéricos (como cadenas) a números enteros o de punto flotante, según corresponda.
Uso básico
La entrada to_numeric()
es una serie o una sola columna de un 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 puede ver, se devuelve una nueva Serie. Recuerde asignar esta salida a un nombre de variable o columna para continuar usándola:
# convert Series
my_series = pd.to_numeric(my_series)
# convert column "a" of a DataFrame
df["a"] = pd.to_numeric(df["a"])
También puedes usarlo para convertir múltiples columnas de un DataFrame mediante el 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)
Siempre que todos sus valores se puedan convertir, probablemente eso sea todo lo que necesite.
Manejo de errores
Pero ¿qué pasa si algunos valores no se pueden convertir a un tipo numérico?
to_numeric()
también toma un errors
argumento de palabra clave que le permite forzar que los valores no numéricos sean NaN
, o simplemente ignorar las columnas que contienen estos valores.
A continuación se muestra un ejemplo que utiliza una serie de cadenas s
que tiene el tipo de objeto:
>>> s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
>>> s
0 1
1 2
2 4.7
3 pandas
4 10
dtype: object
El comportamiento predeterminado es aumentar si no puede convertir un valor. En este caso, no puede manejar la cadena 'pandas':
>>> pd.to_numeric(s) # or pd.to_numeric(s, errors='raise')
ValueError: Unable to parse string
En lugar de fallar, es posible que deseemos que 'pandas' se considere un valor numérico faltante o incorrecto. Podemos forzar valores no válidos de NaN
la siguiente manera usando el errors
argumento de palabra clave:
>>> pd.to_numeric(s, errors='coerce')
0 1.0
1 2.0
2 4.7
3 NaN
4 10.0
dtype: float64
La tercera opción errors
es simplemente ignorar la operación si se encuentra un valor no válido:
>>> pd.to_numeric(s, errors='ignore')
# the original Series is returned untouched
Esta última opción es particularmente útil para convertir todo su DataFrame, pero no sabe cuál de nuestras columnas se puede convertir de manera confiable a un tipo numérico. En ese caso, simplemente escribe:
df.apply(pd.to_numeric, errors='ignore')
La función se aplicará a cada columna del DataFrame. Las columnas que se puedan convertir a un tipo numérico se convertirán, mientras que las columnas que no se puedan convertir (por ejemplo, que contengan cadenas sin dígitos o fechas) se dejarán como están.
abatido
De forma predeterminada, la conversión con to_numeric()
le dará un tipo int64
o float64
d (o cualquier ancho entero que sea nativo de su plataforma).
Normalmente eso es lo que quieres, pero ¿qué pasaría si quisieras ahorrar algo de memoria y usar un tipo de letra más compacto, como float32
o int8
?
to_numeric()
le da la opción de abatir a 'integer'
, 'signed'
, 'unsigned'
, 'float'
. A continuación se muestra un ejemplo de una serie simple s
de tipo entero:
>>> s = pd.Series([1, 2, -7])
>>> s
0 1
1 2
2 -7
dtype: int64
Downcasting 'integer'
utiliza el entero más pequeño posible que puede contener los valores:
>>> pd.to_numeric(s, downcast='integer')
0 1
1 2
2 -7
dtype: int8
Downcasting para 'float'
elegir de manera similar un tipo flotante más pequeño de lo normal:
>>> pd.to_numeric(s, downcast='float')
0 1.0
1 2.0
2 -7.0
dtype: float32
2.astype()
El astype()
método le permite ser explícito sobre el tipo de datos que desea que tenga su DataFrame o Serie. Es muy versátil porque puedes probar y pasar de un tipo a otro.
Uso básico
Simplemente elija un tipo: puede usar un tipo de NumPy (p. ej. np.int16
), algunos tipos de Python (p. ej., bool) o tipos específicos de pandas (como el tipo de d categórico).
Llame al método en el objeto que desea convertir e astype()
intentará convertirlo por usted:
# 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 dije "intentar": si astype()
no sabe cómo convertir un valor en la serie o el marco de datos, generará un error. Por ejemplo, si tiene un valor NaN
o inf
obtendrá un error al intentar convertirlo a un número entero.
A partir de pandas 0.20.0, este error se puede suprimir pasando errors='ignore'
. Su objeto original será devuelto intacto.
Ten cuidado
astype()
es potente, pero a veces convierte valores "incorrectamente". Por ejemplo:
>>> s = pd.Series([1, 2, -7])
>>> s
0 1
1 2
2 -7
dtype: int64
Estos son números enteros pequeños, entonces, ¿qué tal si los convertimos a un tipo de 8 bits sin signo para ahorrar memoria?
>>> s.astype(np.uint8)
0 1
1 2
2 249
dtype: uint8
¡La conversión funcionó, pero el -7 se transformó en 249 (es decir, 2 8 - 7)!
Intentar abatir usando pd.to_numeric(s, downcast='unsigned')
en su lugar podría ayudar a prevenir este error.
3.infer_objects()
La versión 0.21.0 de pandas introdujo el método infer_objects()
para convertir columnas de un DataFrame que tienen un tipo de datos de objeto a un tipo más específico (conversiones suaves).
Por ejemplo, aquí hay un DataFrame con dos columnas de tipo de objeto. Uno contiene números enteros reales y el otro contiene cadenas que representan números enteros:
>>> df = pd.DataFrame({'a': [7, 1, 5], 'b': ['3','2','1']}, dtype='object')
>>> df.dtypes
a object
b object
dtype: object
Usando infer_objects()
, puedes cambiar el tipo de columna 'a' a int64:
>>> df = df.infer_objects()
>>> df.dtypes
a int64
b object
dtype: object
La columna 'b' se ha dejado sola ya que sus valores eran cadenas, no números enteros. Si quisiera forzar ambas columnas a un tipo de número entero, podría usarlas df.astype(int)
en su lugar.
4.convert_dtypes()
La versión 1.0 y superiores incluyen un método convert_dtypes()
para convertir columnas Series y DataFrame al mejor tipo de formato posible que admita el pd.NA
valor faltante.
Aquí "mejor posible" significa el tipo más adecuado para contener los valores. Por ejemplo, este es un tipo entero de pandas, si todos los valores son enteros (o valores faltantes): una columna de objeto de objetos enteros de Python se convierte en Int64
, una columna de valores NumPy int32
, se convertirá en el tipo de pandas Int32
.
Con nuestro object
DataFrame df
, obtenemos el siguiente resultado:
>>> df.convert_dtypes().dtypes
a Int64
b string
dtype: object
Dado que la columna 'a' contenía valores enteros, se convirtió al Int64
tipo (que es capaz de contener valores faltantes, a diferencia de int64
).
La columna 'b' contenía objetos de cadena, por lo que se cambió al string
tipo de pandas.
De forma predeterminada, este método inferirá el tipo a partir de los valores del objeto en cada columna. Podemos cambiar esto pasando infer_objects=False
:
>>> df.convert_dtypes(infer_objects=False).dtypes
a object
b string
dtype: object
Ahora la columna 'a' sigue siendo una columna de objeto: pandas sabe que puede describirse como una columna 'entera' (internamente se ejecutó infer_dtype
) pero no infirió exactamente qué tipo de entero debería tener, por lo que no la convirtió. La columna 'b' se convirtió nuevamente al tipo d 'cadena' ya que se reconoció que contenía valores de 'cadena'.
Utilizar esta:
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
El siguiente código cambiará el tipo de datos de una columna.
df[['col.name1', 'col.name2'...]] = df[['col.name1', 'col.name2'..]].astype('data_type')
En lugar del tipo de datos, puede darle al tipo de datos lo que desee, como str, float, int, etc.
Cuando solo necesitaba especificar columnas específicas y quiero ser explícito, utilicé (según pandas.DataFrame.astype ):
dataframe = dataframe.astype({'col_name_1':'int','col_name_2':'float64', etc. ...})
Entonces, usando la pregunta original, pero proporcionándole nombres de columnas...
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'})