Au-delà des bases de pandas, un Data Engineer Senior maîtrise la vectorisation, la gestion mémoire, et sait quand remplacer pandas par DuckDB ou Polars pour des performances 10x à 100x supérieures.
Un Data Engineer qui utilise des boucles iterrows() sur des DataFrames pandas en production livre de la dette technique. Les pièges de performance, la vectorisation et la maîtrise de DuckDB ou Polars sont les vrais discriminants en entretien.
Quelle est la différence entre .loc, .iloc et .at ? Pourquoi faut-il absolument éviter iterrows() sur un grand DataFrame ?
# MAUVAIS : boucle Python sur un DataFrame (tres lent)
for index, row in df.iterrows():
df.at[index, 'profit'] = row['revenue'] - row['cost']
# BON : vectorisation directe
df['profit'] = df['revenue'] - df['cost']
# BON : np.where pour les conditions simples
import numpy as np
df['tier'] = np.where(df['amount'] > 1000, 'gold',
np.where(df['amount'] > 100, 'silver', 'bronze'))
# Selection par label vs position
df.loc[df['status'] == 'active', 'revenue'] # par label/condition
df.iloc[0:10, 2:5] # par position entiere
df.at[42, 'revenue'] # acces scalaire rapide par labelSignal GO Senior : le candidat cite spontanement iterrows() comme anti-pattern, explique la vectorisation et connait np.where.
Comment reduisez-vous la consommation memoire d'un DataFrame pandas de 3 Go ?
import pandas as pd
# Diagnostic memoire
print(df.memory_usage(deep=True).sum() / 1024**2, "MB")
# Reduction des types numeriques
df['age'] = df['age'].astype('int8') # int64 -> int8 : 8x moins
df['score'] = df['score'].astype('float32') # float64 -> float32 : 2x moins
df['status'] = df['status'].astype('category') # string -> category : -80%
df['pays'] = df['pays'].astype('category')
# Types specifies a l'import (optimal)
df = pd.read_csv("data.csv", dtype={
'age': 'int8',
'status': 'category',
'amount': 'float32'
})
# Lecture par chunks pour les tres gros fichiers
for chunk in pd.read_csv("big_file.csv", chunksize=100_000):
process(chunk)Expliquez pourquoi une operation vectorisee NumPy est 500x plus rapide qu'une boucle Python equivalente.
import numpy as np
# Boucle Python : ~5 secondes sur 1M de lignes
distances = []
for i in range(len(df)):
d = np.sqrt(df['x'].iloc[i]**2 + df['y'].iloc[i]**2)
distances.append(d)
# Vectorisation NumPy : ~10 millisecondes (500x plus rapide)
distances = np.sqrt(df['x']**2 + df['y']**2)
# pd.cut pour les decoupages en tranches
df['age_group'] = pd.cut(df['age'],
bins=[0, 18, 35, 50, 100],
labels=['junior', 'adulte', 'senior', 'aine']
)Dans quel cas utilisez-vous DuckDB plutot que pandas ? Quelle est sa principale force sur les fichiers Parquet ?
import duckdb
# DuckDB lit directement les fichiers Parquet sans tout charger en RAM
result = duckdb.sql("""
SELECT
country,
SUM(revenue) AS total_revenue,
COUNT(*) AS nb_orders
FROM 'data/orders_*.parquet'
WHERE order_date >= '2024-01-01'
GROUP BY country
ORDER BY total_revenue DESC
LIMIT 20
""").df() # convertit en DataFrame pandas si necessaire
# DuckDB peut aussi requeter directement un DataFrame pandas existant
conn = duckdb.connect()
result = conn.execute("""
SELECT customer_id, SUM(amount) AS total
FROM df_orders
GROUP BY customer_id
HAVING total > 1000
""").df()Alternative zero-installation : DuckDB fonctionne en process sans serveur. pip install duckdb et c'est pret. Idéal pour les scripts ETL locaux sur des fichiers volumineux.
Quelle est la difference entre l'API eager et lazy de Polars ? Quand migrer de pandas vers Polars ?
import polars as pl
# API Eager : execution immediate (comme pandas)
df = pl.read_csv("data.csv")
result = (
df
.filter(pl.col("amount") > 100)
.group_by("category")
.agg(pl.col("amount").sum().alias("total"))
)
# API Lazy : execution differee et optimisee (comme Spark)
result = (
pl.scan_csv("data.csv") # scan = lecture lazy
.filter(pl.col("amount") > 100)
.group_by("category")
.agg(pl.col("amount").sum().alias("total"))
.collect() # declenche l'execution optimisee
)
# Polars optimise automatiquement : predicate pushdown,
# projection pushdown, parallelisme multi-thread natifComment identifiez-vous le goulot d'etranglement dans un script Python data lent ?
import cProfile
import timeit
# cProfile : profiling par fonction
cProfile.run('process_dataframe(df)', sort='cumulative')
# timeit : comparer deux approches
t1 = timeit.timeit(
'df.groupby("category").agg({"amount": "sum"})',
globals=globals(), number=100
)
# memory_profiler : profiling memoire ligne par ligne
from memory_profiler import profile
@profile
def process_data(df):
result = df.groupby('category').agg({'amount': 'sum'})
return result
# YData Profiling : rapport qualite automatique
from ydata_profiling import ProfileReport
report = ProfileReport(df, title="Data Quality Report")
report.to_file("report.html")| Niveau | Maitrise attendue | Signal GO | NO-GO |
|---|---|---|---|
| Junior | pandas basique, vectorisation, groupby/merge/pivot | Evite iterrows(), utilise .loc correctement, sait faire un merge multi-cles | Utilise des boucles for sur les DataFrames, ne sait pas ce qu'est la vectorisation |
| Confirme | Optimisation memoire, DuckDB, profiling, types Category | Reduit la memoire via les dtypes, a utilise DuckDB sur un projet, sait utiliser cProfile | Ne connait pas DuckDB, ne sait pas reduire la consommation memoire |
| Senior | Polars, streaming gros volumes, benchmarks, tests de perf | A utilise Polars sur un projet reel, traite des fichiers plus grands que la RAM avec DuckDB ou chunks | N'a jamais entendu parler de Polars, ne sait pas traiter un fichier plus grand que la RAM |
| Lead | Architecture data locale vs distribuee, choix Spark vs DuckDB vs Polars selon le contexte | Justifie le choix DuckDB vs Spark selon le volume et le contexte, a defini les standards Python de son equipe | Ne peut pas expliquer quand utiliser Spark plutot que DuckDB |
Premier entretien gratuit. Rapport GO/NO-GO sous 48h.