1. Faro

Nivel 10 · Desafío final: encuentra las canastas más grandes

La solicitud

De Megan Collins, Directora de Ventas

“Quiero entender nuestras canastas grandes. Dame nuestro valor medio de pedido, los diez pedidos más grandes de todos los tiempos y cuánto de nuestro ingreso por línea se vendió con descuento.”

Tu objetivo

Paso 1 de 3. Construye los ingresos por pedido (la suma del ingreso por línea de cada order_id en order_items). Asigna a result el ingreso medio por pedido, un float redondeado a 2.

  • Usa el DataFrame order_itemspendiente
  • Guarda un número en resultpendiente

Paso 1 · tu código

Python

Cargando el editor…

    ResultadoVista previa de order_items · presiona Ejecutar para ver el tuyo
    Vista previa de order_items
    order_item_idorder_idproduct_idquantityunit_price
    11001121931.99
    21001154311.99
    31002154411.99
    410021941149.00

    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 order_items · presiona Ejecutar para ver el tuyo
      Vista previa de order_items
      order_item_idorder_idproduct_idquantityunit_price
      11001121931.99
      21001154311.99
      31002154411.99
      410021941149.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 order_items · presiona Ejecutar para ver el tuyo
        Vista previa de order_items
        order_item_idorder_idproduct_idquantityunit_price
        11001121931.99
        21001154311.99
        31002154411.99
        410021941149.00

        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.

        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
        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
        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
        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
        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

        Cambiar la granularidad

        order_items tiene una fila por línea de pedido. Las preguntas de Megan son sobre pedidos. Pasar de una granularidad a otra más gruesa es para lo que sirve groupby: muchas líneas entran, sale una fila por pedido.

        items = order_items.assign(
            line_revenue=order_items["quantity"] * order_items["unit_price"]
            * (1 - order_items["discount_pct"] / 100)
        )
        per_order = items.groupby("order_id", as_index=False).agg(revenue=("line_revenue", "sum"))

        Comprueba la granularidad después: per_order["order_id"].is_unique debería ser True, y len(per_order) debería ser igual a order_items["order_id"].nunique(). Ambas son rápidas y detectan que has agrupado por la columna equivocada.

        La media de per_order["revenue"] es el valor medio de pedido (AOV), uno de los números más vigilados del comercio electrónico. Fíjate en que es la media de los totales de pedido, no la media del ingreso por línea: items["line_revenue"].mean() es la línea promedio, unas dos veces y media menor.

        Cada paso del Desafío final se ejecuta sobre una copia nueva de los datos, por eso cada paso vuelve a calcular el ingreso por línea. En un notebook lo calcularías una vez y lo reutilizarías.

        Ordenar los grupos

        Los diez pedidos más grandes son una ordenación sobre la tabla agregada:

        per_order.sort_values(["revenue", "order_id"], ascending=[False, True]).head(10)

        Las mayores canastas de Harbor & Pine vienen de cuentas de empresa que compran muebles en cantidad. Es útil decirlo en voz alta: una lista de «mejores pedidos» suele ser una lista de unos pocos clientes B2B, y los promedios de todos los pedidos esconden ese segmento por completo. Tu siguiente pregunta como analista sería quién los hizo, lo que requiere un join con orders y customers (el próximo capítulo).

        Una proporción de un total

        «¿Cuántos ingresos se vendieron con descuento?» es una razón entre dos sumas: ingresos con descuento sobre todos los ingresos. No necesitas un groupby; una máscara selecciona las líneas con descuento:

        line_revenue = order_items["quantity"] * order_items["unit_price"] * (1 - order_items["discount_pct"] / 100)
        share = line_revenue[order_items["discount_pct"] > 0].sum() / line_revenue.sum()

        La máscara viene de order_items y se aplica a line_revenue. Eso funciona porque ambas comparten el mismo índice, así que pandas alinea las filas por etiqueta. Esta alineación es un superpoder de pandas y a veces una trampa: si hubieras filtrado o reordenado una de ellas, las etiquetas seguirían coincidiendo, pero los índices reiniciados no.

        Fíjate en que es una proporción de ingresos, no de líneas. Una proporción de líneas con descuento, (order_items["discount_pct"] > 0).mean(), responde a otra pregunta. Aquí alrededor del 12.9% de las líneas llevan descuento pero aportan solo un 10.7% de los ingresos, en parte porque el propio descuento las reduce. Di cuál informas.

        Sobre el desorden

        Este nivel usa todas las líneas tal como están, incluida una pequeña cantidad de líneas con cantidades cero o negativas. Son errores de captura de datos que limpiarás en el capítulo 8. Su efecto aquí es mínimo, pero en un informe real limpiarías primero y lo dirías.

        Errores comunes

        • Sumar unit_price en lugar del ingreso por línea.
        • Ordenar filas de order_items (líneas sueltas) en lugar de pedidos.
        • Tomar la media de discount_pct como «proporción de ingresos con descuento».

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