1. Faro

Nivel 2 · Revisa y compara la subida de precios en pandas

La solicitud

De Thomas Whitaker, Director de Merchandising

“Acordamos la pregunta: para los productos cuyo precio subió el 1 de marzo, unidades e ingresos de marzo a agosto frente a los mismos meses del año pasado, junto a los productos que dejamos como estaban. Primero muéstrame que encontraste los productos correctos y que el cambio de verdad ocurrió el 1 de marzo.”

Tu objetivo

Paso 1 de 2. Revisa el cambio. Para cada línea de pedido de los productos cuyo precio subió, etiqueta period ('before' si order_date < '2025-03-01', si no 'after') y price_paid ('old' si el unit_price de la línea es igual al precio más bajo del producto, si no 'new'). Asigna a result un DataFrame con las columnas period, price_paid y lines (el número de líneas de pedido), ordenado por period de forma descendente y luego por price_paid de forma descendente, con índice por defecto. Usa todas las líneas de pedido, sea cual sea el estado.

  • Usa el DataFrame orderspendiente
  • Usa el DataFrame order_itemspendiente
  • Muestra exactamente period price_paid linespendiente

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.

      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

      Qué estás revisando, y por qué

      Antes de comparar nada, demuestra que estás mirando los productos correctos y la fecha correcta. Thomas dice «unos 30 productos, 1 de marzo». Los datos pueden confirmar ambas cosas, porque order_items guarda el precio pagado en cada línea, mientras que products["unit_price"] solo guarda el precio de lista de hoy.

      Un producto cuyo precio cambió tiene dos precios distintos entre sus líneas de pedido. La agregación con nombre da ambos en una sola pasada, y una máscara booleana conserva los productos donde difieren:

      prices = order_items.groupby("product_id", as_index=False).agg(
          old_price=("unit_price", "min"), new_price=("unit_price", "max")
      )
      prices = prices[prices["old_price"] != prices["new_price"]]
      len(prices)   # 29

      El paso 1 etiqueta entonces cada una de sus líneas dos veces, por fecha y por precio, con np.where, y cuenta las combinaciones. Si el cambio ocurrió limpiamente el 1 de marzo, solo existen dos combinaciones: antes con el precio viejo, después con el nuevo.

      En igualdad de condiciones, con un grupo de control

      El paso 2 responde la pregunta acordada. Tres decisiones hacen justa la comparación:

      • Los mismos meses, con un año de diferencia. De marzo a agosto de 2025 frente a marzo a agosto de 2024.
      • Productos en igualdad de condiciones. Solo los productos lanzados antes del 1 de enero de 2024, para que todos estuvieran a la venta durante ambas ventanas completas.
      • Un grupo de control. Todos los demás productos se etiquetan como unchanged. Su crecimiento es lo que la tienda hizo de todos modos.

      isin etiqueta los grupos en una línea, y pivot_table pone cada año en su propia columna, la versión en pandas de SUM(CASE WHEN ...):

      lines["price_group"] = np.where(lines["product_id"].isin(raised), "raised", "unchanged")
      u = lines.pivot_table(index="price_group", columns="yr", values="quantity", aggfunc="sum")

      Las columnas de u son los años como enteros, así que u[2025] - u[2024] resta fila por fila. Calcula los porcentajes con estos totales sin redondear y redondea al final.

      Cómo leer el resultado

      price_group units_change_pct revenue_change_pct
      raised 21.0 44.3
      unchanged 9.2 13.4

      Si la subida hubiera dañado la demanda, los productos con precio más alto crecerían menos que los que no cambiaron. Crecieron más. Los ingresos crecieron más rápido que las unidades porque cada unidad dejó cerca de 6% más y porque las ventas se desplazaron hacia los artículos más caros del grupo.

      Errores comunes

      • Filtra con < sobre el día siguiente al final de la ventana: d < "2025-09-01" conserva todo el 31 de agosto.
      • Olvidar .copy() después de filtrar y luego añadir columnas produce un SettingWithCopyWarning.
      • pivot_table ordena su índice, así que el resultado ya está en el orden de price_group; el sort_values explícito convierte eso en una promesa en lugar de una coincidencia.

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