EDA en público (Parte 1): limpieza y exploración de datos de ventas con Pandas

! Bienvenido al comienzo de un importante viaje de datos al que llamo "EDA en público". Para quienes me conocen, creo que la mejor manera de aprender algo es abordar un problema del mundo real y compartir todo el complicado proceso, incluidos los errores, las victorias y todo lo demás. Si ha estado buscando mejorar sus Pandas y sus habilidades de análisis de datos, esta es la serie para usted.

Actuaremos como analistas de datos para una empresa de comercio electrónico mediana y ficticia a la que llamaré NovaShop. Nos entregaron un CSV de ventas sin editar y desordenado y nos hicieron una pregunta sencilla: "¿Cómo nos va?".

El objetivo de la Parte 1 es fundamental: limpiaremos este desafiante conjunto de datos de comercio electrónico, exploraremos su estructura básica y dominaremos las habilidades básicas de EDA en Pandas que todo científico de datos utiliza a diario. Esta serie está estructurada para llevarlo desde un principiante (Parte 1) hasta un analista de datos avanzado (Parte 3), así que siéntase libre de participar dondequiera que se encuentre.

Antes de pasar al código, definamos nuestra motivación principal. Para NovaShop, necesitamos responder algunas preguntas simples pero poderosas: ¿Qué productos generan mayores ingresos? ¿Qué países generan más ventas? Averigüemos.

Descripción general del conjunto de datos: descomprimiendo los datos de ventas

Para comenzar nuestro análisis de NovaShop, utilizaremos el conjunto de datos minoristas en línea de UCI. Se trata de un conjunto de datos excelente, muy realista y no optimizado que captura todas las transacciones de una empresa minorista en línea sin tiendas con sede en el Reino Unido entre finales de 2010 y finales de 2011.

Este conjunto de datos tiene una licencia Creative Commons Attribution 4.0 International (CC BY 4.0).

Esto permite compartir y adaptar los conjuntos de datos para cualquier propósito, siempre que se otorgue el crédito apropiado.

El conjunto de datos contiene más de medio millón de filas y está lleno de los tipos de anomalías que se encuentran en el mundo real: valores faltantes, números negativos y formato de texto inconsistente. ¡Esto es exactamente lo que queremos!

Estas son las ocho columnas clave con las que trabajaremos y lo que nos dicen desde una perspectiva empresarial:

NoFactura: Este es el número de Factura. Un número único de 6 dígitos asignado a cada transacción. Si el código comienza con 'C', indica una cancelación (una devolución). StockCode/Código de producto: un código único de 5 dígitos asignado a cada producto distinto. Descripción: El nombre del artículo. Esto a menudo necesita limpieza (espacios adicionales, casos inconsistentes). Cantidad: Número de artículos comprados. ¿Cuántas unidades estuvieron involucradas por transacción? Puede ser negativo para las devoluciones. InvoiceDate: La fecha y hora de la transacción. Esto es esencial para el análisis posterior de series de tiempo. PrecioUnitario: Precio del producto por unidad en libras esterlinas (GBP). El precio de un artículo. A veces puede ser 0 o negativo debido a errores/artículos gratuitos. CustomerID: Un número único de 5 dígitos asignado a cada cliente registrado. Lo más importante es que esto suele faltar, lo que significa que tenemos muchas transacciones de los huéspedes. País: Nombre del país donde reside el cliente. Esto sería fantástico para segmentar las ventas internacionales.

Echemos un vistazo rápido a las primeras filas para ver a qué nos enfrentamos. Esta es la salida de df.head():

Importemos el conjunto de datos a Pandas y veamos con cuántas filas estamos tratando.

importar pandas como pd importar numpy como np df = pd.read_csv('Online Retail.csv') df.shape

Producción:

(541909, 8)

Son bastantes filas. Quizás tenga que cortar un poco las filas.

Carga y división de datos: lidiar con el volumen

Para la Parte 1, tomaremos una muestra aleatoria del 10% del conjunto de datos completo. Esto nos da un tamaño mucho más manejable (alrededor de 54.000 filas) manteniendo las características de los datos originales. Esta es una técnica común y práctica cuando se trata de entornos de Big Data o cuando se centra en la creación de prototipos.

# Carga y división de datos FILE_PATH = 'Online Retail.csv' SAMPLE_FRACTION = 0.1 # Tomaremos una muestra del 10% de los datos full_df = pd.read_csv(FILE_PATH, encoding='unicode_escape') # Tomaremos una muestra aleatoria del 10% para un procesamiento más rápido en la Parte 1 df = full_df.sample(frac=SAMPLE_FRACTION, random_state=42).reset_index(drop=Verdadero)

Ahora, revisemos la forma nuevamente usando df.shape

Producción:

(54191, 8)

¡Perfecto! Ahora podemos comenzar.

Inspección inicial de datos: ensuciarnos las manos

Mi primer paso será descubrir qué estamos limpiando. necesito responder estas preguntas

¿Qué contiene el conjunto de datos? ¿Cuáles son los tipos de datos y los recuentos no nulos? ¿Cómo son los números?

Comprobación visual: df.head() y df.tail()

Mirando la primera y la última fila, puedo confirmar algunas cosas:

Cancelaciones y devoluciones: Noté algunos valores de Número de factura que comienzan con 'C' y la Cantidad correspondiente es negativa. Esto confirma que las devoluciones están incluidas y, para el análisis de ingresos, tendré que separarlas o excluirlas. Falta: pude ver visualmente los valores de NaN en la columna CustomerID, lo que confirma la masa de transacciones de invitados. Texto inconsistente: revisé la columna Descripción en busca de espacios en blanco. Basándome en la pequeña muestra que obtuve, realmente no podía decirlo. Pero no está de más abordarlos cuando entro en la limpieza de datos. También podría mantener constante la capitalización. Siempre es una buena práctica eliminar todos los espacios en blanco iniciales y finales de todas las columnas de cadena para evitar errores sutiles al agrupar. También noté algunas inconsistencias en las mayúsculas en la columna País. Vi un país llamado EIRE. Probablemente signifique que Irlanda debería cambiar eso.

¿Cuáles son los tipos de datos y los recuentos no nulos? (.info())

El siguiente paso esencial es comprobar la estructura utilizando el método .info(). Esto me dice el tipo de datos de cada columna y, lo más importante, cuántos valores no nulos (no faltantes) tenemos.

Hallazgos clave

Valores faltantes: después de tomar nuestra muestra del 10%, pude ver una brecha enorme en CustomerID. Faltan aproximadamente el 25% de las identificaciones de clientes. También noté que faltaban unos cientos de valores en Descripción que tendré que abordar. Tipos de datos: la columna InvoiceDate todavía aparece como un tipo de objeto (cadena). Tengo que convertir esto en un objeto de fecha y hora de Pandas adecuado. CustomerID también es actualmente un flotante, probablemente porque contiene esos valores de NaN. Este es un pequeño detalle que tendré que recordar si alguna vez quiero usarlo como una identificación entera verdadera.

¿Cómo son los números? (.describir())

A continuación, utilicé .describe() para obtener un resumen estadístico rápido de todas las columnas numéricas (Cantidad y Precio unitario).

Cantidad (Cantidad): La cantidad mínima es -2472. Esto confirma que existen retornos, pero la escala de esos puntos mínimos sugiere una transacción atípica extrema. Para un análisis de ventas básico, es posible que necesite filtrar estos números negativos y, potencialmente, los valores atípicos positivos y negativos extremos. Precio Unitario (UnitPrice): El precio mínimo es 0. Esto significa que algunos productos se regalaron o son entradas comodín. Dado que un producto debe tener un precio positivo en una venta normal, filtrar las filas donde UnitPrice sea cero o menos siempre es una buena práctica para calcular los ingresos con precisión.

En base a estas rápidas inspecciones realizadas. Estos datos están lejos de ser limpios. Tenemos una gran cantidad de faltas, tipos de datos incorrectos, valores negativos/cero altamente problemáticos en nuestras columnas numéricas principales e inconsistencias de texto que abordar.

Manejo de valores faltantes

Ahora que sabemos dónde faltan nuestros datos, podemos comenzar a discutir estrategias para manejar valores nulos. Lo que más me preocupa es la Descripción y el ID del cliente.

Descripciones faltantes

La Descripción nos dice qué producto se vendió, por lo que perderlo deja sin sentido la transacción. En nuestra muestra, a menos del 1% de las filas les faltan descripciones. Eliminar esas filas por completo tiene sentido, ya que son inútiles para el análisis a nivel de producto.

# Eliminar filas donde falta la descripción df.dropna(subset=['Description'], inplace=True) # comprobando recuentos nulos df['Description'].isnull().sum()

Producción:

np.int64(0)

¡Muy bien, perfecto! Todos los valores nulos desaparecieron.

ID de clientes faltantes

Los CustomerID que faltan (alrededor del 25% de nuestra muestra) son un problema mucho mayor. Si los descarto todos, perderé casi una cuarta parte de nuestros datos de ventas, lo que le dará a NovaShop una visión peligrosamente sesgada de sus ingresos totales.

Para un análisis simple de ingresos y productos (el objetivo de la Parte 1), realmente no necesito el CustomerID. Podemos continuar con el análisis en todas las filas incluso sin una identificación. Solo necesitamos la identificación si planeamos realizar una segmentación de clientes (como un análisis RFM), ¡lo cual guardaré para la Parte 2!

Conversión de tipos de datos y eliminación de duplicados

Ahora que hemos manejado las descripciones que faltan, los siguientes dos problemas inmediatos tienen que ver con filas completamente duplicadas y arreglar nuestra columna esencial InvoiceDate.

Fijar el tipo de fecha de factura

¿Recuerda cómo .info() mostraba InvoiceDate como una cadena (objeto)? Necesitamos solucionar este problema de inmediato para que Pandas sepa cómo ordenar y agregar nuestros datos cronológicamente.

# Convertir InvoiceDate de objeto (cadena) a fecha y hora df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate'])

Eliminar duplicados

Si dos filas son idénticas en todas las columnas, están duplicadas e inflarán artificialmente nuestras cifras de ventas. Revisémoslos y eliminémoslos.

# Eliminar duplicados num_duplicates = df.duplicated().sum() print(f”Encontramos {num_duplicates} filas completamente duplicadas”).

Producción:

Se encontraron 62 filas completamente duplicadas.

vamos a dejarlos

# Eliminar duplicados, manteniendo la primera instancia df.drop_duplicates(inplace=True) print(f”Tamaño del marco de datos después de eliminar duplicados: {len(df)} filas.”)

Producción:

Tamaño del marco de datos después de eliminar duplicados: 53980 filas.

Filtrar devoluciones y errores

Nuestro método .describe() nos mostró algunas señales de alerta graves: cantidades negativas (devoluciones) y precios unitarios cero/negativos (errores/artículos gratuitos). Para la Parte 1, queremos calcular los ingresos netos de las ventas, por lo que debemos filtrar este ruido.

Manejo de cantidades y precios negativos

Mantendremos sólo las transacciones donde:

La cantidad es estrictamente mayor que cero (filtrando todas las devoluciones y cancelaciones). UnitPrice es estrictamente mayor que cero (filtrando errores y artículos gratuitos). # Filtro: Mantener solo las transacciones donde la Cantidad es positiva (es decir, ventas, no devoluciones) df = df[df['Cantidad'] > 0] # Filtro: Mantener solo las transacciones donde el PrecioUnitario es positivo (es decir, no gratuito o un error) df = df[df['Cantidad'] > 0] print(f”Tamaño del marco de datos después del filtrado: {len(df)} filas.”)

Producción

Tamaño del marco de datos después del filtrado: 52933 filas.

Limpieza de columnas de texto

Finalmente, abordemos los problemas de estandarización de texto que detectamos visualmente, como el código de país EIRE y cualquier posible espacio en blanco oculto en la Descripción.

Quitar espacios en blanco: utilizamos .str.strip() para eliminar los espacios iniciales y finales tanto de Descripción como de País. Estandarizar país: asignamos manualmente inconsistencias como 'EIRE' a 'Irlanda'. # Limpiar columnas de texto # Limpiar columnas de descripción y país df['Description'] = df['Description'].str.strip() df['Country'] = df['Country'].str.strip() # Manejar inconsistencias específicas en nombres de países # EIRE es una inconsistencia común en este conjunto de datos para Irlanda df['Country'].replace('EIRE', 'Ireland', inplace=True) print(“Columnas de texto limpias y estandarizadas”).

Ingeniería de funciones y primera visión

Los datos ahora están limpios. Ahora finalmente podemos empezar a hacer preguntas divertidas. La métrica más importante para cualquier conjunto de datos de ventas son los ingresos. Dado que nuestros datos originales solo tienen Cantidad y Precio unitario, debemos diseñar la columna Ingresos nosotros mismos.

Ingeniería de funciones: creación de la columna de ingresos

Los ingresos de una transacción son simplemente la cantidad de artículos vendidos multiplicada por el precio de cada artículo.

df['Ingresos'] = df['Cantidad'] * df['Precio unitario']

Primera idea: ¿Qué países generan ingresos?

Utilicemos nuestros datos limpios para responder la pregunta de NovaShop: "¿Qué países están impulsando nuestras ventas?"
Usaremos una poderosa combinación de tres pasos:

Agrupar por la columna País. Agregue (suma) los ingresos dentro de cada grupo. Ordene los resultados de mayor a menor ingreso. # Agrupar por país, sumar los ingresos y ordenar por los 10 principales top_countries = df.groupby('Country')['Ingresos'].sum().sort_values(ascending=False).head(10) print(“n — — 10 países principales por ingresos (GBP) — -”) print(top_countries)

Producción:

— 10 países principales por ingresos (GBP) — País Reino Unido 941268.661 Países Bajos 27435.830 EIRE 26066.000 Francia 23645.330 Alemania 22389.510 Australia 12429.990 España 5600.900 Suiza 5483.890 Hong Kong 3597.850 Bélgica 3593.510 Nombre: Ingresos, tipo: float64

El resultado de esto suele ser un dominio masivo del Reino Unido. Esto es de esperar ya que la empresa tiene su sede en el Reino Unido. Los siguientes países nos dan una hoja de ruta rápida de dónde debería estar el enfoque internacional de NovaShop.

Conclusión

Lo hicimos. Tomamos un conjunto de datos sin procesar de medio millón de filas, lo dividimos para facilitar su administración, luchamos contra los valores faltantes, arreglamos los tipos de datos, filtramos los errores y calculamos nuestra primera métrica comercial clave. El trabajo duro y fundamental está hecho.

Aquí hay un resumen rápido de lo que conquistamos en la Parte 1:

Investigación de datos: utilizamos .head(), .info() y .describe() para identificar problemas cruciales como precios/cantidades negativos, ID de cliente faltantes y el formato de fecha y hora incorrecto. Limpieza de datos: eliminamos sistemáticamente valores nulos y duplicados, convertimos InvoiceDate en un objeto de fecha y hora adecuado y filtramos las transacciones que no eran de venta (devoluciones y artículos gratuitos). Ingeniería de funciones: creamos la columna de ingresos críticos. Primera información: generamos los 10 países principales por ingresos para NovaShop, brindándoles su primer punto de datos procesable del conjunto limpio.

El conjunto de datos limpio ahora está preparado para análisis más sofisticados. En la Parte 2, profundizaremos en el análisis de productos y la descomposición de series temporales. Descubriremos qué artículos son los que realmente generan dinero y analizaremos cómo cambia el volumen de ventas hora tras hora y mes tras mes.

¡Estoy emocionado de seguir adelante! Si te gustó este artículo. No dudes en hacérmelo saber en cualquiera de estos canales. Tus comentarios significarán mucho para mí.

Medio

LinkedIn

Gorjeo

YouTube