La solicitud

De Catherine Hale, Directora General
Tu objetivo
Paso 1 de 3. Asigna a result el número de filas de order_items (un int) con una cantidad de 0 o menos.
- Usa el DataFrame
order_itemspendiente - Guarda un número en
resultpendiente
Paso 2 de 3. Usando solo las líneas de pedido con cantidad mayor que 0 que pertenezcan a pedidos con status 'completed' hechos en 2025, asigna a result un DataFrame con las columnas month (texto 'YYYY-MM') y revenue (suma de quantity * unit_price * (1 - discount_pct / 100), redondeada a 2), una fila por mes, ordenado por month ascendente, con el índice reiniciado.
- Usa el DataFrame
orderspendiente - Usa el DataFrame
order_itemspendiente - Muestra exactamente
monthrevenuependiente
Paso 3 de 3. Elimina los clientes duplicados por email normalizado (sin espacios, en minúsculas), conservando en cada grupo la fila con el customer_id más bajo. Asigna a result un DataFrame con las columnas segment y n_customers (clientes sin duplicados por segmento), ordenado por segment ascendente, con el índice reiniciado.
- Usa el DataFrame
customerspendiente - Muestra exactamente
segmentn_customerspendiente - 2 filas, ordenadas como se pidependiente
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_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.
Paso 3 · tu código
Cargando el editor…
Solución de referencia
| customer_id | first_name | last_name | city | |
|---|---|---|---|---|
| 1 | Owen | Hudson | owen.hudson@icloud.com | San Francisco |
| 2 | Douglas | Mendoza | dmendoza69@gmail.com | Atlanta |
| 3 | Henry | Brooks | hbrooks@lakesidecafe.com | Los Angeles |
| 4 | Brian | Graham | bgraham@bluecedarhomes.com | Seattle |
Mote revisa tu resultado
Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.
Lo que recibe Catherine, 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 |
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 |
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
Limpia y luego cuenta
Las cifras de la junta se comparan con las de finanzas, con la presentación del trimestre pasado y con la memoria del CEO. Cuando discrepan, la primera pregunta siempre es “¿qué incluiste?”. Un informe limpio responde esa pregunta antes de que nadie la haga: elimina las filas incorrectas conocidas, usa una definición declarada de ingresos y cuenta clientes reales, no registros.
Paso 1: dimensiona el problema
Antes de excluir nada, mídelo:
(order_items["quantity"] <= 0).sum()
order_items[order_items["quantity"] <= 0].head()
Las cantidades cero y negativas son errores de entrada de datos aquí (nunca son artículos devueltos). Saber que son 24 líneas de unas 30.000 te dice que el efecto es pequeño pero real, y te permite escribir una nota al pie de una línea: “24 líneas de pedido con cantidades inválidas excluidas”.
Nunca arregles filas incorrectas adivinando (pasar los negativos a positivo, por ejemplo) a menos que alguien que es dueño de los datos confirme que esa es la corrección correcta.
Paso 2: una serie limpia de ingresos mensuales
La definición, escrita primero: ingresos de línea tras descuento, solo cantidades válidas, solo pedidos completados, por mes de la fecha del pedido, 2025.
good = order_items[order_items["quantity"] > 0]
good = good.assign(line_revenue=good["quantity"] * good["unit_price"] * (1 - good["discount_pct"] / 100))
completed = orders.loc[orders["status"] == "completed", ["order_id", "order_date"]]
lines = good.merge(completed, on="order_id", how="inner", validate="many_to_one")
El merge inner hace dos trabajos: trae la fecha del pedido y descarta las líneas de pedidos cancelados, reembolsados y pendientes. Luego filtra a 2025 y agrupa por una etiqueta de mes:
lines_2025 = lines[lines["order_date"].dt.year == 2025]
lines_2025.assign(month=lines_2025["order_date"].dt.strftime("%Y-%m")).groupby("month", as_index=False)...
strftime("%Y-%m") da texto como '2025-11', que se ordena bien y se lee bien. Espera que noviembre
y diciembre destaquen: el Black Friday, el Cyber Monday y las fiestas los elevan muy por encima de
cualquier otro mes, mientras que febrero es el más tranquilo.
order_date incluye una hora, y eso está bien para agrupar por mes. Si alguna vez filtras con
<= "2025-12-31", recuerda que eso significa la medianoche al inicio del 31 de diciembre, lo que
descarta todo ese día. Usa < "2026-01-01" o .dt.year == 2025 en su lugar.
Paso 3: clientes reales
deduped = (
customers.assign(email_key=customers["email"].str.strip().str.lower())
.sort_values("customer_id")
.drop_duplicates(subset="email_key", keep="first")
)
deduped.groupby("segment", as_index=False).agg(n_customers=("customer_id", "count"))
Ordenar por customer_id antes de eliminar convierte “conservar el registro original” en una regla
explícita en lugar de un accidente del orden del archivo. El total sin duplicados debería ser
exactamente 18 menos que len(customers); compruébalo.
Ten en cuenta que esto cuenta a todos los que se registraron. Si la junta quiere clientes activos, esa es otra definición (clientes con un pedido completado en 2025) y otra cifra. Di cuál de las dos muestra la diapositiva.
Cómo es un buen paso de limpieza
- Está medido: sabes cuántas filas toca.
- Está explicado: una frase sobre el porqué.
- Es reproducible: código, no ediciones manuales en una hoja de cálculo.
- Está comprobado: los totales antes y después concilian.
Errores comunes
- Filtrar la cantidad después de agregar, cuando las líneas incorrectas ya están dentro de los totales.
- Agrupar solo por
dt.month, lo que mezcla meses de años distintos si falta el filtro de año. - Eliminar duplicados sin ordenar, de modo que un registro duplicado podría sobrevivir en lugar del original.
¡Linterna encendida!
Practicaste: data qualityfilteringdatesmergegroupbydrop duplicates
- Resuelto
- Sin pistas
- Al primer intento
