1. Faro

Nivel 5 · Pedidos con cupón por estado

La solicitud

De Andrew Ramos, Responsable de Marketing

“¿Cómo terminan nuestros pedidos con cupón? Para cada código de cupón me gustaría el número de pedidos que se completaron, se cancelaron, se reembolsaron o siguen pendientes, uno junto al otro.”

Tu objetivo

Usando los pedidos que tienen coupon_code, asigna a result un DataFrame con las columnas coupon_code, cancelled, completed, pending, refunded: el número de pedidos de cada código de cupón y estado, como ints, con 0 donde un cupón no tiene pedidos en ese estado. Una fila por código de cupón, ordenada de la A a la Z.

  • Usa el DataFrame orderspendiente
  • Muestra exactamente coupon_code cancelled completed pending refundedpendiente
  • 6 filas, ordenadas como se pidependiente

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

    Un índice de dos niveles

    Agrupa por dos columnas y cuenta, y pandas devuelve una Series cuyo índice tiene dos niveles:

    counts = orders.groupby(["channel", "status"]).size()
    # channel      status
    # app          cancelled     265
    #              completed    3885
    #              pending        50
    #              refunded      120
    # marketplace  cancelled     108
    # ...

    Ese índice es un MultiIndex: cada entrada es un par como ("app", "completed"). Es una tabla larga disfrazada, una fila por par.

    unstack: de filas a columnas

    .unstack() toma el nivel más interno del índice y convierte sus valores en columnas:

    counts.unstack()
    # status       cancelled  completed  pending  refunded
    # channel
    # app                265       3885       50       120
    # marketplace        108       1597       19        43
    # web                353       5367       52       141

    Es la misma cuadrícula que construye pivot_table; groupby más unstack es simplemente otro camino hasta ella, práctico cuando ya tienes la Series agrupada. Para mover un nivel distinto, nómbralo: counts.unstack("channel") pone los canales arriba y los estados en el lateral.

    Combinaciones ausentes

    Un groupby de dos claves solo lista los pares que ocurren realmente. Cuando aplicas unstack, cualquier par que nunca ocurrió deja un hueco, rellenado con NaN:

    orders.groupby(["coupon_code", "status"]).size().unstack()
    # status       cancelled  completed  pending  refunded
    # HOLIDAY20         12.0      174.0      NaN       7.0

    Ninguno de los pedidos de HOLIDAY20, SPRING15 o SUMMER15 está pendiente, porque casi todos los pedidos pendientes se hicieron en los últimos días de 2025, después de que terminaran esas campañas. Para un recuento, una celda vacía es realmente cero, así que pasa fill_value=0. Rellena los huecos y mantiene los recuentos como enteros.

    Fíjate en que los pedidos sin cupón (10.277) no aparecen en absoluto: groupby descarta las claves NaN por defecto. Pasa dropna=False a groupby si los quieres como una fila propia.

    stack: de vuelta a una fila por par

    .stack() es lo inverso: mueve las columnas de vuelta al nivel más interno del índice.

    grid = counts.unstack()
    grid.stack()          # the long Series again, one entry per channel and status

    stack descarta por defecto las celdas NaN, así que tras un unstack y un stack recuperas los pares originales.

    Gestionar el índice

    Las cuadrículas y los resultados agrupados guardan sus claves en el índice. Tres métodos las mueven:

    long = grid.stack().reset_index(name="n_orders")   # index levels → ordinary columns
    by_status = long.set_index("status")               # a column → the index
    by_status.sort_index()                             # sort rows by their index labels

    reset_index() es lo que llamas antes de unir, aplicar melt o exportar, para que las claves sean columnas reales. set_index() va en el otro sentido cuando quieres buscar filas con .loc["completed"]. sort_index() ordena las filas por etiqueta, como ya hacen groupby y pivot_table.

    Errores comunes

    • Aplicar unstack sin fill_value=0 sobre recuentos, dejando NaN y columnas float como 174.0.
    • Aplicar unstack al nivel equivocado. Sin argumento es la última clave por la que agrupaste; nombra el nivel para estar seguro.
    • Rellenar con 0 cuando la celda es una media. Una media vacía es desconocida, no cero.
    • Olvidar que groupby descarta las claves ausentes, y luego preguntarse adónde fueron los pedidos sin cupón.

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