3.2. Orientación a columnas con Pandas#
Introducción#
En esta última parte del curso vamos a analizar el paradigma de orientación a columnas, como lo hace MariaDB ColumnStore y su relación con Pandas.
MariaDB Orientado a columnas y Series en Pandas#
Como vimos en todos los ejemplos anteriores, en Pandas los dataframes se comportan más similares a un almacenamiento por columnas que por filas, aunque cuando los emos visualmente, parece organizado por filas.
Por ejemplo, creabamos un
dataframecomo:
import pandas as pd
df_por_columnas = pd.DataFrame({
'id_cliente': [1, 2, 3, 4, 5, 6, 7],
'nombre': ['Susana', 'Oscar', 'Elena', 'Elena', 'Oscar', 'Elena', 'Lucila'],
'apellido': ['Horia','Acol', 'Morado', 'Morado','Acol','Morado', 'Nave'],
'ciudad': ['Neuquén', 'Rosario', 'Córdoba', 'Córdoba', 'Rosario', 'Córdoba', 'Plottier'],
'producto': ['Palitos de la Selva', 'Chupetín Pico Dulce', 'Rocklets', 'Alfajor Jorgito', 'Tubby 4', 'Tubby 4', 'Tubby 4'],
'precio': [100, 200, 150, 300, 120, 80, 230]
})
donde estamos creando cada columna de la forma:
'nombre': ['Susana', 'Oscar', 'Elena', 'Elena', 'Oscar', 'Elena', 'Lucila']
es decir es una columna. La unión de cada una de esas columnas, resulta en el dataframe. Incluso recordemos que cada columna de un dataframe es una serie.
También podriamos haber creado un dataframe como un conjunto de tuplas, y ahí sí, podemos ver una creación más orientada a filas que a columnas.
En cambio si lo hacemos por filas, sería como crear cada una de las tuplas:
#creamos una lista de tuplas
datos = [
(1, 'Susana', 'Horia', 'Neuquén', 'Palitos de la Selva', 100),
(2, 'Oscar', 'Acol', 'Rosario', 'Chupetín Pico Dulce', 200),
(3, 'Elena', 'Morado', 'Córdoba', 'Rocklets', 150),
(4, 'Elena', 'Morado', 'Córdoba', 'Alfajor Jorgito', 300),
(5, 'Oscar', 'Acol', 'Rosario', 'Tubby 4', 120),
(6, 'Elena', 'Morado', 'Córdoba', 'Tubby 4', 80),
(7, 'Lucila', 'Nave', 'Plottier', 'Tubby 4', 230)
]
# Definimos las columnas por separado
columnas = ['id_cliente', 'nombre', 'apellido', 'ciudad', 'producto', 'precio']
# Creamos el DataFrame
df_por_filas = pd.DataFrame(datos, columns=columnas)
Visualmente el resultado es el mismo. Es decir, si mostramos por pantalla df_por_columnas y df_por_filas, vamos a ver el mismo resultado. Sin embargo la forma de crearlo fue diferente. Esta segunda forma simula una fila completa en cada dato (una tupla), en cambio en el primero creamos listas de cada columna.
Otra forma de simular una organización por columnas es utilizar un dict donde cada clave es el nombre de la columna, y los valores son los datos de la misma.
Haciendo un
dictdesde df_por_filas:
#dict column_store utilizando el df_por_filas creado previamente
#cada clave representa una columna independiente
dict_column_store = {
'id_cliente': df_por_filas['id_cliente'].to_list(),
'nombre': df_por_filas['nombre'].to_list(),
'apellido': df_por_filas['apellido'].to_list(),
'ciudad': df_por_filas['ciudad'].to_list(),
'producto': df_por_filas['producto'].to_list(),
'precio': df_por_filas['precio'].to_list()
}
#type(dict_column_store)
#dict_column_store.get('id_cliente')
dict_column_store
{'id_cliente': [1, 2, 3, 4, 5, 6, 7],
'nombre': ['Susana', 'Oscar', 'Elena', 'Elena', 'Oscar', 'Elena', 'Lucila'],
'apellido': ['Horia', 'Acol', 'Morado', 'Morado', 'Acol', 'Morado', 'Nave'],
'ciudad': ['Neuquén',
'Rosario',
'Córdoba',
'Córdoba',
'Rosario',
'Córdoba',
'Plottier'],
'producto': ['Palitos de la Selva',
'Chupetín Pico Dulce',
'Rocklets',
'Alfajor Jorgito',
'Tubby 4',
'Tubby 4',
'Tubby 4'],
'precio': [100, 200, 150, 300, 120, 80, 230]}
Para simular un recorrido por columnas, podemos recorrer la estructura anterior (dict_column_store).
En el siguiente código, creamos un nuevo dict (total_por_producto) que va a tener:
una clave por cada nombre de producto diferente
por cada clave el precio total (que se va sumando)
Recorremos dict_column_store como si fueran columnas separadas:
# Creamos un diccionario para sumar precios por producto
total_por_producto = {}
# Recorremos las columnas simultáneamente
# el zip permite usar dos o más listas (u otros iterables) y recorrerlas “en paralelo”,
# devolviendo tuplas con un elemento de cada iterable en cada paso.
# en este caso recorremos el dict dict_column_store.get('producto') y cada valor se guarda en la variable producto
# y simultaneamente recorremos el dict dict_column_store.get('precio') y cada valor se guarda en la variable precio
for producto, precio in zip(dict_column_store.get('producto'), dict_column_store.get('precio')):
print(f'El producto es : {producto}, y el precio es: {precio}')
if producto in total_por_producto:
# si el producto esta en el dict total_por_producto, suma el precio
total_por_producto[producto] += precio
else:
#sino crea un nuevo item del dict con el precio
total_por_producto[producto] = precio
total_por_producto
El producto es : Palitos de la Selva, y el precio es: 100
El producto es : Chupetín Pico Dulce, y el precio es: 200
El producto es : Rocklets, y el precio es: 150
El producto es : Alfajor Jorgito, y el precio es: 300
El producto es : Tubby 4, y el precio es: 120
El producto es : Tubby 4, y el precio es: 80
El producto es : Tubby 4, y el precio es: 230
{'Palitos de la Selva': 100,
'Chupetín Pico Dulce': 200,
'Rocklets': 150,
'Alfajor Jorgito': 300,
'Tubby 4': 430}
El código de arriba puede simular un motor orientado a columnas ya que:
se trabaja sobre columnas individuales
se puede operar sobre columnas sin cargar filas completas
se puede paralelizar operaciones por columna
se almacena y comprime cada columna por separado.