Curso Python para Ciencia de datos
Por Jose R. Zapata - https://joserzapata.github.io/
NOTA: Realizar Primero los ejercicios y luego revisar las soluciones propuestas.
Para realizar los ejercicios prácticos de este capitulo, hacer click en el siguiente enlace que los llevara a Google Colab, donde podrán ejecutar el código y realizar los ejercicios propuestos.
Ejercicio SF Salaries
Utilizaremos el SF Salaries Dataset
Kaggle. Simplemente siga y complete las tareas que se detallan a continuación en negrita. Las tareas serán cada vez más difíciles a medida que avance.
La idea principal es aplicar pandas para organizar informacion, manipularla, buscar información en el dataset
Importe pandas como pd. e imprima la version que esta usando
# Copie el código aca
import pandas as pd
print(f"version de pandas = {pd.__version__}")
version de pandas = 2.2.2
leer el archivo Salaries.csv en un dataframe llamado salarios
salarios = pd.read_csv(
"https://joserzapata.github.io/courses/python_ciencia_datos/Salaries.csv"
)
Use el método .info()
para encontrar cuantas Columnas de atributos hay y que tipo de datos tiene cada una.
# Copie el código aca
salarios.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 148654 entries, 0 to 148653
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Id 148654 non-null int64
1 EmployeeName 148654 non-null object
2 JobTitle 148654 non-null object
3 BasePay 148045 non-null float64
4 OvertimePay 148650 non-null float64
5 OtherPay 148650 non-null float64
6 Benefits 112491 non-null float64
7 TotalPay 148654 non-null float64
8 TotalPayBenefits 148654 non-null float64
9 Year 148654 non-null int64
10 Notes 0 non-null float64
11 Agency 148654 non-null object
12 Status 0 non-null float64
dtypes: float64(8), int64(2), object(3)
memory usage: 14.7+ MB
Segun la información del dataframe su tamaño es de memory usage: 14.7+ MB
las columnas Notes y Status solamente tienen valores NaN, verifique que dice 0 non-null osea 0 valores son diferentes a NaN entonces todos los valores son NaN en esas columnas
Elimine las columnas Notes y Status Recuerde usar el método de forma permanente (inplace), y luego visualice cuantas filas y columnas tiene luego de eliminar las columnas
# Copie el código aca
salarios.drop(["Notes", "Status"], axis="columns", inplace=True)
salarios.shape
(148654, 11)
¿Cual es la nueva información del dataframe?
# Copie el código aca
salarios.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 148654 entries, 0 to 148653
Data columns (total 11 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Id 148654 non-null int64
1 EmployeeName 148654 non-null object
2 JobTitle 148654 non-null object
3 BasePay 148045 non-null float64
4 OvertimePay 148650 non-null float64
5 OtherPay 148650 non-null float64
6 Benefits 112491 non-null float64
7 TotalPay 148654 non-null float64
8 TotalPayBenefits 148654 non-null float64
9 Year 148654 non-null int64
10 Agency 148654 non-null object
dtypes: float64(6), int64(2), object(3)
memory usage: 12.5+ MB
- Se puede verificar que al borrar las columnas que no tenian solo NaN se disminuye el tamaño del dataframe a: memory usage: 12.5+ MB
Observe los primeros 5 registros del DataFrame.
# Copie el código aca
salarios.head()
Id | EmployeeName | JobTitle | BasePay | OvertimePay | OtherPay | Benefits | TotalPay | TotalPayBenefits | Year | Agency | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | NATHANIEL FORD | GENERAL MANAGER-METROPOLITAN TRANSIT AUTHORITY | 167411.18 | 0.00 | 400184.25 | NaN | 567595.43 | 567595.43 | 2011 | San Francisco |
1 | 2 | GARY JIMENEZ | CAPTAIN III (POLICE DEPARTMENT) | 155966.02 | 245131.88 | 137811.38 | NaN | 538909.28 | 538909.28 | 2011 | San Francisco |
2 | 3 | ALBERT PARDINI | CAPTAIN III (POLICE DEPARTMENT) | 212739.13 | 106088.18 | 16452.60 | NaN | 335279.91 | 335279.91 | 2011 | San Francisco |
3 | 4 | CHRISTOPHER CHONG | WIRE ROPE CABLE MAINTENANCE MECHANIC | 77916.00 | 56120.71 | 198306.90 | NaN | 332343.61 | 332343.61 | 2011 | San Francisco |
4 | 5 | PATRICK GARDNER | DEPUTY CHIEF OF DEPARTMENT,(FIRE DEPARTMENT) | 134401.60 | 9737.00 | 182234.59 | NaN | 326373.19 | 326373.19 | 2011 | San Francisco |
Observe los ultimos 5 registros del DataFrame.
# Copie el código aca
salarios.tail()
Id | EmployeeName | JobTitle | BasePay | OvertimePay | OtherPay | Benefits | TotalPay | TotalPayBenefits | Year | Agency | |
---|---|---|---|---|---|---|---|---|---|---|---|
148649 | 148650 | Roy I Tillery | Custodian | 0.0 | 0.0 | 0.00 | 0.0 | 0.00 | 0.00 | 2014 | San Francisco |
148650 | 148651 | Not provided | Not provided | NaN | NaN | NaN | NaN | 0.00 | 0.00 | 2014 | San Francisco |
148651 | 148652 | Not provided | Not provided | NaN | NaN | NaN | NaN | 0.00 | 0.00 | 2014 | San Francisco |
148652 | 148653 | Not provided | Not provided | NaN | NaN | NaN | NaN | 0.00 | 0.00 | 2014 | San Francisco |
148653 | 148654 | Joe Lopez | Counselor, Log Cabin Ranch | 0.0 | 0.0 | -618.13 | 0.0 | -618.13 | -618.13 | 2014 | San Francisco |
¿Cuantas filas y columnas tiene el dataframe?
# Copie el código aca
salarios.shape
(148654, 11)
Observe los 10 registros que estan desde el indice 3000
# Copie el código aca
salarios.iloc[3000:3010]
Id | EmployeeName | JobTitle | BasePay | OvertimePay | OtherPay | Benefits | TotalPay | TotalPayBenefits | Year | Agency | |
---|---|---|---|---|---|---|---|---|---|---|---|
3000 | 3001 | WILLIAM BOYLE | FIREFIGHTER | 105934.65 | 18530.98 | 16389.60 | NaN | 140855.23 | 140855.23 | 2011 | San Francisco |
3001 | 3002 | ALAN LIN | ELECTRONIC MAINTENANCE TECHNICIAN | 98965.80 | 41504.40 | 380.00 | NaN | 140850.20 | 140850.20 | 2011 | San Francisco |
3002 | 3003 | WILLIAM CONLEY | SERGEANT III (POLICE DEPARTMENT) | 130457.78 | 3131.42 | 7256.39 | NaN | 140845.59 | 140845.59 | 2011 | San Francisco |
3003 | 3004 | ROBERT MCDONALD | LIEUTENANT, FIRE DEPARTMENT | 123104.99 | 1833.56 | 15897.68 | NaN | 140836.23 | 140836.23 | 2011 | San Francisco |
3004 | 3005 | MICHAEL COSTELLO | POLICE OFFICER III | 112401.08 | 7957.74 | 20466.93 | NaN | 140825.75 | 140825.75 | 2011 | San Francisco |
3005 | 3006 | EDNA HO | MANAGER V | 140821.37 | 0.00 | 0.00 | NaN | 140821.37 | 140821.37 | 2011 | San Francisco |
3006 | 3007 | RODRIGO CONEL | REGISTERED NURSE | 126120.00 | 3981.78 | 10714.21 | NaN | 140815.99 | 140815.99 | 2011 | San Francisco |
3007 | 3008 | VICTOR HURTADO | FIREFIGHTER | 105934.67 | 13112.76 | 21763.50 | NaN | 140810.93 | 140810.93 | 2011 | San Francisco |
3008 | 3009 | DONALD LANDRY | TRANSIT POWER LINE WORKER | 92861.50 | 39789.32 | 8135.86 | NaN | 140786.68 | 140786.68 | 2011 | San Francisco |
3009 | 3010 | FRANK LEE | INSPECTOR III, (POLICE DEPARTMENT) | 130481.56 | 725.40 | 9579.58 | NaN | 140786.54 | 140786.54 | 2011 | San Francisco |
La columna Benefits tiene valores NaN
, ¿Cuantos NaN tiene la columna Benefits? (TIP: Seleccione la Columna y vefique cuantos Na tiene .isna()
luego sumelos .sum()
)
# Copie el código aca
salarios["Benefits"].isna().sum()
36163
Al parecer los valores faltantes en la columna Benefits es por que no se le pagan beneficios entonces:
Reemplace los valores NaN de la Columna Benefits por el valor de cero (Recuerde usar el método de forma inplace) y mostrar los primeros 10 elementos de la lista
# Copie el código aca
salarios["Benefits"] = salarios["Benefits"].fillna(value=0)
salarios.head(10)
Id | EmployeeName | JobTitle | BasePay | OvertimePay | OtherPay | Benefits | TotalPay | TotalPayBenefits | Year | Agency | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | NATHANIEL FORD | GENERAL MANAGER-METROPOLITAN TRANSIT AUTHORITY | 167411.18 | 0.00 | 400184.25 | 0.0 | 567595.43 | 567595.43 | 2011 | San Francisco |
1 | 2 | GARY JIMENEZ | CAPTAIN III (POLICE DEPARTMENT) | 155966.02 | 245131.88 | 137811.38 | 0.0 | 538909.28 | 538909.28 | 2011 | San Francisco |
2 | 3 | ALBERT PARDINI | CAPTAIN III (POLICE DEPARTMENT) | 212739.13 | 106088.18 | 16452.60 | 0.0 | 335279.91 | 335279.91 | 2011 | San Francisco |
3 | 4 | CHRISTOPHER CHONG | WIRE ROPE CABLE MAINTENANCE MECHANIC | 77916.00 | 56120.71 | 198306.90 | 0.0 | 332343.61 | 332343.61 | 2011 | San Francisco |
4 | 5 | PATRICK GARDNER | DEPUTY CHIEF OF DEPARTMENT,(FIRE DEPARTMENT) | 134401.60 | 9737.00 | 182234.59 | 0.0 | 326373.19 | 326373.19 | 2011 | San Francisco |
5 | 6 | DAVID SULLIVAN | ASSISTANT DEPUTY CHIEF II | 118602.00 | 8601.00 | 189082.74 | 0.0 | 316285.74 | 316285.74 | 2011 | San Francisco |
6 | 7 | ALSON LEE | BATTALION CHIEF, (FIRE DEPARTMENT) | 92492.01 | 89062.90 | 134426.14 | 0.0 | 315981.05 | 315981.05 | 2011 | San Francisco |
7 | 8 | DAVID KUSHNER | DEPUTY DIRECTOR OF INVESTMENTS | 256576.96 | 0.00 | 51322.50 | 0.0 | 307899.46 | 307899.46 | 2011 | San Francisco |
8 | 9 | MICHAEL MORRIS | BATTALION CHIEF, (FIRE DEPARTMENT) | 176932.64 | 86362.68 | 40132.23 | 0.0 | 303427.55 | 303427.55 | 2011 | San Francisco |
9 | 10 | JOANNE HAYES-WHITE | CHIEF OF DEPARTMENT, (FIRE DEPARTMENT) | 285262.00 | 0.00 | 17115.73 | 0.0 | 302377.73 | 302377.73 | 2011 | San Francisco |
¿Cual es la nueva información del dataframe?
# Copie el código aca
salarios.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 148654 entries, 0 to 148653
Data columns (total 11 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Id 148654 non-null int64
1 EmployeeName 148654 non-null object
2 JobTitle 148654 non-null object
3 BasePay 148045 non-null float64
4 OvertimePay 148650 non-null float64
5 OtherPay 148650 non-null float64
6 Benefits 148654 non-null float64
7 TotalPay 148654 non-null float64
8 TotalPayBenefits 148654 non-null float64
9 Year 148654 non-null int64
10 Agency 148654 non-null object
dtypes: float64(6), int64(2), object(3)
memory usage: 12.5+ MB
Cuantos valores unicos hay en las columnas categoricas?
# Copie el código aca
salarios[["EmployeeName", "JobTitle", "Agency"]].nunique()
EmployeeName 110811
JobTitle 2159
Agency 1
dtype: int64
- la columna Agency solamente tiene un valor unico, entonces no aporta informacion, por lo tanto se puede eliminar
Elimine la columna Agency del dataframe
# Copie el código aca
salarios.drop(columns=["Agency"], inplace=True)
Cual es el promedio de BasePay ?
# Copie el código aca
salarios["BasePay"].mean()
66325.4488404877
Cual es la cantidad mas alta de OvertimePay en el dataset ?
# Copie el código aca
salarios["OvertimePay"].max()
245131.88
Realice una descripcion estadistica de las columnas numericas?
# Copie el código aca
salarios.describe()
Id | BasePay | OvertimePay | OtherPay | Benefits | TotalPay | TotalPayBenefits | Year | |
---|---|---|---|---|---|---|---|---|
count | 148654.000000 | 148045.000000 | 148650.000000 | 148650.000000 | 148654.000000 | 148654.000000 | 148654.000000 | 148654.000000 |
mean | 74327.500000 | 66325.448840 | 5066.059886 | 3648.767297 | 18924.232839 | 74768.321972 | 93692.554811 | 2012.522643 |
std | 42912.857795 | 42764.635495 | 11454.380559 | 8056.601866 | 17165.279495 | 50517.005274 | 62793.533483 | 1.117538 |
min | 1.000000 | -166.010000 | -0.010000 | -7058.590000 | -33.890000 | -618.130000 | -618.130000 | 2011.000000 |
25% | 37164.250000 | 33588.200000 | 0.000000 | 0.000000 | 0.000000 | 36168.995000 | 44065.650000 | 2012.000000 |
50% | 74327.500000 | 65007.450000 | 0.000000 | 811.270000 | 23214.020000 | 71426.610000 | 92404.090000 | 2013.000000 |
75% | 111490.750000 | 94691.050000 | 4658.175000 | 4236.065000 | 33468.980000 | 105839.135000 | 132876.450000 | 2014.000000 |
max | 148654.000000 | 319275.010000 | 245131.880000 | 400184.250000 | 96570.660000 | 567595.430000 | 567595.430000 | 2014.000000 |
En este dataset las variables Id
y Year
representan variables categorica, convertir estas columnas a su formato correcto
# Copie el código aca
# convertir las columnas Id y Year en categóricas ordinales
salarios["Id"] = salarios["Id"].astype("category")
salarios["Year"] = salarios["Year"].astype("category")
la variable Year
es categorica ordinal, primero detecte cuales son los valores unicos que tiene la columna
# Copie el código aca
salarios["Year"].unique()
[2011, 2012, 2013, 2014]
Categories (4, int64): [2011, 2012, 2013, 2014]
Apartir de la información anterior convierta la columna Year
en variable categorica ordinal y visualice la columna
# Copie el código aca
salarios["Year"] = salarios["Year"].cat.set_categories(
[2011, 2012, 2013, 2014], ordered=True
)
salarios["Year"]
0 2011
1 2011
2 2011
3 2011
4 2011
...
148649 2014
148650 2014
148651 2014
148652 2014
148653 2014
Name: Year, Length: 148654, dtype: category
Categories (4, int64): [2011 < 2012 < 2013 < 2014]
Realice nuevamente una descripcion estadistica de las columnas numericas?
# Copie el código aca
salarios.describe()
BasePay | OvertimePay | OtherPay | Benefits | TotalPay | TotalPayBenefits | |
---|---|---|---|---|---|---|
count | 148045.000000 | 148650.000000 | 148650.000000 | 148654.000000 | 148654.000000 | 148654.000000 |
mean | 66325.448840 | 5066.059886 | 3648.767297 | 18924.232839 | 74768.321972 | 93692.554811 |
std | 42764.635495 | 11454.380559 | 8056.601866 | 17165.279495 | 50517.005274 | 62793.533483 |
min | -166.010000 | -0.010000 | -7058.590000 | -33.890000 | -618.130000 | -618.130000 |
25% | 33588.200000 | 0.000000 | 0.000000 | 0.000000 | 36168.995000 | 44065.650000 |
50% | 65007.450000 | 0.000000 | 811.270000 | 23214.020000 | 71426.610000 | 92404.090000 |
75% | 94691.050000 | 4658.175000 | 4236.065000 | 33468.980000 | 105839.135000 | 132876.450000 |
max | 319275.010000 | 245131.880000 | 400184.250000 | 96570.660000 | 567595.430000 | 567595.430000 |
¿Cuál es el título de trabajo de JOSEPH DRISCOLL? Nota: Utilice mayúsculas, de lo contrario, puede obtener una respuesta que no coincida.
# Copie el código aca
salarios[salarios["EmployeeName"] == "JOSEPH DRISCOLL"]["JobTitle"]
24 CAPTAIN, FIRE SUPPRESSION
Name: JobTitle, dtype: object
¿Cuanto gana JOSEPH DRISCOLL (incluyendo beneficios)?
# Copie el código aca
salarios[salarios["EmployeeName"] == "JOSEPH DRISCOLL"]["TotalPayBenefits"]
24 270324.91
Name: TotalPayBenefits, dtype: float64
¿Cual es el nombre de la persona con mejor pago (incluyendo beneficios)?
# Copie el código aca
salarios[salarios["TotalPayBenefits"] == salarios["TotalPayBenefits"].max()]
Id | EmployeeName | JobTitle | BasePay | OvertimePay | OtherPay | Benefits | TotalPay | TotalPayBenefits | Year | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | NATHANIEL FORD | GENERAL MANAGER-METROPOLITAN TRANSIT AUTHORITY | 167411.18 | 0.0 | 400184.25 | 0.0 | 567595.43 | 567595.43 | 2011 |
¿Cual es el nombre de la persona peor paga (incluyendo beneficios)? ¿Notan algo extraño sobre cuánto le pagan?
# Copie el código aca
salarios[salarios["TotalPayBenefits"] == salarios["TotalPayBenefits"].min()]
Id | EmployeeName | JobTitle | BasePay | OvertimePay | OtherPay | Benefits | TotalPay | TotalPayBenefits | Year | |
---|---|---|---|---|---|---|---|---|---|---|
148653 | 148654 | Joe Lopez | Counselor, Log Cabin Ranch | 0.0 | 0.0 | -618.13 | 0.0 | -618.13 | -618.13 | 2014 |
Cuantos registros tienen un valor de TotalPayBeneficts menor que cero
# Copie el código aca
# el numero de filas donde el valor de TotalPayBenefits sea menor a 0
salarios[salarios["TotalPayBenefits"] < 0].shape[0]
4
¿Cual es el promedio (mean) BasePay de todos los empleados por año? los años van entre (2011-2014)? (Tip: Usar groupby)
# Copie el código aca
salarios.groupby("Year", observed=True)["BasePay"].mean()
Year
2011 63595.956517
2012 65436.406857
2013 69630.030216
2014 66564.421924
Name: BasePay, dtype: float64
¿Cuantos titulos de trabajos (JobTitle) unicos existen?
# Copie el código aca
salarios["JobTitle"].nunique()
2159
¿Cuales son los 5 trabajos mas comunes?
# Copie el código aca
salarios["JobTitle"].value_counts().head(5)
JobTitle
Transit Operator 7036
Special Nurse 4389
Registered Nurse 3736
Public Svc Aide-Public Works 2518
Police Officer 3 2421
Name: count, dtype: int64
¿Cuántos puestos de trabajo Unicos existieron en 2013? (Ejemplo, los títulos de trabajo con solo una ocurrencia en 2013?)
# Copie el código aca
# Se recomienda usar la variable YEAR_TO_COMPARE para hacer la comparación
# para mejorar la legibilidad y el mantenimiento del código
YEAR_TO_COMPARE = 2013
sum(salarios[salarios["Year"] == YEAR_TO_COMPARE]["JobTitle"].value_counts() == 1)
202
¿Cuántas personas tienen la palabra Chief en el título de su trabajo? Apoyese en los métodos para manipular strings y en el método .apply()
# Copie el código aca
Una forma simple seria:
def chief_string(title):
if "chief" in title.lower():
return True
else:
return False
sum(salarios["JobTitle"].apply(chief_string))
Este código es valido, se recomienda hacer el código mas simple y agregar typing y documentación, por ejemplo asi:
def chief_string(title: str) -> bool:
"""
Responde con True si la palabra "chief" esta en el titulo del trabajo,
en caso contrario responde False
Parameters:
title : str : el titulo del trabajo
Returns:
bool : True si la palabra "chief" esta en el titulo del trabajo,
False en caso contrario
"""
return "chief" in title.lower()
sum(salarios["JobTitle"].apply(chief_string))
627
Bono: ¿Existe una correlación entre la longitud del nombre del Título del trabajo y el Salario?
crea una nueva columna donde se guarde la longitud del titulo del trabajo. tip: usar el método .apply()
# Copie el código aca
salarios["title_len"] = salarios["JobTitle"].apply(len)
salarios["title_len"]
0 46
1 31
2 31
3 36
4 44
..
148649 9
148650 12
148651 12
148652 12
148653 26
Name: title_len, Length: 148654, dtype: int64
Utilice el método .corr()
de Pandas para calcular la correlacion
# Copie el código aca
salarios[["title_len", "TotalPayBenefits"]].corr()
title_len | TotalPayBenefits | |
---|---|---|
title_len | 1.000000 | -0.036878 |
TotalPayBenefits | -0.036878 | 1.000000 |
Phd. Jose R. Zapata