1.5. Conversión de datos#
Introducción#
La conversión de datos transforma datos de un formato a otro para que puedan ser usados para un propósito específico. La librería Pandas posee muchos métodos que permiten la manipulación de datos. En esta sección veremos los tipos de datos que posee definida la librería junto con la conversiones posibles.
NumPy y Pandas#
Python posee tipos de datos primitivos como int, float, bool, str, bytes, y None (null). No hay distinción entre tipos primitivos y objetos: todos los tipos en Python son objetos, incluso los primitivos. Así, int es una clase y los números enteros son objetos de esa clase directamente.
Al mismo tiempo, la librería Numpy provee otros tipos de datos para la representación de los arreglos, como np.int8, np.int16, np.float64, np.float128, np.bool, np.str, datetime64, etc.
Pandas utiliza los tipos de datos de Numpy como base, y los extiende para ofrecer funcionalidades más avanzadas, especialmente para manejar valores nulos y tipos heterogéneos en estructuras tabulares. Es así que los dataframes y series de Pandas estan construidos sobre arrays de Numpy.
En conclusión, muchos tipos de datos en Pandas son directamente numpy.dtype (como int64, float64, bool), y a esos Pandas ha creado extensiones como Int8 (de -128 a 127), Int16 (-32,768 a 32,767), Int32, Int64, boolean, string, Float64, pd.NA (null), etc. Estas extensiones sirven para agregar manejo de nulos, o métodos no existentes que mejoren su manipulación y conversión.
Métodos para analizar los tipos de datos#
Podemos diferenciar en tres métodos para utilizar y conocer los tipos de datos de las estructuras:
dtype: indica el tipo de dato de unnp.arrayoserie. Ej.df['col1'].dtype --> int32dtypes: muestra los tipos de datos de todas las columnas de unadataframe. Ej.df.dtypes --> A: int32. B: float64,...type(...): indica la clase de un objeto determinado. Ej.type(arreglo) --> <class 'numpy.ndarray'>
A su vez hay dos métodos específicos para cambiar los tipos de datos:
astype(): que convierte un objeto a un tipo de datos específico (int,string, etc). Documentación completa de pandas.DataFrame.astype. Igualmente funciona sobrearrays,seriesydataframes.pd.to_numeric(): que convierte a tipo numérico (int o float) si es posible. Trabaja solo paraseriesy tiposarrays. Documentación completa de pandas.to_numeric. También tenemos el métodopd.to_datetime()que veremos mas adelante.
Tipos de datos numéricos#
Vamos a comenzar a trabajar con los datos enteros. Empezamos con los
intnativos:
# int nativo de python
a = 5
#convertir un str a int
b = int("7")
#imprimo
print(f'La suma de los valores es {a + b}')
# quiero ver el tipo asignado, como vemos es una clase.
print(f'El tipo de datos de a es {type(a)}, de b es {type(b)} y de a+b es {type(a+b)}')
La suma de los valores es 12
El tipo de datos de a es <class 'int'>, de b es <class 'int'> y de a+b es <class 'int'>
Ahora continuamos con los
np.int8de NumPy:
# debemos importar la librería
import numpy as np
#definimos el tipo int64
a = np.int8(42)
b = np.int8(10)
# imprimimos a y el tipo de a
print(f'El valor de a es {a}, y su tipo es {type(a)}')
print(f'El valor de b es {b}, y su tipo es {type(b)}')
print(f'La suma de a+b es {a+b}, y su tipo es {type(a+b)}')
El valor de a es 42, y su tipo es <class 'numpy.int8'>
El valor de b es 10, y su tipo es <class 'numpy.int8'>
La suma de a+b es 52, y su tipo es <class 'numpy.int8'>
Para el caso de Int8, Int32, Int32 e Int64 de Pandas no se puede crear un valor directamente de estos tipos. Lo debemos hacer con una serie.
Entonces, creamos la
seriey luego la transformamos:
#importo la librería
import pandas as pd
#creo una Serie con el valor 5 y le digo que sea de tipo Int8
s = pd.Series([5], dtype="Int8")
#obtengo el primer valor
#la proxima clase veremos el uso de loc e iloc
valor = s.loc[0]
# mostramos el elemento y su tipo, observar que lo hacemos con type y dtype
print(f'El valor del elemento de la serie es {valor} y su tipo es {valor.dtype} y con type es {type(valor)}')
El valor del elemento de la serie es 5 y su tipo es int8 y con type es <class 'numpy.int8'>
¿Por qué si creamos un Int8 nos muestra que el tipo es int8? Eso lo hace por eficiencia y porque no hay nulos en la serie y no necesita un tipo de datos que consume mas recursos.
Sin embargo, si consultamos el tipo de dato de la
serie:
# sin embargo, el tipo de dato de la serie es la que me pediste!
print(f'El tipo de dato de la serie es {s.dtype}')
El tipo de dato de la serie es Int8
Conversiones de datos numéricos#
Ahora vamos a probar con otros tipos de datos y conversiones entre ellos.
MOMENTO CHEATSHEET
Este es el momento de recurrir a una nueva cheatsheet que provea un resumen breve y práctico de información clave sobre los métodos de Pandas para las conversiones. Mostramos en la figura siguiente la parte que nos interesa en este momento.
CONVERSIONES CON pd.to_numeric()
El método de Pandas to_numeric() convierte valores a números, ya sea enteros (int) o flotantes (float), dependiendo del contenido. Si puede, convierte el valor a int. Si el número tiene decimales, lo convierte a float y si no lo puedo convertir, da un error (que puede estar manejado por un try-except)
Vamos a probar algunos ejemplos sencillos donde trabajamos con datos numéricos y
to_numeric():
import pandas as pd
# vamos a pedir un valor de entrada
# podriamos haber colocado el código:
#entrada = int(input('Ingrese una valor entero:'))
# pero eso va a forzar convertir a entero y si ingresa un str va a dar error
# Entonces debemos pedir la entrada
############# ATENCIÓN #########
#Para ejecutarlo, tienen que descomentar la siguiente línea
#entrada = input('Ingrese una valor entero:')
# y comentar la siguiente línea
entrada = 3
# y tratar de pasarlo a al tipo de dato int
#lo hago cpon un try-except para capturar la excepcion
try:
enteronp = pd.to_numeric(entrada)
print(f'Bien! me hiciste caso e ingresaste un entero {enteronp}, ahora de tipo {type(enteronp)}')
#tambien lo podria haber transformado a un entero nativo
entero_nativo = int(entrada)
print(f'El entero int nativo queda {entero_nativo}, ahora de tipo {type(entero_nativo)}')
except ValueError:
print('mmmm y ahora que puedo hacer?')
Bien! me hiciste caso e ingresaste un entero 3, ahora de tipo <class 'int'>
El entero int nativo queda 3, ahora de tipo <class 'int'>
En el ejemplo anterior si ingresabamos un número con decimales, la parte del int nativa da un error. A probarlo!!
En los ejemplos a continuación, vamos a ver datos del estilo np.nan y pd.NA que representan valores nulos en NumPy y Pandas respectivamente. En la unidad siguiente veremos el significado de estos valores. Por ahora debemos saber que representan valores nulos del estilo de None.
PARÁMETROS DE pd.to_numeric()
Algunos de los parámetros del método pd.to_numeric() son:
arg: de tipo arreglo, lista, serie, columna de unadataframeo lo que se necesite convertir.errors: Indicar como manejar los errores al querer realizar la conversión, tenemos:raise: lanza el error (por defecto)coerce: poneNaN(np.nan) si falla
downcast: Permite reducir el tamaño del tipo numérico, por ejemplo, de int64 a int8.
Veamos algunos ejemplos simples:
#creo un dataframe que tiene valores enteros, letras y nulos
df = pd.DataFrame({'numeros': ['1', '2', '3', 'a', '5', np.nan, pd.NA]})
# paso a numerico la columna numeros y creo una nueva columna con la conversion
df['numeros_con_to_numeric_coerce'] = pd.to_numeric(df['numeros'], errors= 'coerce')
print(f'La columna ahora, con COERCE tiene estos valores \n{df}')
# si queremos capturar el error, hacemos
try:
df['numeros_con_to_numeric_raise'] = pd.to_numeric(df['numeros'], errors='raise')
print(f'La columna ahora, con RAISE tiene estos valores {df}')
except ValueError as e:
print(f'El error es: {e}')
La columna ahora, con COERCE tiene estos valores
numeros numeros_con_to_numeric_coerce
0 1 1.0
1 2 2.0
2 3 3.0
3 a NaN
4 5 5.0
5 NaN NaN
6 <NA> NaN
El error es: Unable to parse string "a" at position 3
Como podemos observar los nulos los tranforma a pd.nan.
Además, si queremos transformar esa serie a Int8 y no a un float, lo que debemos hacer es pasarlo primero a numérico y luego transformarlo con astype().
Entonces, pasamos la misma serie, pero ahora a entero, no float:
# transformo a numero y luego a entero
df['numeros_con_to_numeric_coerce_int'] = pd.to_numeric(df['numeros'], errors= 'coerce').astype('Int8')
print(f'La columna ahora es {df}')
print(f'El tipo de las columnas del dataframe es {df.dtypes}')
La columna ahora es numeros numeros_con_to_numeric_coerce numeros_con_to_numeric_coerce_int
0 1 1.0 1
1 2 2.0 2
2 3 3.0 3
3 a NaN <NA>
4 5 5.0 5
5 NaN NaN <NA>
6 <NA> NaN <NA>
El tipo de las columnas del dataframe es numeros object
numeros_con_to_numeric_coerce float64
numeros_con_to_numeric_coerce_int Int8
dtype: object
Como podemos observar ahora, cambian los nulos a pd.NA (nulo de Pandas). Algo muy similar sucede con el tipo float en sus versiones float16, float32 y float64 para Python nativo y Float32/Float64 de Pandas que permite el dato nulo pd.NA.
Conversiones de datos boolean y string#
Otro caso a analizar son los datos string y boolean de Pandas. Para estos tipos se debe usar el método astype('string') o astype('boolean') respectivamente.
Los tipos de datos string
Existen dos tipos de datos string:
El tipo
strnativo de Python permite usar métodos de strings, comoupper(),contains(),replace(), etc. No entiende nulos (Noneno es nulo, sino la cadena “None”)El tipo
stringde Pandas permite definir también cadenas de texto con la ventaja de que los nulos los transforma al tipopd.NA.
Veamos un ejemplo de un
dataframecon diferentes tipos de valores string y nulos:
df = pd.DataFrame({ 'valorestring': ['Ana', 'Luis', 'Sofía', None, np.nan, pd.NA, 'None'] })
print(f'El tipo de la columna es {df.dtypes}')
df
El tipo de la columna es valorestring object
dtype: object
| valorestring | |
|---|---|
| 0 | Ana |
| 1 | Luis |
| 2 | Sofía |
| 3 | None |
| 4 | NaN |
| 5 | <NA> |
| 6 | None |
Ahora creamos dos nuevas columnas transformando la columna anterior a
stry astring:
#creamos una nueva columna que tenga los valores convertidos a str
df['valorestring_con_str'] = df['valorestring'].astype('str')
print(f'El tipo de la columna {df.columns[1]} es {df["valorestring_con_str"].dtypes}')
#creamos una nueva columna que tenga los valores convertidos a string
df['valorestring_con_string'] = df['valorestring'].astype('string')
print(f'El tipo de la columna {df.columns[2]} es {df["valorestring_con_string"].dtypes}')
df
El tipo de la columna valorestring_con_str es object
El tipo de la columna valorestring_con_string es string
| valorestring | valorestring_con_str | valorestring_con_string | |
|---|---|---|---|
| 0 | Ana | Ana | Ana |
| 1 | Luis | Luis | Luis |
| 2 | Sofía | Sofía | Sofía |
| 3 | None | None | <NA> |
| 4 | NaN | nan | <NA> |
| 5 | <NA> | <NA> | <NA> |
| 6 | None | None | None |
Como podemos observar, la columna valorestring_con_str queda de tipo object y los nulos son convertidos a texto, es decir NO representan nulos, sino una cadena de texto.
En cambio, en la columna valorestring_con_string observamos que todos los nulos son convertidos a pd.NA y tratados como nulos verdaderos. Excepto la fila 6 que posee la cadena “None” ya que fue creada como un texto. La columna quedó entonces de tipo string.
Conversión a string
En resumen, para la transformación a string:
None: lo pasa a<NA>np.nan: lo deja del mismo tipo de nulo (lo visuliza como<NA>)pd.NA: que es un nulo de Pandas lo pasa<NA>"None": lo toma como unstring, como si fuera el nombre de una persona
Luego podemos usar str (tipo accesor) para usar las funciones sobre los strings.
Vamos a pasar todo a mayúsculas y contar la longitud:
# creo una nueva columna con lo que tiene la columna valorestring_con_string pero con
# mayusculas
df['mayusculas'] = df['valorestring_con_string'].str.upper()
# creo una nueva columna con lo que cuanta la longitud de los string de la
# columan valorestring_con_string
df['cantidadCaracteres'] = df['valorestring_con_string'].str.len()
df
| valorestring | valorestring_con_str | valorestring_con_string | mayusculas | cantidadCaracteres | |
|---|---|---|---|---|---|
| 0 | Ana | Ana | Ana | ANA | 3 |
| 1 | Luis | Luis | Luis | LUIS | 4 |
| 2 | Sofía | Sofía | Sofía | SOFÍA | 5 |
| 3 | None | None | <NA> | <NA> | <NA> |
| 4 | NaN | nan | <NA> | <NA> | <NA> |
| 5 | <NA> | <NA> | <NA> | <NA> | <NA> |
| 6 | None | None | None | NONE | 4 |
Ahora veamos el caso de los datos tipo boolean.
Los tipos de datos boolean
Existen dos tipos de datos boolean:
El tipo
boolnativo de Python permite representarTrueoFalse. No soporta nulosEl tipo
booleande Pandas permite nulos y los transforma al tipopd.NA
Entonces si queremos convertir valores enteros, float, o bool a bool, lo hacemos con
astype('bool'):
df = pd.DataFrame({ 'valoresbool': [True, None, False, False, 1, -2, 3.8, 0, "hola!", "", np.nan] })
df['valoresbool_con_bool'] = df['valoresbool'].astype('bool')
df
| valoresbool | valoresbool_con_bool | |
|---|---|---|
| 0 | True | True |
| 1 | None | False |
| 2 | False | False |
| 3 | False | False |
| 4 | 1 | True |
| 5 | -2 | True |
| 6 | 3.8 | True |
| 7 | 0 | False |
| 8 | hola! | True |
| 9 | False | |
| 10 | NaN | True |
Como podemos observar de valoresbool y valoresbool_con_bool poseen datos diferentes.
Conversión a bool
Si el valor es numérico, distinto de 0 –>
TrueSi el valor es 0 –>
FalseSi es un string vacío
("")–>FalseSi es un string NO vacío
("xxx")–>TrueSi es
None–>FalseSi es
np.nan–>True
No puede convertir el valor pd.NA ya que dá un error por desconocerlo. Ahora si queremos pasarlo al boolean de Pandas para aceptar valores pd.NA, podemos hacerlo pero sin pasarle valores numéricos mezclados con strings, ya que también dá un error.
Podemos hacer la conversión de lo siguiente, en donde los nulos, los deja nulos:
df = pd.DataFrame({ 'valoresbool': [True, None, False, False, np.nan, pd.NA] })
df['valoresbool_con_boolean'] = df['valoresbool'].astype('boolean')
df
| valoresbool | valoresbool_con_boolean | |
|---|---|---|
| 0 | True | True |
| 1 | None | <NA> |
| 2 | False | False |
| 3 | False | False |
| 4 | NaN | <NA> |
| 5 | <NA> | <NA> |
Sin embargo daría error pasar una mezcla de valores de diferentes tipos con astype('boolean'). Por ejemplo, pasar
[True, None, False, False, np.nan, pd.NA, 5, "Hola"] devuelve un error ya que se mezclan números con valores booleanos.
Conversiones de datos fechas/horas#
Un caso diferente para analizar son las fechas, o mejor dicho los tipos datetime.
Tanto Pandas como Numpy poseen estos tipos con algunas diferencias. Específicamente, Pandas extiende algunos de estos tipos de Numpy, como datetime64 y timedelta64. Por ejemplo, datetime64 permite definir una fecha y hora como:
np.datetime64('2023-07-01T12:30:45') # con segundos
np.datetime64('2025-07-01T12:30:45.123456') # con hasta microsegundos
Pandas extiende este tipo a datetime64[ns] para poder representar hasta nanosegundos y permitir valores nulos, del tipo NaT (Not a Time). Ademas agrega métodos como .year, .month, .weekday(), etc.
Mas información de estos tipos de datos esta en Pandas datetime
Por ejemplo, creamos una fecha y consultamos sus partes:
# Creamos una fecha
#pd.to_datetime() acepta dtype solo cuando se le pasa una estructura de datos, como
# una lista ([]), una serie, una columna de un DataFrame
fecha = pd.to_datetime(['2025-07-15 15:45:00'])
print(f'La fecha es {fecha}')
print(f'El tipo de la fecha es {fecha.dtype} ')
# Accedemos a prtes de la fecha
print(f'El año es %s, el mes es %s, el día es %s, y la hora %s' %(fecha[0].year, fecha[0].month, fecha[0].day, fecha[0].hour))
La fecha es DatetimeIndex(['2025-07-15 15:45:00'], dtype='datetime64[ns]', freq=None)
El tipo de la fecha es datetime64[ns]
El año es 2025, el mes es 7, el día es 15, y la hora 15
Qué pasa cuando la fecha no cumple con el formato?, lo transforma a
pd.NaT:
df = pd.DataFrame({ 'valoresfecha': ['2025-07-15', '2025-15-12', np.nan, pd.NA] })
df["fechaNueva"] = pd.to_datetime(df["valoresfecha"], format="%Y-%m-%d", errors= 'coerce')
df
| valoresfecha | fechaNueva | |
|---|---|---|
| 0 | 2025-07-15 | 2025-07-15 |
| 1 | 2025-15-12 | NaT |
| 2 | NaN | NaT |
| 3 | <NA> | NaT |
Ejemplo Completo#
Ahora vamos a hacer un ejemplo completo y mas complejo.
Creamos un
dataframecon varias columnas de diferentes tipos:
import numpy as np
import pandas as pd
df_mas_dificil = pd.DataFrame({
"id": ["1", "2", None, "4", "8", "juan", 9],
"precio": ["100.5", None, "300.1", "400", pd.NA, 45.70, 423.80],
"fecha": ["2024-01-01", "no válida", None, "2024-01-04", None, np.nan, "2025-05-12"],
"nombre": ["Ana", "Luis", None, "Sofía", np.nan, pd.NA, "None"],
"tiene_perro": [True, None, False, False, np.nan, pd.NA, True]
})
df_mas_dificil
| id | precio | fecha | nombre | tiene_perro | |
|---|---|---|---|---|---|
| 0 | 1 | 100.5 | 2024-01-01 | Ana | True |
| 1 | 2 | None | no válida | Luis | None |
| 2 | None | 300.1 | None | None | False |
| 3 | 4 | 400 | 2024-01-04 | Sofía | False |
| 4 | 8 | <NA> | None | NaN | NaN |
| 5 | juan | 45.7 | NaN | <NA> | <NA> |
| 6 | 9 | 423.8 | 2025-05-12 | None | True |
print(f'Los tipos que tienen cada una de las columnas son \n{df_mas_dificil.dtypes}')
Los tipos que tienen cada una de las columnas son
id object
precio object
fecha object
nombre object
tiene_perro object
dtype: object
Hacemos las conversiones a los tipos correctos de Pandas:
# transformar la columna id
df_mas_dificil['id'] = pd.to_numeric(df_mas_dificil['id'], errors= 'coerce').astype('Int8')
print(f'El tipo de la columna {df_mas_dificil.columns[0]} es {df_mas_dificil["id"].dtype}')
# transformar la columna precio
df_mas_dificil["precio"] = pd.to_numeric(df_mas_dificil["precio"], errors= 'coerce')
print(f'El tipo de la columna {df_mas_dificil.columns[1]} es {df_mas_dificil["precio"].dtype}')
# transformar la columna fecha
df_mas_dificil["fecha"] = pd.to_datetime(df_mas_dificil["fecha"], format="%Y-%m-%d", errors= 'coerce')
print(f'El tipo de la columna {df_mas_dificil.columns[2]} es {df_mas_dificil["fecha"].dtype}')
# transformar la columna nombre a string
df_mas_dificil["nombre"] = df_mas_dificil["nombre"].astype("string")
print(f'El tipo de la columna {df_mas_dificil.columns[3]} es {df_mas_dificil["nombre"].dtype}')
# transformar la columna tiene_perro a boolean
df_mas_dificil["tiene_perro"] = df_mas_dificil["tiene_perro"].astype("boolean")
print(f'El tipo de la columna {df_mas_dificil.columns[4]} es {df_mas_dificil["tiene_perro"].dtype}')
df_mas_dificil
El tipo de la columna id es Int8
El tipo de la columna precio es float64
El tipo de la columna fecha es datetime64[ns]
El tipo de la columna nombre es string
El tipo de la columna tiene_perro es boolean
| id | precio | fecha | nombre | tiene_perro | |
|---|---|---|---|---|---|
| 0 | 1 | 100.5 | 2024-01-01 | Ana | True |
| 1 | 2 | NaN | NaT | Luis | <NA> |
| 2 | <NA> | 300.1 | NaT | <NA> | False |
| 3 | 4 | 400.0 | 2024-01-04 | Sofía | False |
| 4 | 8 | NaN | NaT | <NA> | <NA> |
| 5 | <NA> | 45.7 | NaT | <NA> | <NA> |
| 6 | 9 | 423.8 | 2025-05-12 | None | True |
print(f'Los tipos que tienen cada una de las columnas son \n{df_mas_dificil.dtypes}')
Los tipos que tienen cada una de las columnas son
id Int8
precio float64
fecha datetime64[ns]
nombre string[python]
tiene_perro boolean
dtype: object