1. Faro
  2. Faro

Nivel 2 · Revisión de la temporada navideña en pandas

La solicitud

De Megan Collins, Directora de Ventas

“Priya quiere la revisión de las fiestas de 2025 en un cuaderno para que el equipo pueda ampliarla. Por favor, constrúyela en pandas y asegúrate de que cada cifra coincida con la versión en SQL de este caso.”

Tu objetivo

Paso 1 de 3. Asigna a result un DataFrame con las columnas month (texto 'YYYY-MM'), orders (pedidos completados distintos), net_revenue (redondeado a 2) y avg_order_value (ingreso neto / pedidos, redondeado a 2) de septiembre a diciembre de 2025, ordenado por month y con índice por defecto.

  • Usa el DataFrame orderspendiente
  • Usa el DataFrame order_itemspendiente
  • Muestra exactamente month orders net_revenue avg_order_valuependiente

Paso 1 · tu código

Python

Cargando el editor…

    ResultadoVista previa de orders · presiona Ejecutar para ver el tuyo
    Vista previa de orders
    order_idcustomer_idorder_datestatuschannel
    10011252023-01-01 20:51:19completedweb
    10021562023-01-02 07:58:46completedweb
    1003222023-01-02 10:25:27completedmarketplace
    1004482023-01-02 11:21:51completedmarketplace

    Mote revisa tu resultado

    Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.

    Paso 2 · tu código

    Python

    Cargando el editor…

      ResultadoVista previa de products · presiona Ejecutar para ver el tuyo
      Vista previa de products
      product_idnamecategorysubcategoryunit_price
      101Vale Velvet LoveseatFurnitureSofas1659.00
      102Alder Oak Coffee TableFurnitureTables919.00
      103Orchard Rattan Dining ChairFurnitureChairs789.00
      104Upland Oak BookcaseFurnitureStorage429.00

      Mote revisa tu resultado

      Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.

      Paso 3 · tu código

      Python

      Cargando el editor…

        ResultadoVista previa de orders · presiona Ejecutar para ver el tuyo
        Vista previa de orders
        order_idcustomer_idorder_datestatuschannel
        10011252023-01-01 20:51:19completedweb
        10021562023-01-02 07:58:46completedweb
        1003222023-01-02 10:25:27completedmarketplace
        1004482023-01-02 11:21:51completedmarketplace

        Mote revisa tu resultado

        Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.

        DataFrames

        Cargados como DataFrames de pandas, junto con pd y np.

        orders12,000 filas · 7 columnas
        • order_idinteger
        • customer_idinteger
        • order_datetimestamp
        • statusvarchar
        • channelvarchar
        • shipping_feedecimal
        • coupon_codevarchar
        Primeras 5 filas de orders
        order_idcustomer_idorder_datestatuschannelshipping_feecoupon_code
        10011252023-01-01 20:51:19completedweb0.00NaN
        10021562023-01-02 07:58:46completedweb0.00NaN
        1003222023-01-02 10:25:27completedmarketplace4.99WELCOME10
        1004482023-01-02 11:21:51completedmarketplace4.99WELCOME10
        1005822023-01-02 11:43:36completedweb0.00WELCOME10
        order_items30,338 filas · 6 columnas
        • order_item_idinteger
        • order_idinteger
        • product_idinteger
        • quantityinteger
        • unit_pricedecimal
        • discount_pctdecimal
        Primeras 5 filas de order_items
        order_item_idorder_idproduct_idquantityunit_pricediscount_pct
        11001121931.990.00
        21001154311.990.00
        31002154411.990.00
        410021941149.000.00
        51002193219.990.00
        products120 filas · 8 columnas
        • product_idinteger
        • namevarchar
        • categoryvarchar
        • subcategoryvarchar
        • unit_pricedecimal
        • unit_costdecimal
        • launched_ondate
        • is_activeboolean
        Primeras 5 filas de products
        product_idnamecategorysubcategoryunit_priceunit_costlaunched_onis_active
        101Vale Velvet LoveseatFurnitureSofas1659.00NaN2023-09-11true
        102Alder Oak Coffee TableFurnitureTables919.00503.502024-05-16true
        103Orchard Rattan Dining ChairFurnitureChairs789.00429.872023-05-27true
        104Upland Oak BookcaseFurnitureStorage429.00214.702022-12-20true
        105Avery Leather Bed FrameFurnitureBeds449.00232.702021-08-31true
        customers2,018 filas · 9 columnas
        • customer_idinteger
        • first_namevarchar
        • last_namevarchar
        • emailvarchar
        • cityvarchar
        • statevarchar
        • signup_datedate
        • segmentvarchar
        • referral_sourcevarchar
        Primeras 5 filas de customers
        customer_idfirst_namelast_nameemailcitystatesignup_datesegmentreferral_source
        1OwenHudsonowen.hudson@icloud.comSan FranciscoCA2022-06-02consumersearch
        2DouglasMendozadmendoza69@gmail.comAtlantaGA2022-06-02consumerNaN
        3HenryBrookshbrooks@lakesidecafe.comLos AngelesCA2022-06-04businessNaN
        4BrianGrahambgraham@bluecedarhomes.comSeattleWA2022-06-06businesspinterest
        5KyleJimenezkjimenez27@yahoo.comIndianapolisIN2022-06-07consumerNaN
        returns1,673 filas · 5 columnas
        • return_idinteger
        • order_item_idinteger
        • returned_ondate
        • reasonvarchar
        • refund_amountdecimal
        Primeras 5 filas de returns
        return_idorder_item_idreturned_onreasonrefund_amount
        1142023-01-10damaged83.69
        2422023-01-10wrong_item19.99
        31052023-01-14wrong_item10.99
        4242023-01-16changed_mind26.99
        51262023-01-16late20.99
        employees60 filas · 8 columnas
        • employee_idinteger
        • first_namevarchar
        • last_namevarchar
        • departmentvarchar
        • titlevarchar
        • manager_idinteger
        • hire_datedate
        • salaryinteger
        Primeras 5 filas de employees
        employee_idfirst_namelast_namedepartmenttitlemanager_idhire_datesalary
        1CatherineHaleExecutiveChief Executive OfficerNaN2016-03-01285000
        2RobertKimExecutiveChief Operating Officer12017-01-09235000
        3MeganCollinsSalesHead of Sales12018-04-16172000
        4ChloeGutierrezSalesAccount Executive32020-01-2172000
        5FrankBellSalesAccount Executive32023-05-2173000
        web_sessions40,000 filas · 7 columnas
        • session_idinteger
        • customer_idinteger
        • started_attimestamp
        • devicevarchar
        • sourcevarchar
        • pages_viewedinteger
        • convertedboolean
        Primeras 5 filas de web_sessions
        session_idcustomer_idstarted_atdevicesourcepages_viewedconverted
        1NaN2023-01-01 23:02:24mobilepaid1false
        2182023-01-01 15:07:49desktoppaid1false
        3NaN2023-01-01 16:55:46desktopdirect7false
        4NaN2023-01-01 12:43:28mobileorganic3false
        5NaN2023-01-01 13:06:21mobilesocial2false
        marketing_spend3,276 filas · 3 columnas
        • spend_datedate
        • channelvarchar
        • amountdecimal
        Primeras 5 filas de marketing_spend
        spend_datechannelamount
        2023-01-01paid492.99
        2023-01-01email37.38
        2023-01-01social200.53
        2023-01-02paid709.13
        2023-01-02email64.13

        La lección

        La misma pregunta, las mismas definiciones

        Esta es la misma revisión del nivel de SQL de este caso, construida en pandas. Mantén las definiciones idénticas o los números no coincidirán: pedidos completados, líneas con quantity > 0, ingreso quantity * unit_price * (1 - discount_pct / 100), sin envío. Cuando una versión en SQL y otra en pandas del mismo informe no coinciden, nueve de cada diez veces es porque una de las dos filtró distinto.

        Construye la tabla base una sola vez

        En SQL repites el JOIN en cada consulta. En pandas construyes una tabla a nivel de línea y la reutilizas:

        completed = orders[orders["status"] == "completed"]
        lines = order_items[order_items["quantity"] > 0].merge(
            completed[["order_id", "order_date", "channel"]],
            on="order_id", how="inner", validate="many_to_one",
        )
        lines["revenue"] = lines["quantity"] * lines["unit_price"] * (1 - lines["discount_pct"] / 100)

        Filtrar orders antes de la unión es la versión en pandas de WHERE o.status = 'completed': la unión interna descarta toda línea cuyo pedido no esté en completed. validate="many_to_one" hace que pandas lance un error si alguna vez se duplica un id de pedido, que es como detectarías una duplicación antes de que llegue a una diapositiva.

        Tres herramientas de fechas y etiquetas que usa este caso

        Etiquetas de mes con .dt.strftime. En una columna datetime, .dt.strftime("%Y-%m") devuelve texto como "2025-11". %Y es el año de cuatro dígitos y %m el mes de dos dígitos. El texto con esa forma se ordena en orden de calendario, así que es una buena clave para groupby.

        orders["order_date"].dt.strftime("%Y-%m").head(2)
        # 0    2023-01
        # 1    2023-01

        Números de mes con .dt.month. .dt.month da el mes como número, de 1 a 12, así que lines["order_date"].dt.month == 11 es True en las filas de noviembre.

        Etiquetas a partir de una condición con np.where. np.where(condition, value_if_true, value_if_false) construye una columna fila por fila, como un if de dos ramas:

        orders["shipping_type"] = np.where(orders["shipping_fee"] == 0, "free shipping", "paid shipping")

        Cada fila cuya tarifa es 0 recibe "free shipping", y cada una de las demás "paid shipping". En este caso lo usas para etiquetar cada línea con su temporada o mes, y luego pivotar sobre esa etiqueta.

        Por último, sort_index() ordena un DataFrame por su índice, que después de un pivote es la clave de las filas (channel en el paso 3), así que equivale a ordenar por canal antes de reset_index().

        Paso 1: groupby con agregación con nombre

        COUNT(DISTINCT order_id) se convierte en ("order_id", "nunique"). Usar "count" en su lugar contaría líneas, el mismo error que COUNT(*) en SQL. El valor medio del pedido es un cociente de dos agregados, así que calcúlalo después del groupby, a partir del ingreso sin redondear, y redondea las columnas al final.

        Los filtros de fecha comparan una columna datetime64 con un texto: pandas interpreta "2026-01-01" como la medianoche, así que < "2026-01-01" conserva todas las marcas de tiempo del 31 de diciembre.

        Pasos 2 y 3: dos periodos como columnas

        El SUM(CASE WHEN period THEN revenue END) de SQL tiene un equivalente directo en pandas: etiqueta cada fila con su periodo y luego pivota.

        holiday = lines[in_24 | in_25].copy()
        holiday["season"] = np.where(holiday["order_date"] < "2025-01-01", "rev_2024", "rev_2025")
        wide = holiday.pivot_table(index="category", columns="season", values="revenue", aggfunc="sum")

        El resultado tiene category como índice y una columna por temporada. Dos líneas de limpieza lo hacen coincidir con la salida de SQL: wide.columns.name = None quita la etiqueta «season» sobrante de las columnas, y reset_index() vuelve a convertir el índice de categorías en una columna. Ponerles a las etiquetas de periodo los nombres finales de las columnas (rev_2024, nov_revenue) te ahorra un renombrado.

        Una alternativa es groupby(["category", "season"])["revenue"].sum().unstack(). Produce la misma tabla ancha; pivot_table simplemente lo dice en una sola llamada.

        SQL y pandas lado a lado

        Paso SQL pandas
        filtrar filas WHERE antes de GROUP BY máscara booleana antes de groupby
        conteo distinto COUNT(DISTINCT ...) nunique
        periodos lado a lado SUM(CASE WHEN ...) pivot_table(columns=...)
        ordenar ORDER BY ... DESC sort_values(..., ascending=False)

        Errores comunes

        • Redondear antes de dividir. Redondea las columnas de ingresos después de calcular el crecimiento, exactamente como lo hace la versión en SQL en su SELECT exterior.
        • Orden de las categorías. pivot_table ordena el índice alfabéticamente; el ejercicio pide el orden por crecimiento, así que ordena de forma explícita.
        • Olvidar reset_index. Un resultado con category como índice se ve igual en un cuaderno, pero no es la tabla que pide el ejercicio.
        • Periodos ausentes. Si una categoría no tuvo ventas en una temporada, pivot_table dejaría un valor faltante (NaN) en esa celda. Aquí todas las categorías venden en ambas temporadas, pero compruébalo antes de dividir.

        Elige qué puede ejecutarse en este sitio. Puedes cambiarlo cuando quieras desde "Configurar cookies", al pie de cada página.