Ganador desafío Transporte y Logística

¡Enhorabuena! He ganado el 1er lugar en la categoría Spanish del FP20 Analytics Challenge 2025 🏆. Este reconocimiento no solo refuerza mi pasión por las operaciones logísticas, análisis de datos y la visualización con Power BI, sino que también refleja el esfuerzo detrás de cada modelo, cada fórmula DAX y cada historia contada a través de los datos. Competir con profesionales de todo el mundo ha sido un desafío inspirador, y hoy celebro este logro como un impulso para seguir innovando en el emocionante mundo del análisis de datos..

Ver Publicación en Linkedin
Importación de datos en Power BI

Proyecto 2

🎯 Objetivos del Proyecto

1️⃣ Rutas principales:

¿Cuáles son las rutas de envío más comunes y sus distancias promedio?


2️⃣ Tiempos de entrega:

¿Qué rutas tienen los tiempos de entrega más largos?


3️⃣ Envíos Top:

¿Cuáles son las fechas de reserva y entrega con mayor actividad?


4️⃣ Análisis de retrasos:

¿Qué factores contribuyen a los retrasos en los envíos?


5️⃣ Información del cliente:

¿Qué clientes reciben la mayoría de los envíos y experimentan retrasos?


6️⃣ Tendencias de los proveedores:

¿Qué proveedores manejan la mayoría de los envíos y algunos tienen mayores retrasos?


7️⃣ Movimiento de materiales:

¿Cuáles son los materiales que se envían con más frecuencia y ciertos materiales tienen tiempos de entrega más largos?


8️⃣ Cuellos de botella:

¿Dónde están los retrasos de envío más comunes según los datos de GPS?

Tecnología y Herramientas

Diccionario origen de datos 'db_logistica'

Análisis Exploratorio de Datos (EDA)

Paso 1: Carga y Exploración Inicial de Datos

  • Importamos librerías.
  • Carga del dataset desde un archivo Excel.
  • Exploración inicial.
                
     # Importar librerías
        import pandas as pd
        import matplotlib.pyplot as plt
        from math import radians, sin, cos, sqrt, atan2          
                
            
                
    # Cargar el archivo Excel
        file_path = 'Transportation & Logistics Tracking Dataset_Spanish.xlsx'
        df = pd.read_excel(file_path, sheet_name='Datos Primarios')        
                
            
                
    # Exploración inicial de los datos
        df.head()
        df.info()   
                
            
                df.info()
            
                
    # Verificar valores nulos, NA y celdas vacías
    null_values = df.isnull().sum()
    na_values = df.isna().sum()
    empty_values = (df == '').sum()
    
    # Mostrar el resumen de valores nulos, NA y celdas vacías
    print("Valores nulos por columna:")
    print(null_values)
    print("\nValores NA por columna:")
    print(na_values)
    print("\nCeldas vacías por columna:")
    print(empty_values)
                
            
                df.isnull()
            

Paso 2: Transformación de datos

Al detectar campos con datatype incorresctos y valores vacíos como nulos procederé a modificarlos.

                
    # Convertir formatos de fecha
    df['ETA Real'] = pd.to_datetime(df['ETA Real'], errors='coerce')
    df['ETA Planificada'] = pd.to_datetime(df['ETA Planificada'], errors='coerce')
    df['Fecha de Inicio del Viaje'] = pd.to_datetime(df['Fecha de Inicio del Viaje'], errors='coerce')
    df['Fecha de Fin del Viaje'] = pd.to_datetime(df['Fecha de Fin del Viaje'], errors='coerce') 
                
            
                
  
    #Eliminamos registros sin Proveedor GPS
    df = df.dropna(subset=['Proveedor GPS']) 

    #Cambiamos los valores nulos con la mediana de la columna.
    df['Kilómetros Mínimos a Cubrir por Día'] = df['Kilómetros Mínimos a Cubrir por Día'].fillna(df['Kilómetros Mínimos a Cubrir por Día'].median()) 

    #Cambiamos los valores nulos con la moda de la columna.
    df['ETA Real'] = df['ETA Real'].fillna(df['ETA Real'].mode()[0]) 
    
    #Cambiamos los valores nulos con la cadena 'Falta Información'
    df['Tipo de Vehículo'] = df['Tipo de Vehículo'].fillna('Falta Información') 
    
    #Cambiamos los valores nulos con la cadena 'Falta Información'
    df['Nombre del Conductor'] = df['Nombre del Conductor'].fillna('Falta Información') 
    
    #Cambiamos los valores nulos con la cadena 'Falta Información'
    df['Número de Móvil del Conductor'] = df['Número de Móvil del Conductor'].fillna('Falta Información') 
    
                
            
                
  
    # Segunda verificación de valores nulos, NA y celdas vacías
    null_values = df.isnull().sum()
    na_values = df.isna().sum()
    empty_values = (df == '').sum()
    
    # Mostrar el resumen de valores nulos, NA y celdas vacías
    print("Valores nulos por columna:")
    print(null_values)
    print("\nValores NA por columna:")
    print(na_values)
    print("\nCeldas vacías por columna:")
    print(empty_values)
    
                
            
                df.isnull()
            
                
  
    # Exportamos el DataFrame limpio a un archivo Excel
    df.to_excel('db_Transportation_&_Logistics_Tracking.xlsx', index=False, sheet_name='Datos Limpios')
    
    print("Archivo exportado exitosamente como 'db_Transportation_&_Logistics_Tracking.xlsx'")
    
                
            

Proceso de Creación del Modelo

Paso 1: Importación de Datos

Importamos archivo limpio, previamente manipulado. Iremos a Power Query para transformar algunos de sus campos.



Importación de datos en Power BI

Paso 2: Modificación de Campos

En PowerQuery, fui a la opción 'Transformar' > 'Columna de texto' > 'Dividir columna' > 'Dividir columna por Delimitador' > Separamos por ',', esto nos permitira reconocer el nombre de la compañia o el nombre de la bodega, luego tendremos la ubicación específica del distrito en donde se encuentra esa bodega y por último tendremos el estado del país indio.



DIVIDIR COLUMNAS POR DELIMITADOR

Una vez divididas los campos de Ubicación de Origen y Destino, eliminamos los espacios, para evitar cualquier tipo de error.


DIVIDIR COLUMNAS POR DELIMITADOR

A continuación procederé a separar columna con formato de Fecha/Hora a Fecha y Hora por separado para trabajar de mejor manera los análisis de tiempo


Visualizaciones en Power BI

Campos separados. Utilizando dividir columna por separador 'Espacio'


Visualizaciones en Power BI

Tabla Calendario, Medidas y Fórmulas DAX

1. Creación de la Tabla Calendario

Para facilitar el análisis temporal, creé una tabla de calendario (Tabla Calendario) que contiene fechas iterativas basadas en el rango de fechas de la tabla db_logistica. Luego, agregué columnas adicionales para desglosar la fecha en componentes útiles como día, mes, año, semestre y semana.


    Tabla Calendario = CALENDAR(
        MIN(db_logistica[Fecha de Reserva]),
        MAX(db_logistica[Fecha de Reserva]))
                

Columnas adicionales:


    Año = YEAR('Tabla Calendario'[Date])
    Mes = MONTH('Tabla Calendario'[Date])
    Día = DAY('Tabla Calendario'[Date])
    Mes Nombre = FORMAT('Tabla Calendario'[Date], "mmmm")
    Día Nombre = FORMAT('Tabla Calendario'[Date], "dddd") 
    Semestre = IF(MONTH([Date]) <= 6, 1, 2)
    Día Numérico = WEEKDAY('Tabla Calendario'[Date], 2)
                

2. Creación de Medidas para db_logistica

Creé una tabla de medidas (Tabla Medidas) para calcular métricas claves relacionadas con las Reservas, Transporte y Niveles de Servicio.


    Total ID Reserva = COUNT(db_logistica[ID de Reserva])

    Total Market = CALCULATE(
        COUNTROWS(db_logistica),
                    db_logistica[Tipo de Envío] = "Market")

    Total Regular = CALCULATE(
        COUNTROWS(db_logistica),
            db_logistica[Tipo de Envío] = "Regular")

    SUM Distancia de Transporte (KM) = SUM(db_logistica[Distancia de Transporte (KM)])

    Entrega Anticipada = CALCULATE(
        COUNTROWS(db_logistica), db_logistica[Estado_Entrega] = "Entrega Anticipada")

    Entrega Mismo Día = CALCULATE(
        COUNTROWS(db_logistica), db_logistica[Estado_Entrega]="Entrega mismo día")

    Entrega Fuera de Plazo = CALCULATE(
        COUNTROWS(db_logistica), db_logistica[Estado_Entrega] = "Entrega fuera de plazo")

    AVG Días de Entrega = AVERAGE(db_logistica[Días_ETA_Real_vs_Planificada])

    AVG Distancia por Ruta = 
        AVERAGEX(
            db_logistica, 
            db_logistica[Distancia de Transporte (KM)])

    AVG_Días_Reserva = 
        AVERAGE(db_logistica[Días_Reserva_ETA])
    

    AVG_Envios_Dias = 
        AVERAGEX(
            db_logistica, 
            DATEDIFF(
                db_logistica[Fecha de Reserva] + db_logistica[Hora de Reserva], 
                db_logistica[Fecha ETA Real] + db_logistica[Hora ETA Real], 
                DAY
            )
        )

    Tiempo de Entrega desde Reserva = 
        VAR Diferencia = DATEDIFF( db_logistica[Fecha de Reserva] + db_logistica[Hora de Reserva], 
                                    db_logistica[Fecha ETA Real] + db_logistica[Hora ETA Real], SECOND)
        VAR Dias = QUOTIENT(Diferencia, 86400)  // 86400 segundos en un día
        VAR Horas = QUOTIENT( MOD(Diferencia, 86400), 3600 )  // 3600 segundos en una hora
        VAR Minutos = QUOTIENT( MOD(Diferencia, 3600), 60 )  // 60 segundos en un minuto
        RETURN
            FORMAT(Dias, "0") & " días, " & FORMAT(Horas, "0") & " horas, " & FORMAT(Minutos, "0") & " minutos"

    Estado_Entrega = 
        VAR FechaPlanificada = db_logistica[Fecha ETA Planificada]
        VAR FechaETAReal = db_logistica[Fecha ETA Real]
        RETURN
            IF(
                FechaETAReal = FechaPlanificada, 
                "Entrega mismo día",
                IF(
                    FechaETAReal > FechaPlanificada, 
                    "Entrega fuera de plazo",
                    "Entrega Anticipada"
                )
            )

    Rango_Horas_ETA_Real = 
        " " & FORMAT(TIME(HOUR(db_logistica[Hora ETA Real]), 0, 0), "hh:mm") & " - " &
        FORMAT(TIME(HOUR(db_logistica[Hora ETA Real]) + 1, 0, 0), "hh:mm")
                

3. Creación de Medidas para Porcentajes

Para complementar el análisis, creé una segunda tabla de medidas (Tabla %) que calcula porcentajes basados en las métricas anteriores. Esto permite entender la proporción de Reservas en cada estado respecto al total.


                    
    % Total Regular = DIVIDE([Total Regular],[Total ID Reserva]) 

    % Total Market = DIVIDE([Total Market], [Total ID Reserva]) 

    % Entrega mismo día = DIVIDE([Entrega Mismo Día],[Total ID Reserva])

    % Entrega fuera de plazo = DIVIDE([Entrega Fuera de Plazo],[Total ID Reserva])

    % Entrega Anticipada = DIVIDE([Entrega Anticipada],[Total ID Reserva])
                

Relaciones entre Orígenes de Datos

El modelo de datos lo conectamos con nuestra Tabla Calendario para analizar los datos de la tabla db_logistica en base al tiempo.

  • La tabla db_logistica se relaciona con Tabla Calendario a través del campo Fecha de Reserva de muchos a uno

Relaciones en Power BI

Visualizaciones en Power BI (SIN FIGMA)

Aquí mostraré algunas imagenes de los paneles creados a partir de las de las medidas creadas anteriormente, pero los paneles finales los mostraré una vez que los alla personalizado con FIGMA para finalizar con una Historytelling con todo lo visto.

Visualización 1

Análisis de Reservas .

Visualizaciones en Power BI (CON FIGMA)

Aquí mostraré los los diseños que he creado con figma para crear un impacto visual mas potente de los dashboard que he creado. Los dashboard y paneles de mando estaran en la siguiente publicación.

Visualización 1

Descripción inicial de la sección CON FIGMA.

Proyecto Power BI - Publicación


LINK: Visita el proyecto Power BI aquí


Conclusiones del Proyecto

Panel 1: Análisis de Reservas


El análisis de reservas revela una evolución significativa en la cantidad de solicitudes, con un crecimiento exponencial del 1.768% de 2019 a 2020. Este incremento refleja una mayor demanda y expansión operativa, pero también destaca la importancia de optimizar los procesos logísticos para garantizar que este crecimiento sea sostenible. La gran mayoría de las reservas provienen de clientes regulares, lo que subraya la necesidad de fidelizar a este segmento y garantizar que su experiencia sea eficiente y confiable. Sin embargo, un punto crítico identificado es el cumplimiento en los tiempos de entrega, ya que más del 61% de las reservas se entregaron fuera de plazo. Esto indica un déficit operativo que podría afectar la percepción del servicio y la retención de clientes a largo plazo.


Recomendaciones:



Panel 2: Análisis de Transporte