La solicitud

De Megan Collins, Directora de Ventas
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 2 de 3. Asigna a result un DataFrame con los 10 pedidos con mayores ingresos: columnas order_id, revenue (redondeado a 2), ordenado por revenue descendente y luego order_id ascendente, índice reiniciado.
- Usa el DataFrame
order_itemspendiente - Muestra exactamente
order_idrevenuependiente - 10 filas, ordenadas como se pidependiente
Paso 3 de 3. Asigna a result la proporción del ingreso total por línea que proviene de líneas con discount_pct mayor que 0, como un float entre 0 y 1 redondeado a 3 decimales.
- Usa el DataFrame
order_itemspendiente - Guarda un número en
resultpendiente
Paso 1 · tu código
Cargando el editor…
Solución de referencia
| order_item_id | order_id | product_id | quantity | unit_price |
|---|---|---|---|---|
| 1 | 1001 | 121 | 9 | 31.99 |
| 2 | 1001 | 154 | 3 | 11.99 |
| 3 | 1002 | 154 | 4 | 11.99 |
| 4 | 1002 | 194 | 1 | 149.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
Cargando el editor…
Solución de referencia
| order_item_id | order_id | product_id | quantity | unit_price |
|---|---|---|---|---|
| 1 | 1001 | 121 | 9 | 31.99 |
| 2 | 1001 | 154 | 3 | 11.99 |
| 3 | 1002 | 154 | 4 | 11.99 |
| 4 | 1002 | 194 | 1 | 149.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
Cargando el editor…
Solución de referencia
| order_item_id | order_id | product_id | quantity | unit_price |
|---|---|---|---|---|
| 1 | 1001 | 121 | 9 | 31.99 |
| 2 | 1001 | 154 | 3 | 11.99 |
| 3 | 1002 | 154 | 4 | 11.99 |
| 4 | 1002 | 194 | 1 | 149.00 |
Mote revisa tu resultado
Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.
Lo que recibe Megan, y por qué
DataFrames
Cargados como DataFrames de pandas, junto con pd y np.
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 |
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 |
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
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_priceen 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».
¡Linterna encendida!
Practicaste: new columngroupbynamed aggregationsort valuesboolean mask
- Resuelto
- Sin pistas
- Al primer intento
