Tarjetas Black¶
La base de datos Tarjetas Black tiene el siguiente modelo de entidad relación:

Las tablas continenem la siguiente información:
- La tabla consejero contiene detalles de la persona que ha relizado el gasto
- La tabla movimiento almacena los detalles del cargo realizado con la tarjeta de crédito
- La tabla comercio almacena datos del comercio donde se ha realizado el cargo
- La tabla actividad contiene una clasificación de las distintas actividades a las que se puede dedicar un comercio
1 | !gdown --id 15p6iQuRTY0AZJY1ZEjruC9KcQPZ642SZ |
/usr/local/lib/python3.8/dist-packages/gdown/cli.py:127: FutureWarning: Option `--id` was deprecated in version 4.3.1 and will be removed in 5.0. You don't need to pass it anymore to use a file ID. warnings.warn( Downloading... From: https://drive.google.com/uc?id=15p6iQuRTY0AZJY1ZEjruC9KcQPZ642SZ To: /content/tajetasblack.db 100% 4.76M/4.76M [00:00<00:00, 125MB/s]
1 | %load_ext sql |
1 | %sql sqlite:///tajetasblack.db |
'Connected: @tajetasblack.db'
1 2 3 4 5 6 7 8 9 | import pandas as pd from sqlalchemy import create_engine engine = create_engine('sqlite:///tajetasblack.db', echo=False) df_actividad = pd.read_sql_table("actividad", engine) df_consejero = pd.read_sql_table("consejero", engine) df_comercio = pd.read_sql_table("comercio", engine) df_movimiento = pd.read_sql_table("movimiento", engine) |
Ejercicio 1¶
Obtener todas las actividades
1 2 3 4 | %%sql SELECT * FROM actividad; |
Como ya tenemos guardado la tabla actividad en un dataframe y nos piden mostrar todo. Mostramos df_actividad.
1 | df_actividad
|
| id_actividad | actividad | |
|---|---|---|
| 0 | 1 | ROPA |
| 1 | 2 | HOTEL |
| 2 | 3 | RESTAURANTE |
| 3 | 4 | COCHE |
| 4 | 5 | COMPRA BIENES |
| 5 | 6 | TELECOM |
| 6 | 7 | MISC |
| 7 | 8 | AVION |
| 8 | 9 | REGALOS |
| 9 | 10 | SOCIAL |
| 10 | 11 | DIRECT MARKETING |
| 11 | 12 | LIBRERIA |
| 12 | 13 | SALIDAS |
| 13 | 14 | TREN |
| 14 | 15 | SUPERMERCADO |
| 15 | 16 | DEPORTE |
| 16 | 17 | VIAJE |
| 17 | 18 | HOGAR |
| 18 | 19 | BUSINESS |
| 19 | 20 | DUTYFREE |
| 20 | 21 | BANCO |
| 21 | 22 | IMAGEN |
| 22 | 23 | COMPRA BIENES LUJO |
| 23 | 24 | ESTETICA |
| 24 | 25 | SALUD |
| 25 | 26 | ESCUELA |
| 26 | 27 | MUEBLES |
| 27 | 28 | CASINO |
| 28 | 29 | BEBIDAS |
| 29 | 30 | BARCO |
| 30 | 31 | TABACO |
| 31 | 32 | CA$H |
| 32 | 33 | ARTE |
| 33 | 34 | SEGUROS |
| 34 | 35 | AGRICULTURA |
| 35 | 36 | FUNERARIAS |
Ejercicio 2¶
Obtener 5 actividades.
Puede ser cualquier actividad
1 2 3 4 5 | %%sql SELECT * FROM actividad LIMIT 5; |
* sqlite:///tajetasblack.db Done.
| id_actividad | actividad |
|---|---|
| 1 | ROPA |
| 2 | HOTEL |
| 3 | RESTAURANTE |
| 4 | COCHE |
| 5 | COMPRA BIENES |
El comando limit de sql es el metodo head(n) donde n es el número de filas que se imprimen.
1 | df_actividad.head(5) |
| id_actividad | actividad | |
|---|---|---|
| 0 | 1 | ROPA |
| 1 | 2 | HOTEL |
| 2 | 3 | RESTAURANTE |
| 3 | 4 | COCHE |
| 4 | 5 | COMPRA BIENES |
Ejercicio 3¶
Obtener 5 actividades ordenadas por el nombre de la actividad (ascendente)
1 2 3 4 5 6 | %%sql SELECT * FROM actividad ORDER BY actividad LIMIT 5; |
* sqlite:///tajetasblack.db Done.
| id_actividad | actividad |
|---|---|
| 35 | AGRICULTURA |
| 33 | ARTE |
| 8 | AVION |
| 21 | BANCO |
| 30 | BARCO |
El metodo ORDER BY de sql en Pandas es mediante el metodo sort_values(col), por defecto se ordena de forma ascendente.
1 2 3 4 5 | ( df_actividad .sort_values("actividad") .head(5) ) |
| id_actividad | actividad | |
|---|---|---|
| 34 | 35 | AGRICULTURA |
| 32 | 33 | ARTE |
| 7 | 8 | AVION |
| 20 | 21 | BANCO |
| 29 | 30 | BARCO |
Ejercicio 4¶
Obtener 5 comercios ordenados por su id (descendente)
1 2 3 4 5 6 | %%sql SELECT comercio, actividad_completa FROM comercio ORDER BY id_comercio DESC LIMIT 5; |
* sqlite:///tajetasblack.db Done.
| comercio | actividad_completa |
|---|---|
| LAZCANO | SUPERMERCADOS,ULTRAMARINOS, ECONOMATOS |
| RESTAURANTE OR DAGO | RESTAURANTES RESTO |
| PORTO ALEGRE II | RESTAURANTES RESTO |
| PC CITY S.SEBASTIAN REYES | ELECTRODOMESTICOS,EQUIPOS ELECTRICOS |
| SIDRERIA RESTAURANTE GAZTELUPE | RESTAURANTES RESTO |
Refiriendome al anterior ejercicio para poder ordenar de manera ascendente hay que añadir un atributo en sort_values(column, ascending=False). Para seleccionar las columnas utilizamos filter(column). Es importante que primero se ordene el dataframe ya que si se filtra primero no tendremos la columna id_comercio.
1 2 3 4 5 6 7 | ( df_comercio .sort_values("id_comercio",ascending=False) .filter(["comercio","actividad_completa"]) .head(5) ) |
| comercio | actividad_completa | |
|---|---|---|
| 13807 | LAZCANO | SUPERMERCADOS,ULTRAMARINOS, ECONOMATOS |
| 13806 | RESTAURANTE OR DAGO | RESTAURANTES RESTO |
| 13805 | PORTO ALEGRE II | RESTAURANTES RESTO |
| 13804 | PC CITY S.SEBASTIAN REYES | ELECTRODOMESTICOS,EQUIPOS ELECTRICOS |
| 13803 | SIDRERIA RESTAURANTE GAZTELUPE | RESTAURANTES RESTO |
Ejercicio 5¶
- Sobre la tabla de movimientos, crea una nueva columna con el Iva (Importe * 21%)
- 2 Decimales
- Ordenado por fecha (descendente), hora (ascendente) y minuto (ascendente)
- Sólo 5 registros
1 2 3 4 5 6 7 8 | %%sql SELECT id_movimiento, importe, round(importe * 0.21, 2) as "Iva" FROM movimiento ORDER BY fecha ASC, id_movimiento ASC LIMIT 5; |
* sqlite:///tajetasblack.db Done.
| id_movimiento | importe | Iva |
|---|---|---|
| 6293 | 134.49 | 28.24 |
| 11314 | 60.97 | 12.8 |
| 15693 | 22.7 | 4.77 |
| 15694 | 48.08 | 10.1 |
| 15695 | 2.84 | 0.6 |
Para añadir una columna utilizamos el metodo assign(nombre_column = function).
1 2 3 4 5 6 7 | ( df_movimiento .sort_values(["fecha","id_movimiento"]) .filter(["id_movimiento","importe"]) .assign(Iva = lambda ds: round( ds.importe * 0.21, 2 )) .head(5) ) |
| id_movimiento | importe | Iva | |
|---|---|---|---|
| 6292 | 6293 | 134.49 | 28.24 |
| 11313 | 11314 | 60.97 | 12.80 |
| 15692 | 15693 | 22.70 | 4.77 |
| 15693 | 15694 | 48.08 | 10.10 |
| 15694 | 15695 | 2.84 | 0.60 |
Ejercicio 6¶
- Muestra el nombre del consejero junto con su cargo
- Ordenado el resultado por la primera columna (descendente)
- Sólo 5 registros
1 2 3 4 5 6 7 | %%sql SELECT id_consejero, nombre || ' es ' || funcion as "Nombre_Cargo" FROM consejero ORDER BY 1 DESC LIMIT 5; |
* sqlite:///tajetasblack.db Done.
| id_consejero | Nombre_Cargo |
|---|---|
| 83 | Domingo Navalmoral Sánchez es directivo |
| 82 | José Carlos Contreras Gómez es directivo |
| 81 | José María García Alonso es directivo |
| 80 | Rodrigo de Rato Figaredo es directivo |
| 79 | María Elena Gil García es directivo |
Prácticamente como el ejercicio anterior, hacemos el filter despues de la creación de Nombre_Cargo dado a que necesitamos columnas que no mostramos. Dentro del lambda es python por lo que solo con hacer una concatenación de Strings es suficiente para la función.
1 2 3 4 5 6 7 | ( df_consejero .sort_values("id_consejero", ascending=False) .assign(Nombre_Cargo = lambda ds: ds.nombre + " es " + ds.funcion) .filter(["id_consejero","Nombre_Cargo"]) .head(5) ) |
| id_consejero | Nombre_Cargo | |
|---|---|---|
| 82 | 83 | Domingo Navalmoral Sánchez es directivo |
| 81 | 82 | José Carlos Contreras Gómez es directivo |
| 80 | 81 | José María García Alonso es directivo |
| 79 | 80 | Rodrigo de Rato Figaredo es directivo |
| 78 | 79 | María Elena Gil García es directivo |
Ejercicio 7¶
- Muestra los conceales del Partido Popular
- Ordena por el nombre del concejal (ascendente)
- Solo 5 registros
1 2 3 4 5 6 7 8 | %%sql SELECT id_consejero, nombre FROM consejero WHERE funcion = 'concejal' AND organizacion = 'Partido Popular' ORDER BY nombre ASC LIMIT 5; |
* sqlite:///tajetasblack.db Done.
| id_consejero | nombre |
|---|---|
| 1 | Alberto Recarte García Andrade |
| 5 | Antonio Cámara Eguinoa |
| 9 | Beltrán Gutiérrez Moliner |
| 10 | Cándido Cerón Escudero |
| 13 | Estanislao Rodríguez-Ponga Salamanca |
Para filtrar filas empleamos query(), no es necesario ascending=True ya que es el por defecto.
1 2 3 4 5 6 7 | ( df_consejero .query("funcion == 'concejal' and organizacion == 'Partido Popular'", engine="python") .filter(["id_consejero","nombre"]) .sort_values("nombre") .head(5) ) |
| id_consejero | nombre | |
|---|---|---|
| 0 | 1 | Alberto Recarte García Andrade |
| 4 | 5 | Antonio Cámara Eguinoa |
| 8 | 9 | Beltrán Gutiérrez Moliner |
| 9 | 10 | Cándido Cerón Escudero |
| 12 | 13 | Estanislao Rodríguez-Ponga Salamanca |
Ejercicio 8¶
- Mostrar los movimientos cuyo importe es mayor a 1023 euros y menor a 1400
- Ordenado por importe
- Sólo 5 registros
1 2 3 4 5 6 7 | %%sql SELECT * FROM movimiento WHERE importe between 1023 and 1400 ORDER BY importe LIMIT 5; |
* sqlite:///tajetasblack.db Done.
| id_movimiento | fecha | hora | minuto | importe | id_consejero | id_comercio |
|---|---|---|---|---|---|---|
| 33910 | 2011-07-10 | 10 | 1 | 1024.16 | 38 | 7147 |
| 62166 | 2008-12-07 | 13 | 39 | 1024.65 | 65 | 12074 |
| 24810 | 2008-06-04 | 19 | 29 | 1025 | 27 | 5593 |
| 66222 | 2007-11-11 | 17 | 41 | 1028.05 | 69 | 1076 |
| 32979 | 2006-09-16 | 17 | 19 | 1030.59 | 38 | 736 |
El comando Between de SQL no esta implementado dentro de Pandas pero se puede realizar con una query normal.
1 2 3 4 5 6 | ( df_movimiento .query("1023 <= importe <= 1400", engine="python") .sort_values("importe") .head(5) ) |
| id_movimiento | fecha | hora | minuto | importe | id_consejero | id_comercio | |
|---|---|---|---|---|---|---|---|
| 33909 | 33910 | 2011-07-10 | 10 | 1 | 1024.16 | 38 | 7147 |
| 62165 | 62166 | 2008-12-07 | 13 | 39 | 1024.65 | 65 | 12074 |
| 24809 | 24810 | 2008-06-04 | 19 | 29 | 1025.00 | 27 | 5593 |
| 66221 | 66222 | 2007-11-11 | 17 | 41 | 1028.05 | 69 | 1076 |
| 32978 | 32979 | 2006-09-16 | 17 | 19 | 1030.59 | 38 | 736 |
Ejercicio 9¶
- Mostrar los consejeros cuyo id corresponde a los número 1, 2, 3, 5, 7 y 10
- Ordena por Id (descendente)
1 2 3 4 5 6 | %%sql SELECT * FROM consejero WHERE id_consejero in (1, 2, 3, 5, 7, 10) order by id_consejero desc |
* sqlite:///tajetasblack.db Done.
| id_consejero | nombre | funcion | organizacion |
|---|---|---|---|
| 10 | Cándido Cerón Escudero | concejal | Partido Popular |
| 7 | Antonio Romero Lázaro | concejal | PSOE |
| 5 | Antonio Cámara Eguinoa | concejal | Partido Popular |
| 3 | Ángel Eugenio Gómez del Pulgar Perales | concejal | PSOE |
| 2 | Alejandro Couceiro Ojeda | concejal | CEIM |
| 1 | Alberto Recarte García Andrade | concejal | Partido Popular |
1 2 3 4 5 | ( df_consejero .query("id_consejero in (1,2,3,5,7,10)", engine="python") .sort_values("id_consejero", ascending=False) ) |
| id_consejero | nombre | funcion | organizacion | |
|---|---|---|---|---|
| 9 | 10 | Cándido Cerón Escudero | concejal | Partido Popular |
| 6 | 7 | Antonio Romero Lázaro | concejal | PSOE |
| 4 | 5 | Antonio Cámara Eguinoa | concejal | Partido Popular |
| 2 | 3 | Ángel Eugenio Gómez del Pulgar Perales | concejal | PSOE |
| 1 | 2 | Alejandro Couceiro Ojeda | concejal | CEIM |
| 0 | 1 | Alberto Recarte García Andrade | concejal | Partido Popular |
Ejercicio 10¶
Muestra los comercios con el identificador de actividad vacio
1 2 3 4 5 | %%sql SELECT * FROM comercio WHERE id_actividad IS NULL |
* sqlite:///tajetasblack.db Done.
| id_comercio | comercio | actividad_completa | id_actividad |
|---|---|---|---|
| 4472 | None | None | None |
| 9412 | BLANCO-SUMMER-CC.ZIELO | BLANCO | None |
| 12725 | SALA RETIRO CAJA DE MADRID | None | None |
| 13277 | MOVISTAR | None | None |
No se puede hacer mediante comandos sql por lo que recurrimos a la busqueda por serie booleana.
1 | df_comercio.query("id_actividad.isnull()",engine="python") |
| id_comercio | comercio | actividad_completa | id_actividad | |
|---|---|---|---|---|
| 4471 | 4472 | None | None | NaN |
| 9411 | 9412 | BLANCO-SUMMER-CC.ZIELO | BLANCO | NaN |
| 12724 | 12725 | SALA RETIRO CAJA DE MADRID | None | NaN |
| 13276 | 13277 | MOVISTAR | None | NaN |
Ejercicio 11¶
- Obtener comercios ordenados por su nombre en forma descendente
- Que no tengan el nombre a nulo
- Que no tengan un ID cuyo valor sea 650 o 6788
- Sólo 5 registros
Pista: La ordenación distingue entre mayúsculas y minúsculas, por lo que a la hora de ordenar hazlo sobre una columna auxiliar donde estén todos los valores en mayúscula
1 2 3 4 5 6 7 8 | %%sql SELECT id_comercio, comercio, actividad_completa FROM comercio WHERE comercio is not null and id_comercio not in (650, 6788) ORDER BY comercio COLLATE NOCASE DESC LIMIT 5 |
* sqlite:///tajetasblack.db Done.
| id_comercio | comercio | actividad_completa |
|---|---|---|
| 12863 | ZUBEROA JATEXEA | RESTAURANTES DE 4 Y 5 TENEDORES |
| 1352 | ZORZAL | RESTAURANTES RESTO |
| 7322 | ZOO AQUARIUM MADRID - TPV PC | ESPECTACULOS Y DEPORTES |
| 3567 | ZOO AQUARIUM | ESPECTACULOS Y DEPORTES |
| 7066 | ZONULA S.L. | FOTOGRAFIA,EQUIPOS CINE,OPTICA,INST.MEDI |
Al pedirnos todos los datos no nulso podemos utilizar el metodo de la serie notnull(). Y para ordenar hay que crear una columna de comercio de mayúsculas para poder ordenar bien. Y ascending=False.
1 2 3 4 5 6 7 8 | ( df_comercio .query("comercio.notnull() and id_comercio not in (650, 6788)", engine="python") .assign(comercio_Mayus = lambda ds: ds.comercio.str.upper()) .sort_values("comercio_Mayus",ascending=False) .filter(["id_comercio","comercio","actividad_completa"]) .head(5) ) |
| id_comercio | comercio | actividad_completa | |
|---|---|---|---|
| 12862 | 12863 | ZUBEROA JATEXEA | RESTAURANTES DE 4 Y 5 TENEDORES |
| 1351 | 1352 | ZORZAL | RESTAURANTES RESTO |
| 7321 | 7322 | ZOO AQUARIUM MADRID - TPV PC | ESPECTACULOS Y DEPORTES |
| 3566 | 3567 | ZOO AQUARIUM | ESPECTACULOS Y DEPORTES |
| 7065 | 7066 | ZONULA S.L. | FOTOGRAFIA,EQUIPOS CINE,OPTICA,INST.MEDI |
Ejercicio 12¶
- Mostrar los consejeros cuyo nombre empieza por A y su apellido por R
- Ordenado por nombre (ascendente)
1 2 3 4 5 6 | %%sql SELECT * FROM consejero WHERE nombre LIKE 'A% R%' ORDER BY nombre |
* sqlite:///tajetasblack.db Done.
| id_consejero | nombre | funcion | organizacion |
|---|---|---|---|
| 1 | Alberto Recarte García Andrade | concejal | Partido Popular |
| 4 | Angel Rizaldos González | concejal | Izquierda Unida |
| 6 | Antonio Rey de Viñas Sánchez-Majestad | concejal | CC OO |
| 7 | Antonio Romero Lázaro | concejal | PSOE |
No funciona LIKE en el query pero podemos utilizar los metodos de str dentro del query y utilizar expresiones regulares para obtener los consejeros que empiezan por A y su apellido por R.
1 2 3 4 5 | ( df_consejero .query("nombre.str.contains(pat='^A+.*R')", engine="python") .sort_values("nombre") ) |
| id_consejero | nombre | funcion | organizacion | |
|---|---|---|---|---|
| 0 | 1 | Alberto Recarte García Andrade | concejal | Partido Popular |
| 3 | 4 | Angel Rizaldos González | concejal | Izquierda Unida |
| 5 | 6 | Antonio Rey de Viñas Sánchez-Majestad | concejal | CC OO |
| 6 | 7 | Antonio Romero Lázaro | concejal | PSOE |
Ejercicio 13¶
- Muestra los consejeros cuyo ID sea menor o igual a 5
- El consejero cuyo ID es 3 debe de salir 2 veces
- Ordenado por ID (ascendente)
Pista: Duplica la tabla consejero para obtener el dato repetido
1 2 3 4 5 6 7 8 9 10 | %%sql SELECT * FROM consejero WHERE id_consejero BETWEEN 1 AND 5 UNION ALL SELECT * FROM consejero WHERE id_consejero = 3 ORDER BY id_consejero |
* sqlite:///tajetasblack.db Done.
| id_consejero | nombre | funcion | organizacion |
|---|---|---|---|
| 1 | Alberto Recarte García Andrade | concejal | Partido Popular |
| 2 | Alejandro Couceiro Ojeda | concejal | CEIM |
| 3 | Ángel Eugenio Gómez del Pulgar Perales | concejal | PSOE |
| 3 | Ángel Eugenio Gómez del Pulgar Perales | concejal | PSOE |
| 4 | Angel Rizaldos González | concejal | Izquierda Unida |
| 5 | Antonio Cámara Eguinoa | concejal | Partido Popular |
Creamos un duplicado con el query restrictivo y lo añadimos el dataframe entero y hacemos el otro query para obtener la tabla deseada.
1 2 3 4 5 6 7 | df_clone = df_consejero.query("id_consejero == 3", engine="python") df_clone = pd.concat([df_consejero,df_clone]) ( df_clone .query("1 <= id_consejero <= 5", engine="python") .sort_values("id_consejero") ) |
| id_consejero | nombre | funcion | organizacion | |
|---|---|---|---|---|
| 0 | 1 | Alberto Recarte García Andrade | concejal | Partido Popular |
| 1 | 2 | Alejandro Couceiro Ojeda | concejal | CEIM |
| 2 | 3 | Ángel Eugenio Gómez del Pulgar Perales | concejal | PSOE |
| 2 | 3 | Ángel Eugenio Gómez del Pulgar Perales | concejal | PSOE |
| 3 | 4 | Angel Rizaldos González | concejal | Izquierda Unida |
| 4 | 5 | Antonio Cámara Eguinoa | concejal | Partido Popular |
Ejercicio 14¶
Muestra las distintas funciones que tienen los consejeros
1 2 3 4 | %%sql SELECT DISTINCT funcion FROM consejero |
* sqlite:///tajetasblack.db Done.
| funcion |
|---|
| concejal |
| directivo |
Para quitar duplicados usamos drop_duplicates()
1 2 3 4 5 6 | ( df_consejero .filter(["funcion"]) .drop_duplicates() ) |
| funcion | |
|---|---|
| 0 | concejal |
| 64 | directivo |
Ejercicio 15¶
Muestra el total de movimientos, el importe mínimo, máximo y medio (2 decimales), para todos los importes positivos
Resutado con Pandas:
1 2 3 4 5 6 7 8 | %%sql SELECT count(*) as "Total_Movimientos", min(importe) as "Importe_Minimo", max(importe) as "Importe_Maximo", avg(importe) as "Importe_Medio" FROM movimiento WHERE importe > 0 |
* sqlite:///tajetasblack.db Done.
| Total_Movimientos | Importe_Minimo | Importe_Maximo | Importe_Medio |
|---|---|---|---|
| 75567 | 0.05 | 16921.75 | 156.45903238185838 |
Filtramos los importes positivos, filtramos las columnas que queremos y agregamos las operaciones.
1 2 3 4 5 6 7 8 9 10 11 12 | ( df_movimiento .query("importe > 0", engine="python") .filter(["id_movimiento","importe"]) .agg( Total_Movimientos = ("id_movimiento", "count"), Importe_Minimo = ("importe", "min"), Importe_Maximo = ("importe", "max"), Importe_Medio = ("importe", "mean") ) .round({"importe": 2}) # Para solo redondear la columna importe ) |
| id_movimiento | importe | |
|---|---|---|
| Total_Movimientos | 75567.0 | NaN |
| Importe_Minimo | NaN | 0.05 |
| Importe_Maximo | NaN | 16921.75 |
| Importe_Medio | NaN | 156.46 |
Ejercicio 16¶
- Obten los siguientes datos agrupados de la tabla de movimientos:
- Fecha del primer movimiento
- Fecha del último movimiento
- Número de días que han pasado desde el último al primer movimento
- Suma de Importes
Pista: Para obtener el número de días entre 2 fechas se tienen que restar las fechas
Resutado con Pandas:
1 2 3 4 5 6 7 | %%sql select min(fecha) as "Inicio", max(fecha) as "Fin", (JULIANDAY(max(fecha)) - JULIANDAY(min(fecha))) as "Num_Dias", sum(importe) as "Sum_Importe" FROM movimiento |
* sqlite:///tajetasblack.db Done.
| Inicio | Fin | Num_Dias | Sum_Importe |
|---|---|---|---|
| 2002-12-31 | 2012-08-28 | 3528.0 | 11659204.609999878 |
Filtramos por fecha e importe y agregamos Inicio que el la fecha más pequeña, Fin que es la fecha más grande, Num_dias es la diferencia y Sum_importe es la suma de importe.
1 2 3 4 5 6 7 8 9 10 | ( df_movimiento .filter(["fecha", "importe"]) .agg( Inicio = ("fecha", "min"), Fin = ("fecha", "max"), Num_dias = ("fecha", lambda s: s.max() - s.min()), Sum_importe = ("importe", "sum") ) ) |
| fecha | importe | |
|---|---|---|
| Inicio | 2002-12-31 00:00:00 | NaN |
| Fin | 2012-08-28 00:00:00 | NaN |
| Num_dias | 3528 days 00:00:00 | NaN |
| Sum_importe | NaN | 11659204.61 |
Ejercicio 17¶
Muestra las 5 horas de día donde se gasta más dinero
1 2 3 4 5 6 7 | %%sql select hora, round(sum(importe), 2) as "Importe" from movimiento group by hora order by 2 desc limit 5 |
* sqlite:///tajetasblack.db Done.
| hora | Importe |
|---|---|
| 15 | 2256615.99 |
| 16 | 1951272.07 |
| 17 | 902056.68 |
| 14 | 897004.71 |
| 19 | 636204.99 |
Para hacer la consulta agrupamos por hora y sumamos todo el importe. ordenamos descendiente y se cogen los 5 primeros.
1 2 3 4 5 6 7 8 9 10 | ( df_movimiento .groupby("hora") .agg( Importe = ("importe", "sum") ) .sort_values("Importe", ascending=False) .head(5) ) |
| Importe | |
|---|---|
| hora | |
| 15 | 2256615.99 |
| 16 | 1951272.07 |
| 17 | 902056.68 |
| 14 | 897004.71 |
| 19 | 636204.99 |
Ejercicio 18.¶
- Muestra las 5 organizaciones con mas miembros
- No tengas en cuenta valoles nulos
1 2 3 4 5 6 7 8 | %%sql select organizacion, count(*) as "Numero_miembros" from consejero where organizacion is not null group by organizacion order by 2 desc limit 5 |
* sqlite:///tajetasblack.db Done.
| organizacion | Numero_miembros |
|---|---|
| Partido Popular | 27 |
| PSOE | 15 |
| CC OO | 6 |
| Izquierda Unida | 5 |
| UGT | 4 |
Primero obtenemos los valores no nulos de organizacion, los agrupamos por organización y añadimos Numero_miembros que cuenta el Numero de filas con la misma organizacion. Se ordena y se muestra
1 2 3 4 5 6 7 8 9 10 | ( df_consejero .query("organizacion.notnull()",engine="python") .groupby("organizacion") .agg( Numero_miembros = ("organizacion","size") ) .sort_values("Numero_miembros",ascending=False) .head(5) ) |
| Numero_miembros | |
|---|---|
| organizacion | |
| Partido Popular | 27 |
| PSOE | 15 |
| CC OO | 6 |
| Izquierda Unida | 5 |
| UGT | 4 |
Ejercicio 19¶
Muestra los comercios con más de 1 registro por actividad, ordenando el resultado por e número de actividades (descendente) y el nombre del comercio (ascendente)
1 2 3 4 5 6 7 8 9 | %%sql select comercio, id_actividad, count(*) as "Numero_actividades" from comercio where comercio IS NOT NULL group by comercio, id_actividad having count(*) > 1 order by 3 desc, 1 ASC limit 5 |
* sqlite:///tajetasblack.db Done.
| comercio | id_actividad | Numero_actividades |
|---|---|---|
| DATAFONOS PARKING P4 | 4 | 4 |
| EMBASSY | 3 | 4 |
| LA VACA ARGENTINA | 3 | 4 |
| TICKETMASTER VENTA ENTRAD | 13 | 4 |
| APARCAMIENTO CLINICA LA CONCEP | 4 | 3 |
para poder contar cuantas actividades tiene cada comercio hay que sumar cuantos filas del mismo comercio y para ordenar es el sort_values en modo de lista.
1 2 3 4 5 6 7 8 9 10 | ( df_comercio .query("comercio.notnull()",engine="python") .groupby(["comercio","id_actividad"]) .agg( Numero_actividades = ("comercio", "size") ) .sort_values(["Numero_actividades","comercio"],ascending =[False,True]) .head(5) ) |
| Numero_actividades | ||
|---|---|---|
| comercio | id_actividad | |
| DATAFONOS PARKING P4 | 4.0 | 4 |
| EMBASSY | 3.0 | 4 |
| LA VACA ARGENTINA | 3.0 | 4 |
| TICKETMASTER VENTA ENTRAD | 13.0 | 4 |
| APARCAMIENTO CLINICA LA CONCEP | 4.0 | 3 |
Ejercicio 20¶
Muestra los 5 movimientos que tengan mayor importe
1 2 3 4 5 6 7 8 | %%sql select c.nombre, m.fecha, m.importe from movimiento as m INNER JOIN consejero as c ON (m.id_consejero = c.id_consejero) order by m.importe DESC limit 5 |
* sqlite:///tajetasblack.db Done.
| nombre | fecha | importe |
|---|---|---|
| Ildefonso José Sánchez Barcoj | 2009-12-30 | 16921.75 |
| Matías Amat Roca | 2006-12-26 | 15000 |
| Ramón Ferraz Ricarte | 2007-12-19 | 13549 |
| Miguel Blesa de la Parra | 2006-07-19 | 13148.29 |
| Miguel Blesa de la Parra | 2006-04-04 | 12597.26 |
Empleamos merge(df, how, on) para hacer los join luego es mostrar por pantalla lo pedido.
1 2 3 4 5 6 7 | ( df_movimiento .merge(df_consejero,how= "inner", on="id_consejero") .filter(["nombre","fecha","importe"]) .sort_values("importe",ascending=False) .head(5) ) |
| nombre | fecha | importe | |
|---|---|---|---|
| 62488 | Ildefonso José Sánchez Barcoj | 2009-12-30 | 16921.75 |
| 66104 | Matías Amat Roca | 2006-12-26 | 15000.00 |
| 65386 | Ramón Ferraz Ricarte | 2007-12-19 | 13549.00 |
| 64137 | Miguel Blesa de la Parra | 2006-07-19 | 13148.29 |
| 64056 | Miguel Blesa de la Parra | 2006-04-04 | 12597.26 |
Ejercicio 21¶
Muestra los comercios que no tienen asociada una actividad
1 2 3 4 5 6 7 8 | %%sql select c.*, a.actividad from comercio as c LEFT JOIN actividad as a ON (c.id_actividad = a.id_actividad) where a.id_actividad IS NULL order by id_comercio ASC |
* sqlite:///tajetasblack.db Done.
| id_comercio | comercio | actividad_completa | id_actividad | actividad |
|---|---|---|---|---|
| 4472 | None | None | None | None |
| 9412 | BLANCO-SUMMER-CC.ZIELO | BLANCO | None | None |
| 12725 | SALA RETIRO CAJA DE MADRID | None | None | None |
| 13277 | MOVISTAR | None | None | None |
Como nos piden los comercios que no tengan asociadas ninguna actividad tendremos que hacer un join del comercio entero para obtener valores nulos luego solo debemos mostrar los nulos por pantalla.
1 2 3 4 5 6 | ( df_comercio .merge(df_actividad, how="left", on="id_actividad") .query("id_actividad.isnull()",engine="python") .sort_values("id_comercio") ) |
| id_comercio | comercio | actividad_completa | id_actividad | actividad | |
|---|---|---|---|---|---|
| 4471 | 4472 | None | None | NaN | NaN |
| 9411 | 9412 | BLANCO-SUMMER-CC.ZIELO | BLANCO | NaN | NaN |
| 12724 | 12725 | SALA RETIRO CAJA DE MADRID | None | NaN | NaN |
| 13276 | 13277 | MOVISTAR | None | NaN | NaN |
Ejercicio 22¶
Muestra el importe agregado por consejero y actividad
1 2 3 4 5 6 7 8 9 | %%sql select c.nombre, a.actividad, sum(importe) as Sum_Importe from movimiento as m INNER JOIN consejero as c ON (m.id_consejero = c.id_consejero) INNER JOIN comercio as comer ON (m.id_comercio = comer.id_comercio) LEFT JOIN actividad as a ON (comer.id_actividad = a.id_actividad) group by 1, 2 order by 3 DESC limit 5 |
* sqlite:///tajetasblack.db Done.
| nombre | actividad | Sum_Importe |
|---|---|---|
| Ricardo Morado Iglesias | BANCO | 398902 |
| José Antonio Moral Santín | CA$H | 367809.82 |
| Ildefonso José Sánchez Barcoj | BANCO | 219123.41999999998 |
| Ramón Ferraz Ricarte | CA$H | 172817.0400000001 |
| Ildefonso José Sánchez Barcoj | COMPRA BIENES | 154770.60000000024 |
Podemos hacerlo mediante innerjoins todos menos actividad que se debe hacer mediante un left. Agrupamos por nombre y actividad y creamos nueva columna que es la suma de los importes. Por último se ordenan por Sum_Importe de manera descendente.
1 2 3 4 5 6 7 8 9 10 11 | ( df_movimiento .merge(df_consejero,how="inner",on="id_consejero") .merge(df_comercio, how="inner", on="id_comercio") .merge(df_actividad, how="left", on="id_actividad") .groupby(["nombre","actividad"]).agg( Sum_Importe = ("importe", "sum") ) .sort_values("Sum_Importe", ascending=False) .head(5) ) |
| Sum_Importe | ||
|---|---|---|
| nombre | actividad | |
| Ricardo Morado Iglesias | BANCO | 398902.00 |
| José Antonio Moral Santín | CA$H | 367809.82 |
| Ildefonso José Sánchez Barcoj | BANCO | 219123.42 |
| Ramón Ferraz Ricarte | CA$H | 172817.04 |
| Ildefonso José Sánchez Barcoj | COMPRA BIENES | 154770.60 |
Ejercicio 23¶
Muestra la media de los importes medios mensuales
1 2 3 4 5 6 7 8 9 10 | %%sql with subquery as ( select strftime('%m', fecha) as mes, avg(importe) as importe from movimiento group by 1 ) select round(avg(importe), 2) as "Media importes mensuales" from subquery |
* sqlite:///tajetasblack.db Done.
| Media importes mensuales |
|---|
| 152.51 |
Primero tenemos que utilizar DatetimeIndex para obtener el mes para agruparlo y hacer la media de cada mes por último hacemos la media de esa columna y la redondeamos a 2.
1 2 3 4 5 6 7 8 9 10 11 | ( df_movimiento .assign( mes = lambda df: pd.DatetimeIndex(df["fecha"]).month ) .groupby("mes").agg( avg_importe = ("importe","mean"), ) .avg_importe.mean().round(2) ) |
152.51
Ejercicio 24¶
Muestra los 5 importes mas altos que están debajo de la media ordenados por importe (descendente) y fecha
1 2 3 4 5 6 7 8 9 10 | %%sql select c.nombre, m.fecha, m.importe from movimiento as m INNER JOIN consejero as c ON (m.id_consejero = c.id_consejero) where importe < ( select avg(importe) from movimiento) order by m.importe DESC, m.fecha limit 5 |
* sqlite:///tajetasblack.db Done.
| nombre | fecha | importe |
|---|---|---|
| Rubén Cruz Orive | 2004-11-04 | 152.91 |
| Carmen Contreras Gómez | 2006-03-31 | 152.91 |
| Rubén Cruz Orive | 2008-05-01 | 152.91 |
| Carmen Contreras Gómez | 2003-04-08 | 152.89 |
| Carmen Contreras Gómez | 2003-07-13 | 152.89 |
Para poder hacer el query es necesario conocer el valor de la media del importe por lo que creamos una nueva columna con ese valor y procedemos ha hacer las indicaciones del ejercicio empleando todo lo utilizado anteriormente.
1 2 3 4 5 6 7 8 9 | ( df_movimiento .merge(df_consejero, how="inner", on="id_consejero") .assign(avg_importe = lambda ds: ds.importe.mean()) .query("importe < avg_importe",engine="python") .sort_values(["importe","fecha"],ascending=[False,True]) .filter(["nombre","fecha","importe"]) .head(5) ) |
| nombre | fecha | importe | |
|---|---|---|---|
| 54999 | Rubén Cruz Orive | 2004-11-04 | 152.91 |
| 69737 | Carmen Contreras Gómez | 2006-03-31 | 152.91 |
| 56264 | Rubén Cruz Orive | 2008-05-01 | 152.91 |
| 69310 | Carmen Contreras Gómez | 2003-04-08 | 152.89 |
| 69386 | Carmen Contreras Gómez | 2003-07-13 | 152.89 |
Ejercicio 25¶
- Muestra los 5 primeros consejeros con un mayor importe medio
1 2 3 4 5 6 7 8 9 | %%sql select nombre as "Consejero", (select avg(importe) from movimiento as m where m.id_consejero = c.id_consejero) as "Importe_Medio" from consejero as c order by 2 desc limit 5 |
* sqlite:///tajetasblack.db Done.
| Consejero | Importe_Medio |
|---|---|
| Ricardo Morado Iglesias | 1675.4673962264155 |
| Gerardo Díaz Ferrán | 1357.8145161290322 |
| Enrique de la Torre Martínez | 820.7129347826086 |
| Ramón Ferraz Ricarte | 477.60496909765124 |
| Matías Amat Roca | 423.5705408163269 |
Para realizar este ejercicio es requerido hacer un inner join de movimientos y consejero agrupar por nombre, hallar la media del importe y ordenar de manera descendente
1 2 3 4 5 6 7 8 9 10 11 | ( df_consejero .merge(df_movimiento, how="inner", on="id_consejero") .groupby("nombre").agg( Importe_Medio = ("importe", "mean") ) .rename({"nombre" : "Consejero"}) .filter(["Consejero","Importe_Medio"]) .sort_values("Importe_Medio",ascending=False) .head(5) ) |
| Importe_Medio | |
|---|---|
| nombre | |
| Ricardo Morado Iglesias | 1675.467396 |
| Gerardo Díaz Ferrán | 1357.814516 |
| Enrique de la Torre Martínez | 820.712935 |
| Ramón Ferraz Ricarte | 477.604969 |
| Matías Amat Roca | 423.570541 |
1 |