Veremos las siguientes fases del análisis de datos: ingesta de datos, diagnóstico de la calidad de los datos, y agrupación de variables.
Si deseas obtener el conjunto de datos que vamos a utilizar, puedes descargarlo aquí: vehiculos_original.csv (tamaño 4.3 MB).
Los datos fueron tomados de https://www.fueleconomy.gov/feg/ws/index.shtml
En esta fase leemos el conjunto de datos original y le aplicamos el tratamiento necesario para facilitar su uso, por ejemplo, renombrar las columnas.
Importar pandas y leer el archivo csv
import pandas as pd vehiculos = pd.read_csv("vehiculos_original.csv")
Renombrar las columnas
vehiculos = vehiculos.rename(columns={ "cylinders": "cilindros", "trany": "transmision", "make": "fabricante", "model": "modelo", "displ": "desplazamiento", "drive": "traccion", "VClass": "clase", "fuelType": "combustible", "comb08": "consumo", "co2TailpipeGpm": "co2" })
Guardar los cambios en un nuevo csv para no modificar el archivo original
vehiculos.to_csv("vehiculos_paso1.csv", index=False)
En esta fase revisamos que no existan datos duplicados (que no debiesen estar duplicados), datos nulos o inexistentes (que pudiesen arrojarnos errores de procesamiento), y se revisan también los valores extremos.
Importamos las librerías que requeriremos, leemos el archivo csv y mostramos cuántas filas y columnas tiene nuestro dataset.
import pandas as pd import matplotlib.pyplot as plt vehiculos = pd.read_csv("vehiculospaso1.csv") print(vehiculos.shape)
Veamos cuántas filas duplicadas hay
print (vehiculos[vehiculos.duplicated()].shape)
Eliminamos las filas duplicadas
vehiculos = vehiculos.drop_duplicates() print (vehiculos.shape)
Midamos ahora la cardinalidad de algunas columnas. Con esto queremos observar si una cantidad grande de registros tiene el mismo valor.
La siguiente función nos permite ver el porcentaje de valores comunes en cada columna.
n_registros = len(vehiculos) def valores_duplicados_col(df): for columna in df: n_por_valor = df[columna].value_counts() mas_comun = n_por_valor.iloc[0] menos_comun = n_por_valor.iloc[-1] print ("{} | {} - {} | {}".format( df[columna].name, round(mas_comun/(1.0*n_registros),3), round(menos_comun/(1.0*n_registros),3), df[columna].dtype )) print("Revisando valores duplicados") valores_duplicados_col(vehiculos)
Desde luego podemos observar ésto en forma gráfica, columna por columna. Por ejemplo, para las columnas “transmision” y “combustible”:
vehiculos.transmision.value_counts(normalize=True).plot.barh() vehiculos.cilindros.hist() vehiculos.combustible.value_counts(normalize=True).plot.barh() plt.show()
La siguiente función nos permite observar el porcentaje de valores inexistentes en cada columna
print ("Revisando valores inexistentes") n_registros = len(vehiculos) def valores_inexistentes_col(df): for columna in df: print ("{} | {} |{}".format( df[columna].name, len(df[df[columna].isnull()]) / (1.0*n_registros), df[columna].dtype )) valores_inexistentes_col(vehiculos)
Se consideran valores extremos aquellos cuya puntuación Z es mayor a 3. Se considera a Z como
Z = (x – media ) / desviación_estándar
Afortunadamente, contamos con la librería scipy que nos permite obtener la puntación Z fácilmente.
Podemos implementar una función que nos permita observar la cantidad de valores extremos que existen en cada columna.
from scipy import stats import numpy as np def extermos_col(df): for columna in df: if df[columna].dtype != np.object: n_extremos = len(df[np.abs(stats.zscore(df[columna]))>3]) print ("{} | {} | {}".format( df[columna].name, n_extremos, df[columna].dtype )) print ("Revisando valores extremos") extermos_col(vehiculos)
Gráficamente también podemos revisar los valores extremos. Por ejemplo, en las columnas consumo y co2.
vehiculos.boxplot(column='consumo') vehiculos.boxplot(column='co2') plt.show()
Se observa que hay coches que no producen co2. ¿Qué combustible usarán?
print(vehiculos[vehiculos.co2==0].combustible.unique()) #Los tipos de combustible son print(vehiculos.combustible.unique())
Bueno, para éste ejercicio sólo nos interesan los vehículos que sí generan co2, por lo que vamos a eliminar a los que no generan co2.
print (vehiculos[vehiculos.co2==0].shape) #Hay 139 que no generan co2 vehiculos_no_electricos = vehiculos[vehiculos.co2>0] print (vehiculos_no_electricos.shape)
Hay 1506 registros duplicados que se han eliminado.
Las variables desplazamiento, cilindro, transmision y traccion tienen valores nulos o inexistentes.
La variable combustible tiene una clase dominante en 65%.
Se eliminaron 139 registros correspondientes a autos electricos que no emiten co2.
Guardamos un nuevo csv con los cambios
vehiculos_no_electricos.to_csv("vehiculospaso2.csv", index=False)
Se requiere agrupar algunas variables a fin de reducir el campo de estudio a aquellas variables que nos interesan.
La siguiente función permite ver los valores únicos de cada columna.
vehiculos = pd.read_csv("vehiculospaso2.csv") def valores_unicos_col(df): for column in df: print ("{} | {} | {} ".format( df[column].name, len(df[column].unique()), df[column].dtype )) print ("=== Valores unicos por columna") valores_unicos_col(vehiculos)
Veamos qué valores componen la columna clase
print ("*** Valores de la columna clase") print (vehiculos.clase.unique())
Si se observan, podemos clasificar todos éstos valores en ciertas categorías. Vamos a crear las siguientes categorías o tipos para la columna clase: Coches pequeños, Coches medianos, Coches grandes, Camionetas, Vehículos especiales, Deportivos, Coche familiar, Furgoneta.
A continuación creamos una nueva columna llamada “clase_tipo”, donde se clasificará cada registro en su tipo correspondiente.
pequeno = ['Compact Cars', 'Subcompact Cars', 'Two Seaters', 'Minicompact Cars'] medio = ['Midsize Cars'] grande= ['Large Cars'] vehiculos.loc[vehiculos['clase'].isin(pequeno),'clase_tipo'] = 'Coches pequeños' vehiculos.loc[vehiculos['clase'].isin(medio),'clase_tipo'] = 'Coches medianos' vehiculos.loc[vehiculos['clase'].isin(grande),'clase_tipo'] = 'Coches grandes' vehiculos.loc[vehiculos['clase'].str.contains('Truck'), 'clase_tipo'] = 'Camionetas' vehiculos.loc[vehiculos['clase'].str.contains('Special Purpose'), 'clase_tipo'] = 'Vehiculos especiales' vehiculos.loc[vehiculos['clase'].str.contains('Sport Utility'), 'clase_tipo'] = 'Deportivos' vehiculos.loc[vehiculos['clase'].str.contains('Station'), 'clase_tipo'] = 'Coche familiar' vehiculos.loc[vehiculos['clase'].str.lower().str.contains('van'), 'clase_tipo'] = 'Furgoneta'
Es útil que las columnas categóricas sean consideradas como tales por Python. A continuación haremos eso a la columna “clase_tipo”.
vehiculos.clase_tipo = vehiculos.clase_tipo.astype("category")
Si deseamos verificar que todas las filas hayan recibido una categoría, podemos contar cuántos registros contiene cada una de ellas.
print ("==== Mis Categorias de clase") print(vehiculos.clase_tipo.value_counts())
Agruparemos los registros en dos tipos: dos y cuatro ruedas.
print ("Valores de la columna traccion") print(vehiculos.traccion.unique()) vehiculos["traccion_tipo"] = "dos" vehiculos.loc[vehiculos.traccion.isin([ "4-Wheel or All-Whell Drive", "All-Whell Drive", "4-Wheel Drive", "Part Time 4-Whell Drive"]), "traccion_tipo"] = "cuatro" print("") print ("=== Mis Categorias de traccion") print (vehiculos.traccion_tipo.value_counts())
Aquí necesitamos solo dos categorías (Manual y Automática) de todas las que el dataset maneja.
vehiculos["transmision_tipo"] = "Automática" vehiculos.loc[vehiculos.transmision.notnull() & vehiculos.transmision.str.startswith('M'), "transmision_tipo"] = "Manual" print("") print ("=== Mis Categorias de transmision") print (vehiculos.transmision_tipo.value_counts())
Cuarto caso: la columna “combustible”
Crearemos los tipos: Normal, Premium, Hibrido y Otros tipos de combustible
vehiculos["combustible_tipo"] = "Otros tipos de combustible" vehiculos.loc[vehiculos["combustible"] == "Regular", "combustible_tipo"] = "Normal" vehiculos.loc[vehiculos["combustible"] == "Premium", "combustible_tipo"] = "Premium" vehiculos.loc[vehiculos["combustible"].str.contains("Electricity"), "combustible_tipo"]= "Hibrido" print("") print ("=== Mis Categorias de combustible") print (vehiculos.combustible_tipo.value_counts())
Las variables continuas tienen valores numéricos por lo que se deben clasificar de acuerdo a rangos de valores.
Esto ocurre en el caso de las columnas “desplazamiento”, “consumo” y “co2”. Éstas columnas las clasificaremos en categorías como “muy bajo”, “bajo”, “moderado”, “alto”, “muy alto”, o de una forma similar.
tipos_tam_motor = ["muy pequeño", "pequeño", "mediano", "grande", "muy grande"] vehiculos["tamano_motor_tipo"] = pd.qcut(vehiculos["desplazamiento"], 5, tipos_tam_motor) print("") print ("=== Mis Categorias de desplazamiento") print (vehiculos.tamano_motor_tipo.value_counts()) tipos_consumo = ["muy bajo", "bajo", "moderado","alto", "muy alto"] vehiculos["consumo_tipo"] = pd.qcut(vehiculos["consumo"], 5, tipos_consumo) print("") print ("=== Mis Categorias de consumo") print (vehiculos.consumo_tipo.value_counts()) tipos_co2 = ["muy bajo", "bajo", "moderado","alto", "muy alto"] vehiculos["co2_tipo"] = pd.qcut(vehiculos["co2"], 5, tipos_co2) print("") print ("=== Mis Categorias de co2") print (vehiculos.co2_tipo.value_counts())
Por último ajustaremos los valores de consumo y co2 para que ambos utilicen una misma unidad de medida.
litros_por_galon = 3.78541 vehiculos["consumo_litros_milla"] = litros_por_galon / vehiculos.consumo print(vehiculos.head(5)) vehiculos.plot.scatter(x="consumo_litros_milla", y = "co2") plt.show()
Para evitar que se pierda información sobre qué variables son categóricas, en vez del formato CSV guardaremos en formato pickle. Pandas puede leer y escribir sobre pickle y al hacerlo es como si el dataframe nunca hubiese perdido ninguna propiedad.
vehiculos.to_pickle("vehiculospaso3.pkl")
Éstas son las primeras 3 de las fases del análisis de datos. En otra entrada consideraremos las siguientes.