La solicitud

De Logan Brooks, Especialista en Email Marketing
Tu objetivo
Asigna a result un DataFrame con los clientes que no tienen filas en orders: columnas customer_id, first_name, last_name, email, ordenado por customer_id ascendente, con el índice reiniciado.
- Usa el DataFrame
customerspendiente - Usa el DataFrame
orderspendiente - Muestra exactamente
customer_idfirst_namelast_nameemailpendiente
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 Logan, y por qué
DataFrames
Cargados como DataFrames de pandas, junto con pd y np.
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 |
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 |
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 |
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 |
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
Los left joins conservan todo lo de la izquierda
Un join inner descarta las filas sin pareja. Cuando las filas sin pareja son justo lo que buscas, eso es exactamente lo contrario de lo que quieres. Un left join conserva todas las filas de la tabla izquierda y rellena con NaN las columnas de la tabla derecha donde no hubo coincidencia:
customers.merge(orders, on="customer_id", how="left")
Los clientes que nunca compraron aparecen una vez, con NaN en todas las columnas de pedido. Los clientes con cinco pedidos aparecen cinco veces. Ese segundo dato importa: un left join puede alargar la tabla, así que no sirve para “añadir información” a los clientes sin cambiar su número.
indicator: ¿de dónde vino cada fila?
indicator=True añade una columna _merge con uno de tres valores:
"both": la fila tuvo pareja."left_only": solo está en la tabla izquierda."right_only": solo está en la derecha (posible conhow="outer").
ordered = orders[["customer_id"]].drop_duplicates()
merged = customers.merge(ordered, on="customer_id", how="left", indicator=True)
merged["_merge"].value_counts()
never = merged[merged["_merge"] == "left_only"]
orders[["customer_id"]] es una tabla de una columna con una fila por pedido, así que un cliente con
cinco pedidos aparece cinco veces. .drop_duplicates() elimina las filas repetidas y conserva la
primera de cada una, dejando cada ID de cliente una sola vez:
orders[["customer_id"]].drop_duplicates() # 1,771 rows: each customer who ordered, once
Quitar duplicados así antes del merge mantiene la tabla unida en exactamente una fila por cliente,
así que no hay fan-out en absoluto. (Verás drop_duplicates con más detalle en el capítulo de
limpieza.)
indicator es también la mejor herramienta para auditar un merge en el que no esperabas perder
filas: ejecútalo con how="outer" y mira qué es left_only y right_only.
El atajo con isin
Cuando solo necesitas las filas de la tabla izquierda, un anti-join con isin es más corto y no
crea una tabla intermedia grande:
customers[~customers["customer_id"].isin(orders["customer_id"])]
Léelo como “clientes cuyo ID no está en los IDs de cliente de los pedidos”. Ambos enfoques dan las
mismas filas. Usa isin para un anti-join rápido y el merge con indicator cuando también quieras
inspeccionar las filas con pareja.
Comprobar NaN tras un left join
Otra forma habitual de encontrar filas sin pareja es comprobar si falta valor en una columna de la
derecha. .isna() devuelve True donde falta un valor (NaN, la marca de pandas para “sin valor”):
merged = customers.merge(orders, on="customer_id", how="left")
merged[merged["order_id"].isna()]
Esto funciona cuando la columna que compruebas nunca falta en las filas con pareja. order_id
cumple, pero una columna como coupon_code, que a menudo está vacía en pedidos reales, marcaría por
error a muchos compradores. Comprueba una columna clave, o usa indicator.
¿Quiénes son estos clientes?
Algunos son registros recientes que simplemente aún no han comprado, y otros son los registros de cliente duplicados que conocerás en el capítulo de limpieza: la misma persona se registró dos veces y hizo pedidos solo bajo el primer ID. Antes de que Logan envíe un email a todos los de esta lista, esos duplicados deberían eliminarse, o un cliente fiel recibirá un email de “aún no has hecho ningún pedido”.
Errores comunes
- Usar un join inner y preguntarse por qué el resultado está vacío.
- Comprobar NaN en una columna que tiene huecos propios para detectar “sin pareja”.
- Olvidar que un left join con una tabla “de muchos” repite filas de la izquierda.
¡Linterna encendida!
Practicaste: left joinindicatoranti joinisin
- Resuelto
- Sin pistas
- Al primer intento
