La solicitud

De Jordan Reyes, Directora de Marketing
Tu objetivo
Usa pedidos registrados (status "completed" o "refunded"). Un pedido de reactivación es un pedido registrado hecho más de 180 días después del pedido registrado anterior del mismo cliente (intervalo = (order_date - previous order_date).dt.days). Asigna a result un dict con "reactivation_orders" (int), "reactivated_customers" (clientes con al menos un pedido de reactivación, int), "repeat_customers" (clientes con 2 o más pedidos registrados, int), "reactivated_pct" (reactivados × 100 / clientes recurrentes, redondeado a 1) y "net_sales": las ventas netas de los pedidos de reactivación (Σ quantity × unit_price × (1 − discount_pct / 100) sobre las líneas con quantity > 0), redondeadas a 2.
- Usa el DataFrame
orderspendiente - Usa el DataFrame
order_itemspendiente - Guarda un dict en
resultpendiente
Tu código
Cargando el editor…
Solución de referencia
| order_id | customer_id | order_date | status | channel |
|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace |
Mote revisa tu resultado
Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.
Lo que recibe Jordan, y por qué
DataFrames
Cargados como DataFrames de pandas, junto con pd y np.
orders12,000 filas · 7 columnas
order_idintegercustomer_idintegerorder_datetimestampstatusvarcharchannelvarcharshipping_feedecimalcoupon_codevarchar
| order_id | customer_id | order_date | status | channel | shipping_fee | coupon_code |
|---|---|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web | 0.00 | NaN |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web | 0.00 | NaN |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace | 4.99 | WELCOME10 |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace | 4.99 | WELCOME10 |
| 1005 | 82 | 2023-01-02 11:43:36 | completed | web | 0.00 | WELCOME10 |
order_items30,338 filas · 6 columnas
order_item_idintegerorder_idintegerproduct_idintegerquantityintegerunit_pricedecimaldiscount_pctdecimal
| order_item_id | order_id | product_id | quantity | unit_price | discount_pct |
|---|---|---|---|---|---|
| 1 | 1001 | 121 | 9 | 31.99 | 0.00 |
| 2 | 1001 | 154 | 3 | 11.99 | 0.00 |
| 3 | 1002 | 154 | 4 | 11.99 | 0.00 |
| 4 | 1002 | 194 | 1 | 149.00 | 0.00 |
| 5 | 1002 | 193 | 2 | 19.99 | 0.00 |
customers2,018 filas · 9 columnas
customer_idintegerfirst_namevarcharlast_namevarcharemailvarcharcityvarcharstatevarcharsignup_datedatesegmentvarcharreferral_sourcevarchar
| customer_id | first_name | last_name | city | state | signup_date | segment | referral_source | |
|---|---|---|---|---|---|---|---|---|
| 1 | Owen | Hudson | owen.hudson@icloud.com | San Francisco | CA | 2022-06-02 | consumer | search |
| 2 | Douglas | Mendoza | dmendoza69@gmail.com | Atlanta | GA | 2022-06-02 | consumer | NaN |
| 3 | Henry | Brooks | hbrooks@lakesidecafe.com | Los Angeles | CA | 2022-06-04 | business | NaN |
| 4 | Brian | Graham | bgraham@bluecedarhomes.com | Seattle | WA | 2022-06-06 | business | |
| 5 | Kyle | Jimenez | kjimenez27@yahoo.com | Indianapolis | IN | 2022-06-07 | consumer | NaN |
products120 filas · 8 columnas
product_idintegernamevarcharcategoryvarcharsubcategoryvarcharunit_pricedecimalunit_costdecimallaunched_ondateis_activeboolean
| product_id | name | category | subcategory | unit_price | unit_cost | launched_on | is_active |
|---|---|---|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 | NaN | 2023-09-11 | true |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 | 503.50 | 2024-05-16 | true |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 | 429.87 | 2023-05-27 | true |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 | 214.70 | 2022-12-20 | true |
| 105 | Avery Leather Bed Frame | Furniture | Beds | 449.00 | 232.70 | 2021-08-31 | true |
returns1,673 filas · 5 columnas
return_idintegerorder_item_idintegerreturned_ondatereasonvarcharrefund_amountdecimal
| return_id | order_item_id | returned_on | reason | refund_amount |
|---|---|---|---|---|
| 1 | 14 | 2023-01-10 | damaged | 83.69 |
| 2 | 42 | 2023-01-10 | wrong_item | 19.99 |
| 3 | 105 | 2023-01-14 | wrong_item | 10.99 |
| 4 | 24 | 2023-01-16 | changed_mind | 26.99 |
| 5 | 126 | 2023-01-16 | late | 20.99 |
employees60 filas · 8 columnas
employee_idintegerfirst_namevarcharlast_namevarchardepartmentvarchartitlevarcharmanager_idintegerhire_datedatesalaryinteger
| employee_id | first_name | last_name | department | title | manager_id | hire_date | salary |
|---|---|---|---|---|---|---|---|
| 1 | Catherine | Hale | Executive | Chief Executive Officer | NaN | 2016-03-01 | 285000 |
| 2 | Robert | Kim | Executive | Chief Operating Officer | 1 | 2017-01-09 | 235000 |
| 3 | Megan | Collins | Sales | Head of Sales | 1 | 2018-04-16 | 172000 |
| 4 | Chloe | Gutierrez | Sales | Account Executive | 3 | 2020-01-21 | 72000 |
| 5 | Frank | Bell | Sales | Account Executive | 3 | 2023-05-21 | 73000 |
web_sessions40,000 filas · 7 columnas
session_idintegercustomer_idintegerstarted_attimestampdevicevarcharsourcevarcharpages_viewedintegerconvertedboolean
| session_id | customer_id | started_at | device | source | pages_viewed | converted |
|---|---|---|---|---|---|---|
| 1 | NaN | 2023-01-01 23:02:24 | mobile | paid | 1 | false |
| 2 | 18 | 2023-01-01 15:07:49 | desktop | paid | 1 | false |
| 3 | NaN | 2023-01-01 16:55:46 | desktop | direct | 7 | false |
| 4 | NaN | 2023-01-01 12:43:28 | mobile | organic | 3 | false |
| 5 | NaN | 2023-01-01 13:06:21 | mobile | social | 2 | false |
marketing_spend3,276 filas · 3 columnas
spend_datedatechannelvarcharamountdecimal
| spend_date | channel | amount |
|---|---|---|
| 2023-01-01 | paid | 492.99 |
| 2023-01-01 | 37.38 | |
| 2023-01-01 | social | 200.53 |
| 2023-01-02 | paid | 709.13 |
| 2023-01-02 | 64.13 |
La lección
El abandono no es el final
El nivel 4 llamó abandonado a un cliente tras 180 días sin un pedido registrado. Es una etiqueta útil para la lista de hoy, pero es una instantánea. Si miras hacia atrás tres años, muchos clientes que cruzaron esa línea volvieron por su cuenta. Una reactivación es un pedido que pone fin a un silencio tan largo.
El pedido anterior, fila por fila
Ordena los pedidos registrados por cliente y fecha, y shift() dentro de cada cliente baja cada fecha
una fila, de modo que cada pedido ve el pedido anterior del cliente:
booked["prev"] = booked.groupby("customer_id")["order_date"].shift()
booked["gap"] = (booked["order_date"] - booked["prev"]).dt.days
El primer pedido de cada cliente no tiene anterior, así que su intervalo es NaN, y NaN > 180 es
False: los primeros pedidos nunca son reactivaciones, lo cual es correcto. El orden importa: shift()
toma la fila anterior, no la fecha anterior, así que con datos sin ordenar los intervalos no tienen
sentido.
Tres números, tres denominadores
Los pedidos y los clientes responden preguntas distintas. Un cliente puede reactivarse dos veces, así que hay más pedidos de reactivación que clientes reactivados. Para la proporción, usa a los clientes que podrían reactivarse: necesitas al menos dos pedidos para tener un intervalo, así que la base son los clientes con dos o más pedidos registrados, no todos los clientes.
Cuánto valen los pedidos de regreso
Usa las mismas ventas netas que el resto de la ruta: solo líneas válidas (quantity > 0), con el precio
pagado después del descuento del cupón. Busca los pedidos de reactivación en un total por pedido:
order_net.reindex(react["order_id"]).sum()
Compara el resultado con la tienda en conjunto. Si los pedidos de reactivación son más pequeños que un pedido típico, un descuento de recuperación se come una parte mayor de cada uno.
Por qué importa antes de una campaña
A un correo de recuperación enviado a todos los que llevan 180 días en silencio le seguirán muchos regresos. Algunos habrían ocurrido de todos modos, y el correo se llevará el crédito. El número que calculas aquí es la línea base que la campaña tiene que superar. La forma limpia de medir la campaña es reservar un grupo aleatorio de clientes que abandonaron y comparar.
Errores comunes
- Olvidar ordenar antes de
shift(). - Contar pedidos cancelados como el pedido “anterior”. Un pago fallido no pone fin a un silencio.
- Usar a todos los clientes como base. Quienes compraron una sola vez no pueden reactivarse.
¡Linterna encendida!
Practicaste: reactivationchurnshiftgapnet sales
- Resuelto
- Sin pistas
- Al primer intento
