# A veces necesitamos instalar nuevas librerías en nuestros proyectos
!pip install openpyxl==3.0.10
Collecting openpyxl==3.0.10
Downloading openpyxl-3.0.10-py2.py3-none-any.whl (242 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 242.1/242.1 KB 20.7 MB/s eta 0:00:00
Collecting et-xmlfile
Downloading et_xmlfile-1.1.0-py3-none-any.whl (4.7 kB)
Installing collected packages: et-xmlfile, openpyxl
Successfully installed et-xmlfile-1.1.0 openpyxl-3.0.10
WARNING: You are using pip version 22.0.4; however, version 22.1.2 is available.
You should consider upgrading via the '/root/venv/bin/python -m pip install --upgrade pip' command.
# 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
import warnings
warnings.filterwarnings("ignore")
# Levanto los datos en 3 diferentes dataframes
# Artículos
conn = sql3.connect('/work/data/articles.db')
df_articles = 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)
Muestra de datos
article_id article_name unit_price
0 20015 Smartphone 525.00
1 20016 Full Pc 2127.81
2 20017 Monitor 230.00
3 20018 Tablet 130.00
4 20019 Desk 130.10
Formato del dataframe
(31, 3)
Búsqueda de valores nulos por columna
article_id 0
article_name 0
unit_price 0
dtype: int64
Formato de los datos por columna
article_id int64
article_name object
unit_price object
dtype: object
# 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)
Muestra de datos
seller_name
seller_id
1 Aveline Swanwick
2 Jase Doy
3 Oliviero Charkham
4 Cornie Wynrehame
5 Ewell Peres
Formato del dataframe
(15, 1)
Búsqueda de valores nulos por columna
seller_name 0
dtype: int64
Formato de los datos por columna
seller_name object
dtype: object
# 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)
Muestra de datos
order_id week article_id quantity seller_id country_name
0 15024 1 20039 10 10 Peru
1 15025 1 20029 15 5 Peru
2 15026 1 20024 5 14 Bolivia
3 15027 1 20018 9 14 Brazil
4 15028 1 20035 6 15 Mexico
Formato del dataframe
(1000, 6)
Búsqueda de valores nulos por columna
order_id 0
week 0
article_id 0
quantity 0
seller_id 0
country_name 0
dtype: int64
Formato de los datos por columna
order_id int64
week int64
article_id int64
quantity int64
seller_id int64
country_name object
dtype: object
df_articles['unit_price'] = df_articles['unit_price'].astype(float)
print(df_articles.dtypes)
article_id int64
article_name object
unit_price float64
dtype: object
# 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
id_articulo = my_df.loc[i]['article_id']
nombre_articulo = df_articles.loc[id_articulo]['article_name']
# se lo asigno a la columna correspondiente
my_df.loc[i,'article_name'] = nombre_articulo
# hago lo mismo en un solo paso para la columna de total_amount
precio_articulo = df_articles.loc[id_articulo]['unit_price']
cant_articulo = my_df.loc[i]['quantity']
my_df.loc[i,'total_amount'] = precio_articulo * cant_articulo
# columna de seller_name
id_vendedor = my_df.loc[i]['seller_id']
nombre_vendedor = df_sellers.loc[id_vendedor]['seller_name']
my_df.loc[i,'seller_name'] = nombre_vendedor
# elimino las columnas que no necesito
my_df.drop(['order_id', 'article_id', 'seller_id'], axis = 'columns', inplace=True)
print(my_df)
week quantity country_name article_name total_amount seller_name
0 1 10 Peru Water Cooling 675.0 Cirilo Grandham
1 1 15 Peru Mouse 454.5 Ewell Peres
2 1 5 Bolivia Netbook 725.0 Janel O'Curran
3 1 9 Brazil Tablet 1170.0 Janel O'Curran
4 1 6 Mexico Case 227.4 Daisie Slograve
.. ... ... ... ... ... ...
995 4 1 Brazil Modem 67.5 Kati Innot
996 4 15 Brazil Heatsink 150.0 Daisie Slograve
997 4 2 Colombia Heatsink 20.0 Vasily Danilyuk
998 4 14 Brazil Tablet 1820.0 Vasily Danilyuk
999 4 12 Brazil SDD 264.0 Onida Cosely
[1000 rows x 6 columns]
# 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))
mes ventas costos
0 ene 10 7
1 feb 20 16
2 mar 30 25
3 abr 15 12
# 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))
article_name
HDD 413
Name: quantity, dtype: int64
# RESOLUCIÓN GRÁFICA
sns.barplot(y=por_cant["quantity"], x=por_cant.index)
plt.xticks(rotation=90)
plt.show()
# RESOLUCIÓN ANALÍTICA
print(my_df)
df2 = (my_df.groupby(by='article_name').sum()).sort_values('total_amount',ascending=False).head(5)
print(df2['total_amount'])
# El artículo que mas ingreso proporcionó fué Full Pc
week quantity country_name article_name total_amount seller_name
0 1 10 Peru Water Cooling 675.0 Cirilo Grandham
1 1 15 Peru Mouse 454.5 Ewell Peres
2 1 5 Bolivia Netbook 725.0 Janel O'Curran
3 1 9 Brazil Tablet 1170.0 Janel O'Curran
4 1 6 Mexico Case 227.4 Daisie Slograve
.. ... ... ... ... ... ...
995 4 1 Brazil Modem 67.5 Kati Innot
996 4 15 Brazil Heatsink 150.0 Daisie Slograve
997 4 2 Colombia Heatsink 20.0 Vasily Danilyuk
998 4 14 Brazil Tablet 1820.0 Vasily Danilyuk
999 4 12 Brazil SDD 264.0 Onida Cosely
[1000 rows x 6 columns]
article_name
Full Pc 538335.93
Notebook 251000.00
Smartphone 152250.00
Chair 69477.48
Tablet 48620.00
Name: total_amount, dtype: float64
# 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()
# solo lo quise mostrar con los dos tipos de gráficos,
plt.bar(df2.index,df2['total_amount'])
plt.show()
# RESOLUCIÓN ANALÍTICA
df4 = (my_df.groupby(by='seller_name').sum()).sort_values('total_amount',ascending=False).head(5)
print(df4[['quantity', 'total_amount']])
# el mejor vendedor del mes es Janel O' Curran
quantity total_amount
seller_name
Janel O'Curran 703 192832.47
Brockie Patience 441 142709.88
Oliviero Charkham 555 141329.76
Vasily Danilyuk 521 129157.55
Daisie Slograve 554 120520.11
# 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'])
# si hay grandes variaciones, pienso que debe lanzarse una campaña de promociones en la última semana
week
1 507458.81
2 415364.44
3 329140.03
4 223844.56
Name: total_amount, dtype: float64
# RESOLUCIÓN GRÁFICA
plt.bar(df5.index,df5['total_amount'])
plt.show()
# RESOLUCIÓN
df_pais_min_semana = my_df.groupby(['week', 'country_name']).sum().sort_values('total_amount')
print(df_pais_min_semana.isnull().sum())
weeks = my_df['week'].unique()
for week in weeks:
condition = df_pais_min_semana['total_amount'] ==\
df_pais_min_semana.loc[week,]['total_amount'].min()
# se accede al indice de la clave que refiere al pais (1).porque 0 corresponde a la semana
pais = df_pais_min_semana[condition].index[0][1]
monto = round(df_pais_min_semana[condition]['total_amount'].values[0],3)
print("El país que menos compró la semana {} fué ".format(week))
print(f"{pais} con un total de $ {monto} \n")
# tener un conocimiento de qué país compra menos cada semana,
# puede ayudar a tomar decisiones en
# la creación de campañas en esos lugares para incentivar la venta.
quantity 0
total_amount 0
dtype: int64
El país que menos compró la semana 1 fué
Ecuador con un total de $ 67.5
El país que menos compró la semana 2 fué
Puerto Rico con un total de $ 8.85
El país que menos compró la semana 3 fué
Paraguay con un total de $ 1026.62
El país que menos compró la semana 4 fué
Uruguay con un total de $ 164.98
# RESOLUCIÓN
# cuál fué el vendedor que más vendió en cada país?
#creo que es importante saber que vendedor vende más en cada país, para dar incentivos por ventas generadas.
df_ven_max_pais = my_df.groupby(['country_name'])
print(df_ven_max_pais)
for name, data in df_ven_max_pais:
print(name)
print(data.groupby('seller_name').sum().sort_values("total_amount", ascending=False)['total_amount'].head(1))
print("\n")
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7ffb82852d90>
Argentina
seller_name
Janel O'Curran 34971.47
Name: total_amount, dtype: float64
Bolivia
seller_name
Kati Innot 10750.37
Name: total_amount, dtype: float64
Brazil
seller_name
Daisie Slograve 65283.28
Name: total_amount, dtype: float64
Chile
seller_name
Onida Cosely 6769.8
Name: total_amount, dtype: float64
Colombia
seller_name
Brockie Patience 33654.38
Name: total_amount, dtype: float64
Costa Rica
seller_name
Oliviero Charkham 24021.21
Name: total_amount, dtype: float64
Ecuador
seller_name
Oliviero Charkham 6647.5
Name: total_amount, dtype: float64
El Salvador
seller_name
Janel O'Curran 32967.15
Name: total_amount, dtype: float64
Guatemala
seller_name
Vasily Danilyuk 27661.53
Name: total_amount, dtype: float64
Honduras
seller_name
Oliviero Charkham 19403.32
Name: total_amount, dtype: float64
Mexico
seller_name
Jase Doy 29096.3
Name: total_amount, dtype: float64
Paraguay
seller_name
Aveline Swanwick 3220.0
Name: total_amount, dtype: float64
Peru
seller_name
Brockie Patience 46089.88
Name: total_amount, dtype: float64
Puerto Rico
seller_name
Ewell Peres 1256.58
Name: total_amount, dtype: float64
Uruguay
seller_name
Janel O'Curran 9488.58
Name: total_amount, dtype: float64
Venezuela
seller_name
Oliviero Charkham 33961.53
Name: total_amount, dtype: float64
df_min_article = my_df.groupby("article_name").sum().sort_values("total_amount", ascending=True)["total_amount"].head(5)
print(df_min_article)
sns.barplot(x=df_min_article.index,y= df_min_article)
plt.title('5 artículos que generaron menos ingresos')
plt.show()
# es importante conocer cuales son los artículos que menos ingresos generan, para evaluar la posibilidad de hacer campañas
# de promoción o bien ver si realmente conviene tener mucho stock de los mismos.
article_name
Sata Cable 564.96
Usb Cable 805.35
Fan Cooler 871.25
Heatsink 2800.00
Pci Express Port 2944.92
Name: total_amount, dtype: float64
8- ¿Cuál fue el total de ventas por país?, ¿Cuál fue la venta de cada país por semana?
## Total de ventas por país
df_total_sales = my_df.groupby(['country_name']).sum()
#define Seaborn color palette to use
colors = sns.color_palette('Paired')
#bar chart
sns.barplot(x=df_total_sales.index,y=df_total_sales['total_amount'])
plt.xticks(rotation=60, ha="right")
plt.title('Total de ventas por país')
plt.show()
sns.set_theme()
## Total de ventas por pais y por semana
df_sales = my_df.groupby(['country_name', 'week']).sum()
countries = my_df['country_name'].unique()
df_countries = pd.DataFrame(columns=['country', 'week', 'total_amount'])
for country in countries:
df_country = df_sales.loc[country]
for week in df_country.index:
df_countries = df_countries.append({'country': country,
'week': week,
'total_amount': df_country.loc[week]['total_amount']},
ignore_index=True)
print(df_countries.head(len(df_countries)))
countries_data = df_countries.pivot("country", "week", "total_amount")
# heatmap con datos numericos en cada celda,
#las celdas vacias indican que no existen ventas en esa instancia.
f, ax = plt.subplots(figsize=(9, 6))
sns.heatmap(countries_data, annot=True, fmt=".2f", linewidths=.7, ax=ax)
plt.title('Total de ventas por país y por semana \n')
plt.show()
country week total_amount
0 Peru 1 48807.97
1 Peru 2 56591.80
2 Peru 3 51855.31
3 Peru 4 4166.04
4 Bolivia 1 13033.19
.. ... ... ...
57 Puerto Rico 2 8.85
58 Ecuador 1 67.50
59 Ecuador 2 12085.96
60 Ecuador 3 4801.84
61 Ecuador 4 520.00
[62 rows x 3 columns]
1- Creo que este trabajo ha sido interesante para comprender como se distribuyen los datos, ver las diversas perspectivas. Los gráficos realmente detallan de manera sencilla como se comportan estos datos.
2- Además los diferentes tipos de análisis planteados permiten comprender las métricas dada las distintas dimensiones que se evalúan.
3- Una cuestión interesante fué que el país que más vendió durante todo el mes ha sido Brasil, lo que genera inconsistencia a la hora de concluir respecto al promedio de ventas por país. Además se observa la disparidad de cantidades y montos facturados en el conjunto de paises,