1.1. Datos estructurados

1.1. Datos estructurados#

Introducción#

Los datos estructurados generalmente provienen de las bases de datos.

Como ya conocen MariaDB vamos a leer datos de una de las bases de datos trabajadas en la asignatura anterior.

Vamos a utilizar las siguientes librerías:

  • Pandas: para análisis y manipulación de datos.

  • Mysql connector: para la conexión de entre python y la BD.

Comenzamos importando las librerías:

import pandas as pd
import mysql.connector as mariadb

Conexión#

Comenzamos configurando los parámetros de la conexión, en donde indicamos en qué máquina esta alojada la BD, el puerto, usuario, clave y el nombre de la BD a la cual acceder. En nuestro caso, podemos cambiar el nombre de la BD ('database': 'baseAerolinea') a cualquiera de las disponibles en el sistema gestor.

Creamos el diccionario db_config con los siguientes parámetros:

# Crear una estructura que posea todos los datos que necesita una conexion a  
# Maria DB y a la BD
db_config = {
    'host': '10.0.2.200',
    'port': 3306,
    'user': 'usuario2',
    'password': 'usuario1123',
    'database': 'baseAerolinea'
}

El host donde esta instalada MariaDB en este ejemplo es: 10.0.2.200. El mismo representa un host dentro de la red local y el puerto 3306 que se utiliza por defecto con MariaDB.

Sin embargo, se puede reemplazar por el gestor de MariaDB en la Web con los siguientes parámetros: 'host': 'eida-db.fi.uncoma.edu.ar', 'port': 3337,

Para hacer la conexión, usamos el try-except que nos permite manejar las excepciones nosotros mismos.

Luego usamos la estructura anterior y el método connect(**db_config) para conectarnos con la DB de la aerolinea:

conexion = None
try:
        #Establecer la conexion
        print("Conectando a MariaDB...")
        conexion = mariadb.connect(**db_config)
        print("Conexión exitosa!")
    
        # Siempre se debe cerrar la conexion
        conexion.close()
        print("Conexión a MariaDB cerrada.")

except mariadb.Error as e:
        print(f"Ocurrió un error :( {e}")
Conectando a MariaDB...
Conexión exitosa!
Conexión a MariaDB cerrada.

RECORDAMOS…

El ** se usa para extraer todas las claves y valores de un diccionario y pasarlos como argumentos con nombre (kwargs)

Cursores#

Un objeto importante de librería de MySQL es el tipo cursor, ya que permite la ejecución de las consultas y la manipulación de los resultados.

MÉTODOS Y PROPIEDADES DEL CURSOR

Algunos de los métodos y propiedades de los cursores son:

  • execute(): toma una consulta SQL como parámetro (str) y la ejecuta

  • executemany(): permite ejecutar la misma sentencia SQL muchas veces con diferentes parámetros, normalmente para INSERT, UPDATE o DELETE

  • fetchall(): recupera todas las tuplas del resultado de una consulta y las devuelve como una lista de tuplas:[(..,..,..),(..,..,..)]. Si se ejecuta después de recuperar algunas tuplas, se devuelven las restantes. Si no posee filas devuelve una lista vacía [].

  • fetchone(): recupera la siguiente tupla del resultado de una consulta y la devuelve como una tupla

  • fetchmany(size): recupera el conjunto de tuplas indicado como parámetro. Por ejemplo fetchmany(3) recupera una lista de las tres primeras tuplas del resultado de la consulta.

  • close(): Cierra un cursor abierto

  • description: Es una propiedad de solo lectura que devuelve una lista de tuplas con las columnas de un conjunto de tuplas. El primer valor de cada tupla de la lista es el nombre de la columna

Un aspecto importante del cursor es que no se puede volver atrás en su recorrido. Una vez que se consumen las filas con fetchone(), fetchmany() o fetchall(), no se puede utilizar o volver a recorrer. Hay que volver a ejecutar la consulta.

Otro aspecto de los cursores es que una vez que se busca la información con un cursor, se debe consumir por completo. Es decir, si hago una consulta que devuelve 200 tuplas, debo recuperar las 200.

La documentación completa de este método la podemos encontrar en The cursor class

Vamos a ir por partes para ir mostrando como recuperar datos y manipularlos. Para eso vamos a sacar el try-except por cuestiones de simplicidad, pero en el código terminado y completo debe quedar.

Ahora vamos a utilizar el método fetchall() para recuperar el resultado de la consulta SQL de todos los datos de la tabla pasajero

#Establecer la conexion
print("Conectando a MariaDB...")
conexion = mariadb.connect(**db_config)
print("Conexión exitosa!")
    
#Crear un cursor con la conexion
cursor = conexion.cursor()
print("\nSeleccionando datos...")
consulta = "SELECT * FROM pasajero"

#Ejecutar la consulta y guardarla en un cursor
cursor.execute(consulta)   
    
#Colocar a datos_del_pasajero el resultado de buscar todos los valores de la consulta       
datos_del_pasajero = cursor.fetchall()
Conectando a MariaDB...
Conexión exitosa!

Seleccionando datos...

Este código deja el cursor abierto y también la conexión con la BD. Luego debemos recordar de cerrar el cursor con cursor.close() y la conexión con conexion.close().

Para imprimir o acceder a las tuplas de la lista creada de datos_del_pasajero, se debe acceder como cualquier lista, con corchetes:

print('La primera tupla del pasajero es: ', datos_del_pasajero[0])

#hace slicing listando del indice 3 (incluido) al 5 (sin incluir)
print('La cuarta y quinta tupla del pasajero es: ', datos_del_pasajero[3:5])
La primera tupla del pasajero es:  ('16366542', 'Juan Muñoz', datetime.date(1963, 7, 10))
La cuarta y quinta tupla del pasajero es:  [('19283746', 'Sofia Castro', datetime.date(1967, 10, 27)), ('21928374', 'Pedro Gomez', datetime.date(1969, 9, 4))]

Tambien podemos iterar sobre el resultado de la consulta. Recordemos que el resultado del fetchall() es una lista de tuplas.

Vamos a iterar sobre las tuplas del resultado de los pasajeros:

#lo hago con un for
#cada elemento i es una tupla
# el 0 significa que estamos buscando la primera columna
for i in datos_del_pasajero:
    print(i)
    print(f'El dni del pasajero es {i[0]}')
('16366542', 'Juan Muñoz', datetime.date(1963, 7, 10))
El dni del pasajero es 16366542
('17273609', 'Mario Flores', datetime.date(1965, 11, 15))
El dni del pasajero es 17273609
('18121332', 'Paula Perez', datetime.date(1965, 8, 10))
El dni del pasajero es 18121332
('19283746', 'Sofia Castro', datetime.date(1967, 10, 27))
El dni del pasajero es 19283746
('21928374', 'Pedro Gomez', datetime.date(1969, 9, 4))
El dni del pasajero es 21928374
('27426136', 'Maria Torres', datetime.date(1980, 2, 21))
El dni del pasajero es 27426136
('27466880', 'Carlos Perez', datetime.date(1980, 3, 16))
El dni del pasajero es 27466880
('29211155', 'Jose Flores', datetime.date(1982, 6, 19))
El dni del pasajero es 29211155
('39112312', 'Carla Santos', datetime.date(1996, 8, 13))
El dni del pasajero es 39112312

Dataframes#

Para poder utilizar todos los beneficios de los dataframes de Pandas, vamos a almacenar el resultado de la consulta en una estructura de este tipo.

QUÉ SON LOS DATAFRAMES?

Es una estructura de datos bidimensional (filas y columnas) similar a una hoja de cálculo o tabla SQL. Posee filas, columnas y datos.

En la sección 1.4 se describen los métodos principales de los dataframes.

Vamos a ir por partes para ir mostrando como recuperar datos, organizarlos y guardarlos en un dataframe. Para poder crear el dataframe debemos determinar cuales son sus columnas, que en este caso son los nombres de los atributos de la tabla pasajero. Como vimos, cada columna es de tipo Series.

Vamos a recuperar el nombre de los atributos de la tabla que los debemos obtener del cursor. La lista creada datos_del_pasajero no posee las columnas, sino solo el resultado con las tuplas. Entonces, vamos a utilizar la propiedad description para recuperar esos nombres.

Propiedad description

La propiedad description de un cursor devuelve una lista de tuplas de este tipo (column_name, type, None, None, None, None, null_ok, column_flags).

Vemos qué devuelve la propiedad cursor.description, donde el primer elemento de cada tupla posee el nombre de la columna:

for i in cursor.description:
    print(i)
    print(type(i[0]))

#columnas = [i[0] for i in cursor.description]
('dni_pax', 254, None, None, None, None, 0, 20483, 255)
<class 'str'>
('nombre_pax', 253, None, None, None, None, 0, 4097, 255)
<class 'str'>
('fechaNac_pax', 10, None, None, None, None, 0, 4225, 63)
<class 'str'>

Obtenemos los nombres de las columnas y los guardamos en un lista de columnas:

# Obtener nombres de columnas
columnas=[]
for i in cursor.description:
    columnas.append(i[0])
columnas
['dni_pax', 'nombre_pax', 'fechaNac_pax']

Creamos un dataframe con las columnas y el resultado de la consulta SQL. Mostramos las primeras 20 filas.

# Crear el dataframe con las filas de la consulta que fueron recuperadas y las columnas
df = pd.DataFrame(datos_del_pasajero, columns=columnas)
                                                       
#Mostrar las primeras 20 filas del dataframe
print(df.head(20)) 
    dni_pax    nombre_pax fechaNac_pax
0  16366542    Juan Muñoz   1963-07-10
1  17273609  Mario Flores   1965-11-15
2  18121332   Paula Perez   1965-08-10
3  19283746  Sofia Castro   1967-10-27
4  21928374   Pedro Gomez   1969-09-04
5  27426136  Maria Torres   1980-02-21
6  27466880  Carlos Perez   1980-03-16
7  29211155   Jose Flores   1982-06-19
8  39112312  Carla Santos   1996-08-13

Cerrar la conexion:

cursor.close()
conexion.close()

Exportar a .csv#

Otra tarea que podemos hacer antes de terminar, es exportar el dataframe a un archivo .csv. En la sección 1.4 se describe completamente esta funcionalidad.

La creación del archivo .csv va a permitir tener el dataframe guardado en disco y reutilizarlo cuando sea necesario.

Algunos ejemplos de transformar el dataframe con los datos del pasajero a un archivo llamado datos_pasajeroXXX.csv son (crear una carpeta llamada archivosGenerados):

#con las opciones por defecto
df.to_csv('archivosGenerados/datospasajeroPorDefecto.csv')

# sin indice
df.to_csv('archivosGenerados/datospasajeroCambios.csv', index=False)

#sin indice y con separador entre datos 
df.to_csv('archivosGenerados/datospasajeroCambios2.csv', index=False, sep='#')

# con indice y nombre de columnas que queremos incluir
df.to_csv('archivosGenerados/datospasajeroCambios3.csv', index=True, columns=['dni_pax', 'nombre_pax'])

# con formato de fecha aaaa/mm/dd
df.to_csv('archivosGenerados/datospasajeroCambios4.csv', date_format='%Y-%m-%d')

Ahora, HACEMOS TODO JUNTO dentro del try-except:

conexion = None
cursor = None
try:
        #Establecer la conexion
        print("Conectando a MariaDB...")
        conexion = mariadb.connect(**db_config)
        print("Conexión exitosa!")
    
        #Crear un cursor con la conexion
        cursor = conexion.cursor()
        print("\nSeleccionando datos...")
        consulta = "SELECT * FROM pasajero"

        #Ejecutar la consulta y gurdarla en un cursor
        cursor.execute(consulta)   

        #Colocar a datos_del_pasajero el resultado de buscar todos los valores de la consulta       
        datos_del_pasajero = cursor.fetchall()

        #print(datos_del_pasajero)
    
        ############### ACA USAMOS EL DATAFRAME ################
        # Obtener nombres de columnas       
        columnas=[]
        for i in cursor.description:
            columnas.append(i[0])

        # Crear el dataframe con las filas de la consulta que fueron recuperadas y las columnas
        df = pd.DataFrame(datos_del_pasajero, columns=columnas)
                                                       
        #Mostrar las primeras 20 filas del dataframe
        print(df.head(20)) 

        # creamos un csv con las opciones por defecto
        df.to_csv('archivosGenerados/datospasajeroPorDefecto.csv')

        # Siempre se debe cerrar el cursor
        cursor.close()

        # Siempre se debe cerrar la conexion
        conexion.close()
        print("Conexión a MariaDB cerrada.")       

except mariadb.Error as e:
        print(f"Ocurrió un error :( {e}")
Conectando a MariaDB...
Conexión exitosa!

Seleccionando datos...
    dni_pax    nombre_pax fechaNac_pax
0  16366542    Juan Muñoz   1963-07-10
1  17273609  Mario Flores   1965-11-15
2  18121332   Paula Perez   1965-08-10
3  19283746  Sofia Castro   1967-10-27
4  21928374   Pedro Gomez   1969-09-04
5  27426136  Maria Torres   1980-02-21
6  27466880  Carlos Perez   1980-03-16
7  29211155   Jose Flores   1982-06-19
8  39112312  Carla Santos   1996-08-13
Conexión a MariaDB cerrada.