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 un np.array o serie. Ej. df['col1'].dtype --> int32

  • dtypes: muestra los tipos de datos de todas las columnas de una dataframe. 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 sobre arrays, series y dataframes.

  • pd.to_numeric(): que convierte a tipo numérico (int o float) si es posible. Trabaja solo para series y tipos arrays. Documentación completa de pandas.to_numeric. También tenemos el método pd.to_datetime() que veremos mas adelante.

Tipos de datos numéricos#

Vamos a comenzar a trabajar con los datos enteros. Empezamos con los int nativos:

# 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.int8 de 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 serie y 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 una dataframe o 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: pone NaN (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 str nativo de Python permite usar métodos de strings, como upper(), contains(), replace(), etc. No entiende nulos (None no es nulo, sino la cadena “None”)

  • El tipo string de Pandas permite definir también cadenas de texto con la ventaja de que los nulos los transforma al tipo pd.NA.

Veamos un ejemplo de un dataframe con 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 str y a string:

#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 un string, 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 bool nativo de Python permite representar True o False. No soporta nulos

  • El tipo boolean de Pandas permite nulos y los transforma al tipo pd.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 –> True

  • Si el valor es 0 –> False

  • Si es un string vacío ("") –> False

  • Si es un string NO vacío ("xxx") –> True

  • Si es None –> False

  • Si 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 dataframe con 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