# A veces necesitamos instalar nuevas librerías en nuestros proyectos
!pip install openpyxl==3.0.10
# imports
import numpy as np
import pandas as pd
import sqlite3 as sql3
import openpyxl # para que levante bien el excel
import matplotlib.pyplot as plt
import seaborn as sns
# Levanto los datos en 3 diferentes dataframes
# Artículos
conn = sql3.connect('/work/data/articles.db')
sql_query = pd.read_sql_query('SELECT * FROM articles', conn)
df_articles = pd.DataFrame(sql_query, columns = ['article_id', 'article_name', 'unit_price'])
# Vendedores
df_sellers = pd.read_excel('/work/data/sellers.xlsx', index_col=0)
# Órdenes
df_orders = pd.read_csv('/work/data/orders.csv')
# Exploración del df de artículos
print('Muestra de datos')
print(df_articles.head())
print('\nFormato del dataframe')
print(df_articles.shape)
print('\nBúsqueda de valores nulos por columna')
print(df_articles.isnull().sum())
print('\nFormato de los datos por columna')
print(df_articles.dtypes)
# Exploración del df de vendedores
print('Muestra de datos')
print(df_sellers.head())
print('\nFormato del dataframe')
print(df_sellers.shape)
print('\nBúsqueda de valores nulos por columna')
print(df_sellers.isnull().sum())
print('\nFormato de los datos por columna')
print(df_sellers.dtypes)
# Exploración del df de órdenes
print('Muestra de datos')
print(df_orders.head())
print('\nFormato del dataframe')
print(df_orders.shape)
print('\nBúsqueda de valores nulos por columna')
print(df_orders.isnull().sum())
print('\nFormato de los datos por columna')
print(df_orders.dtypes)
df_articles['unit_price'] = df_articles['unit_price'].astype(float)
print(df_articles.dtypes)
# Creo una copia del df_orders
my_df = df_orders.copy()
# Cambio el índice del df de artículos
df_articles.set_index('article_id',inplace = True)
# Agrego algunas columnas y pongo el campo que me va a servir de "ancla" para acordarme
my_df = my_df.assign(article_name = my_df['article_id'])
my_df = my_df.assign(total_amount = my_df['article_id'])
my_df = my_df.assign(seller_name = my_df['seller_id'])
# reemplazo los valores en el nuevo df
# 1. busco el nombre del artículo y lo asigno a una variable
# como df_articles está indexado por article_id, lo uso para ubicarme en
# el registro que busco
# SINTAXIS: df_articles.loc[indice][columna]
# [indice] va a ser el dato que obtengo de my_df.loc[i]['article_id']
# -> o sea, tomo registro a registro el article_id y lo uso para extraer
# el nombre del artículo
for i in range(len(my_df.index)): # len(my_df.index) devuelve la cantidad de registros
idArticulo = my_df.loc[i]['article_id']
nombreArticulo = df_articles.loc[idArticulo]['article_name']
# se lo asigno a la columna correspondiente
my_df.loc[i,'article_name'] = nombreArticulo
# hago lo mismo en un solo paso para la columna de total_amount
precioArticulo = df_articles.loc[idArticulo]['unit_price']
cantArticulo = my_df.loc[i]['quantity']
my_df.loc[i,'total_amount'] = precioArticulo * cantArticulo
# columna de seller_name
idVendedor = my_df.loc[i]['seller_id']
nombreVendedor = df_sellers.loc[idVendedor]['seller_name']
my_df.loc[i,'seller_name'] = nombreVendedor
# elimino las columnas que no necesito
my_df.drop(['order_id', 'article_id', 'seller_id'], axis = 'columns', inplace=True)
print(my_df)
# SI SOBRA TIEMPO Se puede mencionar la función merge para hacer joins entre dataframes
# https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.merge.html
d1=pd.DataFrame({'mes': ['ene','feb','mar','abr'], 'ventas':[10,20,30,15]})
d2=pd.DataFrame({'mes': ['ene','feb','mar','abr'], 'costos':[7,16,25,12]})
print(pd.merge(d1,d2))
# RESOLUCIÓN ANALÍTICA
my_df2=my_df.groupby('article_name').sum()
por_cant = my_df2.sort_values('quantity', ascending=False)
print(por_cant['quantity'].head(1))
# RESOLUCIÓN GRÁFICA
sns.barplot(y=por_cant["quantity"], x=por_cant.index)
plt.xticks(rotation=90)
plt.show()
# RESOLUCIÓN ANALÍTICA
df2 = (my_df.groupby(by='article_name').sum()).sort_values('total_amount',ascending=False).head(5)
print(df2['total_amount'])
# RESOLUCIÓN GRÁFICA ---> OJO: Voy a tomar sólo los 5 primeros artículos que más ingresos proporcionaron
# No puedo hacer una comparativa ni porcentajes porque no tomé el total de los datos
#plt.pie(x=df2['total_amount'], labels=df2.index)
#plt.show()
plt.barh(df2.index,df2['total_amount'] , height=0.8)
plt.show()
# RESOLUCIÓN ANALÍTICA
df4 = (my_df.groupby(by='seller_name').sum()).sort_values('total_amount',ascending=False)
print(df4[['quantity']+['total_amount']])
# RESOLUCIÓN GRÁFICA
plt.bar(df4.index,df4['total_amount'])
plt.xticks(rotation=60, ha="right")
plt.show()
# RESOLUCIÓN ANALÍTICA
df5 = (my_df.groupby(by='week').sum()).sort_values('total_amount',ascending=False)
print(df5['total_amount'])
# RESOLUCIÓN GRÁFICA
semanas = ['1','2','3','4']
plt.plot(semanas, df5['total_amount'], 'or:')
plt.xlabel("Semanas")
plt.ylabel("Totales en U$S")
plt.show()
# RESOLUCIÓN ANÁLITICA
# ------------------------- CREAR LA CLAVE "OTROS" ------------------------
'''
La clave "Otros" guardará el acumulado de los países ajenos al top 5.
La idea es crear un gráfico circular. Si muestro el gráfico con todos los
valores del DF, la información es precisa pero poco legible. Si muestro
únicamente el top 5, sin tener en cuenta los demás datos, la información es
legible pero imprecisa. En cambio, mostrando el top 5 y el resto de los países
agrupados bajo una clave "Otros", se logran las dos cosas: información legible
y precisa
'''
# Crear un DF que contenga los totales de los países, ordenados de mayor a menor
compras = my_df.groupby('country_name').sum().sort_values('total_amount', ascending=False)
# Crear un pandas series "top5" que almacene los 5 primeros países
top5 = compras['total_amount'].head()
# ------------- Acumula la suma de los países ajenos al top 5 --------------
acumulador = 0.0
# El bloque FOR comienza desde el sexto elemento hasta el final del DF
for p in range(5, len(compras)):
acumulador += compras['total_amount'][p]
# --------------------------------------------------------------------------
# Agrego el nuevo valor a la serie
top5['Otros']= acumulador
print(top5)
# RESOLUCIÓN GRÁFICA
# Colores de las porciones
colores = ['tab:orange', 'tab:cyan', 'tab:gray', 'tab:brown', 'tab:green', 'tab:red' ]
# Valores que representan si las porciones están separadas (> 0) o no (== 0)
desfase = [0, 0, 0, 0, 0, 0.15]
plt.pie(x=top5, labels=top5.index, autopct="%0.1f %%", colors=colores, shadow=True, explode=desfase)
plt.title('Top 5 países que más compraron')
plt.show()
# RESOLUCIÓN ANALÍTICA
print("\nPaís que más monitores compró en el mes (en unidades")
df2 = my_df.groupby(by=['article_name', 'country_name']).sum().query('article_name == "Monitor"').sort_values(by='quantity', ascending=False)
print(df2['quantity'].head(1))
print("\nPaís que menos monitores compró en el mes (en unidades")
df2 = my_df.groupby(by=['article_name', 'country_name']).sum().query('article_name == "Monitor"').sort_values(by='quantity', ascending=True)
print(df2['quantity'].head(1))
# RESOLUCIÓN ANALÍTICA
df7 = my_df.query('week==4').groupby(by='seller_name').sum().sort_values(by='quantity', ascending=False).head(3)
print(df7['quantity'])
# RESOLUCIÓN GRÁFICA
# "Cantidades_vendidas" es un numpy array que contiene las cantidades de los 3 vendedores
cantidades_vendidas = df7['quantity'].values
'''
El bucle FOR hace el trabajo de colocar las etiquetas en las correspondientes barras.
Para lograrlo uso el método annotate, a la que le paso 2 parámetros:
1) La variable "cant" que, en cada iteración del bucle, guarda la cantidad de cada vendedor
2) Una tupla, que va a ser la posición (x, y) donde se va a imprimir la etiqueta "cant".
'''
for i, cant in enumerate(cantidades_vendidas):
plt.annotate(cant, (df7.index[i],cant))
plt.bar(df7.index,df7['quantity'])
plt.title('Vendedores que más artículos vendieron en la última semana', fontsize='large')
plt.xlabel('Vendedor')
plt.ylabel(ylabel='Cantidad')
plt.show()