Missing data handling
এই পাঠে যা শিখবেন
- Missing data কেন ঘটে — তিন উৎস
- NaN, None, pd.NA — পার্থক্য ও mixed dtype
- Detection —
isna(),isna().sum(), percent missing dropna— axis, how, thresh, subsetfillna— scalar, dict, ffill/bfill- Imputation — mean, median, mode, KNN, sklearn pipeline
- MCAR, MAR, MNAR — তিন ধরনের missingness
১ · Missing data কেন ঘটে?
বাস্তব ডেটা কখনই perfect নয়। একটি Daraz order table-এ delivery_rating অনেক জায়গায় ফাঁকা — কারণ গ্রাহক rating দেননি। একটি IoT temperature sensor কখনো reading miss করে — বিদ্যুৎ গেলে। দুটি table merge করার পর — বাঁ table-এ key থাকলেও ডান table-এ না থাকলে — সমস্ত column NaN।
১) System fail: sensor crash, network drop, file truncate।
২) User skip: optional form field, refusal, "prefer not to say"।
৩) Join mismatch: left join-এ ডান side-এ row নেই → NaN।
২ · NaN, None, pd.NA — তিন রূপ
Pandas-এ "missing" তিনটি ভিন্ন object দিয়ে represent হয় — যা প্রায়শই বিভ্রান্তি তৈরি করে।
-
NaNNaN — Not a NumberIEEE 754 floating-point standard-এর special value। numpy.nan। পার্থক্য: NaN == NaN → False (অনন্য property)। Float column-এ default missing marker। — IEEE 754 float। Numeric column-এ default। অদ্ভুত:NaN == NaN→False। -
None— Python-এর native null object। Object (string) column-এ থাকে। Pandas convert করে NaN-এ যখন numeric context। -
pd.NA— Pandas 1.0+ এর nullable scalar। নতুন nullable dtypes (Int64,boolean,string)-এ ব্যবহৃত। Type-preserving — int column-এ NaN দিলে dtype float হয়ে যায়, কিন্তু pd.NA দিলে Int64 থাকে।
৩ · Detection — কোথায় missing?
Pipeline-এর প্রথম কাজ — কোথায় কত missing গণনা।
import pandas as pd
import numpy as np
# একটি sample DataFrame — কিছু missing value সহ
df = pd.DataFrame({
"name": ["রহিম", "করিম", "সালমা", None, "নাদিয়া"],
"age": [25, np.nan, 32, 28, np.nan],
"city": ["ঢাকা", "চট্টগ্রাম", None, "সিলেট", "খুলনা"],
"salary": [50000, 45000, np.nan, np.nan, 60000]
})
# কোথায় missing?
print(df.isna())
# প্রতিটি column-এ কত missing?
print("\nMissing count:")
print(df.isna().sum())
# শতকরা কত missing?
print("\nMissing %:")
print((df.isna().mean() * 100).round(2))
isna() → boolean DataFrame। .sum() column-wise গণনা। .mean() দিলে percent missing — কারণ True=1, False=0।
৪ · Visualization — missing pattern
Bar chart-এ প্রতিটি column-এর missing percent দেখলে দ্রুত বুঝতে পারবেন কোন column drop, কোনটা impute।
import matplotlib.pyplot as plt
# আগের df ব্যবহার করছি
missing_pct = df.isna().mean() * 100
ax = missing_pct.plot(kind="bar", color="#dc2626")
ax.set_ylabel("Missing %")
ax.set_title("প্রতি column-এ missing শতকরা")
plt.tight_layout()
plt.show()
# alternative: missingno library
# import missingno as msno
# msno.matrix(df) # heatmap-like view
৫ · dropna — সরিয়ে ফেলা
সবচেয়ে সরল strategy — যেখানে missing, সেই row বা column বাদ। কিন্তু সাবধান — আপনি ডেটা হারাচ্ছেন।
# যেকোনো row-এ একটিও NaN থাকলে drop
df.dropna()
# শুধু সেই row drop যেখানে সব column NaN
df.dropna(how="all")
# অন্তত ৩টি non-NaN value থাকতে হবে
df.dropna(thresh=3)
# নির্দিষ্ট column-এ NaN হলেই drop
df.dropna(subset=["age", "salary"])
# Column drop — যেকোনো column-এ NaN থাকলে
df.dropna(axis=1)
৬ · fillna — পূরণ করা
# সব NaN → 0
df.fillna(0)
# Column-wise dict — প্রতি column-এ আলাদা value
df.fillna({
"age": 30,
"salary": df["salary"].median(),
"city": "অজানা",
"name": "Unknown"
})
# Forward fill — আগের value দিয়ে পূরণ
df.fillna(method="ffill") # time series-এ চমৎকার
# Backward fill — পরের value দিয়ে
df.fillna(method="bfill")
# Limit — কতগুলো consecutive NaN পূরণ হবে
df.fillna(method="ffill", limit=2)
৭ · Imputation strategies — mean, median, mode
ImputationImputationMissing value-এর জায়গায় estimated value বসানো। সরল: mean/median/mode। জটিল: KNN, regression, MICE। লক্ষ্য: distribution যতটা সম্ভব preserve করা। মানে — missing value-র জায়গায় একটি অনুমান বসানো। কোন statistic ব্যবহার করবেন তা data-র ধরনের উপর নির্ভর।
# Numeric → mean (symmetric data) বা median (skewed data)
df["age"] = df["age"].fillna(df["age"].mean())
df["salary"] = df["salary"].fillna(df["salary"].median())
# Categorical → mode (সবচেয়ে common value)
df["city"] = df["city"].fillna(df["city"].mode()[0])
# Group-wise imputation — শ্রেণিভিত্তিক mean
df["salary"] = df.groupby("city")["salary"].transform(
lambda s: s.fillna(s.median())
)
# String column-এ "Unknown" placeholder
df["name"] = df["name"].fillna("Unknown")
KNNImputer। imputer বা MICE বেশি সঠিক।
৮ · sklearn SimpleImputer — production pipeline
Pandas-এ inline imputation prototype-এ ভাল, কিন্তু production-এ আপনাকে train ও test-এ একই rule apply করতে হবে। sklearn-এর SimpleImputer এই purpose-এ designed।
from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
import numpy as np
# Numeric imputer — train-এ fit, test-এ transform
num_imputer = SimpleImputer(strategy="median")
X_train_num = num_imputer.fit_transform(df[["age", "salary"]])
# Categorical imputer — most frequent
cat_imputer = SimpleImputer(strategy="most_frequent")
X_train_cat = cat_imputer.fit_transform(df[["city"]])
# Production pipeline — fit একবার, যেকোনো নতুন data-তে apply
num_pipeline = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler())
])
X_processed = num_pipeline.fit_transform(df[["age", "salary"]])
# KNN imputer — neighbors থেকে শেখে (বেশি সঠিক, বেশি ধীর)
knn = KNNImputer(n_neighbors=3)
X_knn = knn.fit_transform(df[["age", "salary"]])
print(X_knn)
fit_transform train-এ — তারপর সেই fitted object দিয়ে transform test-এ। এতে data leakage এড়ানো যায় — test-এর statistic train-এ "leak" করে না।
৯ · Missingness pattern — MCAR, MAR, MNAR
Statistician Donald Rubin (১৯৭৬) তিনটি মৌলিক pattern আলাদা করেছিলেন। কোন imputation strategy সঠিক — তা এই pattern-এর উপর নির্ভর।
- MCARMissing Completely At RandomMissingness-এর সাথে কোনো observed বা unobserved variable-এর সম্পর্ক নেই। সবচেয়ে "নিরাপদ" — drop বা mean impute তেমন bias আনে না। বাস্তবে দুর্লভ। (Missing Completely At Random): Missing-এর কারণ random — কোনো variable-এর সাথে সম্পর্ক নেই। যেমন: random sensor failure। নিরাপদে drop বা mean impute চলে।
- MAR (Missing At Random): Missing-এর কারণ অন্য observed variable-এর উপর নির্ভর — missing value নিজে নয়। যেমন: পুরুষরা salary disclose করতে কম রাজি। gender জানা থাকলে এটি model করা যায়।
- MNAR (Missing Not At Random): Missing-এর কারণ unobserved value-এর উপরই — সবচেয়ে কঠিন। যেমন: উচ্চ-আয়ের লোক salary disclose করতে কম রাজি। Naive impute serious bias আনে।
১০ · Risk — naive imputation-এর বিপদ
একটি সরল রিস্ক — সব missing-এ 0 বসিয়ে দিলে — মডেল "0 = ছোট value" শিখে, যেটা missing-এর সঠিক প্রতিনিধিত্ব নয়। সমাধান: একটি অতিরিক্ত boolean column "was_missing" — যা মডেলকে missing-এর pattern নিজে শিখতে দেয়।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ NaN, None, এবং pd.NA — তিনটি missing marker-এর internal পার্থক্য কী? কেন একই DataFrame-এ এরা মিশে থাকতে পারে? float NaN-এর IEEE 754 origin বুঝুন।
এটি প্রায়শই junior data scientist-দের বিভ্রান্ত করে — তিনটি object একই কাজ করে মনে হলেও — internal মেকানিজম আলাদা।
(১) numpy.nan — IEEE 754 float:
- NaN-এর জন্ম ১৯৮৫ সালের IEEE 754 floating-point standard-এ। যেকোনো undefined float operation (০/০, sqrt(-1)) → NaN।
- Type:
float64। অর্থাৎ — শুধু float column-এই থাকতে পারে। - অদ্ভুত property:
NaN == NaN→False। কারণ — IEEE define করেছে "NaN-এর সাথে কোনো comparison-ই True নয়"। তাই detection-এর জন্যnp.isnan()বাpd.isna()ব্যবহার করতে হয়। - একটি integer column-এ NaN ঢোকালে — pandas সম্পূর্ণ column float-এ convert করে। কারণ int-এ NaN representation নেই।
(২) None — Python's null:
- Python-এর singleton object। Type:
NoneType। - Object dtype column-এ থাকে — সাধারণত string column-এ।
None == None→True(NaN-এর বিপরীত)।- Numeric context-এ pandas auto-convert করে NaN-এ।
(৩) pd.NA — pandas 1.0+ nullable scalar:
- 2020-এ introduced। লক্ষ্য: type-preserving missing।
- Nullable dtypes-এ ব্যবহৃত:
Int64,Float64,boolean,string। pd.NA == pd.NA→pd.NA(propagating, NaN-এর মতই, কিন্তু নিজস্ব semantics)।- Boolean operation-এ "three-valued logic" — True / False / NA।
কেন এক DataFrame-এ মিশে থাকে:
- CSV read-এ — empty cell numeric column-এ NaN, string column-এ None হিসেবে আসে।
- Mixed-type column (object dtype) — উভয়ই থাকতে পারে।
- Legacy code NaN ব্যবহার করে, modern code pd.NA। Pipeline-এ মিশ্রণ স্বাভাবিক।
Best practice: Detection-এ সবসময় pd.isna() বা .isna() ব্যবহার করুন — এটি তিনটিকেই uniformly ধরে। == np.nan বা is None avoid করুন।
মূল উপলব্ধি: "Missing" একটি concept — কিন্তু Python-এ implementation একাধিক, কারণ Pandas-এর evolution numpy float-এর সীমাবদ্ধতা থেকে শুরু করে আজকের nullable type-এ পৌঁছেছে। এই history না জানলে subtle bug এসে যায়।
প্র ০২ MCAR, MAR, MNAR — তিন ধরনের missingness pattern। প্রতিটিতে imputation strategy কেন আলাদা? প্রত্যেকটির real-world example দিন।
১৯৭৬-এ statistician Donald Rubin এই taxonomy দেন। আজও missing data theory-র ভিত্তি। কোন strategy "correct" — তা pattern-এর উপর directly নির্ভর।
(১) MCAR — Missing Completely At Random:
- সংজ্ঞা: Missing-এর probability কোনো observed বা unobserved variable-এর উপর নির্ভর করে না। সম্পূর্ণ random।
- Real example: ল্যাব-এ একটি pH meter randomly fail করে — কোনো sample-এর বৈশিষ্ট্যের সাথে সম্পর্ক নেই।
- Strategy: Listwise deletion (dropna) safe — sample size কমে কিন্তু bias আসে না। Mean impute তুলনামূলক নিরাপদ।
- Test: Little's MCAR test দিয়ে statistically check করা যায়।
(২) MAR — Missing At Random:
- সংজ্ঞা: Missing-এর probability অন্য observed variable-এর উপর নির্ভর — কিন্তু missing value নিজে নয়।
- Real example: পুরুষরা mental-health survey-তে কম response দেন (gender একটি observed variable)। missing-এর কারণ gender, depression score নয়।
- Strategy: Conditional imputation — gender-অনুযায়ী মান। Multiple imputation (MICE) সঠিক estimate দেয়। Naive mean impute bias আনবে।
- আধুনিক ML-এ অধিকাংশ missingness MAR বলে ধরে নেওয়া হয়।
(৩) MNAR — Missing Not At Random:
- সংজ্ঞা: Missing-এর probability missing value নিজের উপর নির্ভর। Unobserved।
- Real example: উচ্চ-আয়ের ব্যক্তি income disclose করতে অনিচ্ছুক। বা — যাদের HIV positive, তারা test result শেয়ার করেন না। missing-এর কারণই value-টি।
- Strategy: সবচেয়ে কঠিন। Standard imputation systematically biased estimate দেবে। Selection model বা pattern-mixture model দরকার — domain knowledge ছাড়া অসম্ভব।
- "Missingness নিজে একটি feature" — boolean indicator যোগ করা একটি pragmatic solution।
কেন strategy আলাদা:
- MCAR-এ data-র distribution missing/non-missing-এ identical। যেকোনো imputation নিরাপদ।
- MAR-এ subgroup-অনুযায়ী distribution আলাদা। Conditional imputation দরকার।
- MNAR-এ missing data fundamentally observed data থেকে আলাদা। Imputation মানেই assumption — যা ভুল হতে পারে।
মূল উপলব্ধি: "যেকোনো missing → mean impute" — এটি statistically illiterate behavior। Missingness pattern সম্পর্কে hypothesis ছাড়া কোনো imputation justify করা যায় না।
প্র ০৩ Mean impute সবচেয়ে সহজ ও জনপ্রিয়। কিন্তু কেন risky? Variance underestimation, distribution distortion, এবং downstream model bias — তিনটি ব্যাখ্যা করুন।
Mean impute — beginner tutorial-এর প্রিয় strategy। কিন্তু statistically এটি বহু সমস্যা তৈরি করে — যা production model-এ silent bias হয়ে দাঁড়ায়।
(১) Variance underestimation:
- Statistical formula: $\text{Var}(X) = \frac{1}{n} \sum (x_i - \bar{x})^2$।
- সব missing-এ $\bar{x}$ বসালে — সেই terms-এর contribution exactly 0।
- ফলে computed variance original-এর চেয়ে কম। যত % missing — তত বড় underestimation।
- Standard error, confidence interval — সবই artificially narrow হয়ে যায়। হাইপোথিসিস টেস্ট-এ false positive বাড়ে।
(২) Distribution distortion:
- Original data normal distribution-এ। Mean impute-এর পর — distribution-এ একটি spike তৈরি হয় mean-এর জায়গায়।
- Bimodal বা skewed distribution-এ এই spike ভয়ংকর misleading।
- Histogram, density estimation — সবই বিকৃত। Visual inspection-এ "অস্বাভাবিক" দেখাবে।
- Categorical mode-impute-এ similar issue: minority class-এর representation হারিয়ে যায়।
(৩) Downstream model bias:
- Linear regression: coefficient estimate biased। Variable-এর "true effect" underestimated।
- Tree models: imputed value-এ artificial split তৈরি হয়। Decision tree "mean exactly" এ split করার tendency develop করে।
- Distance-based (k-NN, clustering): imputed point আসল pattern থেকে দূরে — neighborhood বিকৃত।
- Feature importance: imputed feature artificially "stable" দেখায় — true importance underestimated।
কখন mean impute তবু গ্রহণযোগ্য:
- Missing < 5% এবং MCAR — impact minimal।
- Quick prototype বা baseline model।
- Downstream model robust (যেমন regularized regression)।
উন্নত alternatives:
- Median impute: skewed data-এ better। Outlier-এর প্রভাব কম।
- Stochastic imputation: mean নয়, একটি sample distribution থেকে — variance preserve।
- Multiple imputation (MICE): একই dataset-এ একাধিক imputation, multiple model train, results pool — uncertainty সংরক্ষণ।
- Model-based (KNN, regression): অন্য features ব্যবহার করে predict।
মূল উপলব্ধি: Mean impute = "convenient lie"। Quick এবং safe-looking, কিন্তু statistical foundation দুর্বল। Production-এ — অন্তত median, ideally MICE বা KNN imputer ব্যবহার করুন।
প্র ০৪ আধুনিক approach — KNN imputer, MICE (multiple imputation by chained equations), missingness-as-feature। তিনটির trade-off ও কখন কোনটা ব্যবহার করবেন?
সাধারণ mean/median impute beyond — তিনটি প্রধান modern technique। প্রত্যেকটির নিজস্ব assumption ও cost আছে।
(১) KNN Imputer (sklearn):
- আইডিয়া: প্রতিটি missing row-এর জন্য — K nearest neighbors খুঁজে — তাদের value-র average বসানো।
- Distance metric: Euclidean (default), অন্য features-এর মান অনুযায়ী।
- সুবিধা: Local pattern capture। Variance তুলনামূলকভাবে preserve। Implementation সরল (
KNNImputer(n_neighbors=5))। - অসুবিধা: Curse of dimensionality — high-D-এ neighbors meaningless। Computation $O(n^2)$। বড় dataset-এ slow।
- কখন: মাঝারি dataset (< ১ লাখ row), < ৫০ features, mixed numeric data।
(২) MICE — Multiple Imputation by Chained Equations:
- আইডিয়া: প্রতিটি missing variable-কে অন্য variables-এর regression model দিয়ে predict। Iteratively সব variable পূরণ। M বার (যেমন ১০ বার) imputation — M sets of complete data।
- Statistical foundation: Rubin (১৯৮৭)। MAR-এ unbiased estimate দেয়।
- সুবিধা: Uncertainty quantification — প্রতিটি imputation আলাদা, downstream model M বার train করে result pool — confidence interval ঠিকভাবে আসে। Variance underestimation নেই।
- অসুবিধা: Computationally expensive। Implementation জটিল (sklearn-এ
IterativeImputer, R-এmicepackage)। Convergence guarantee সবসময় নেই। - কখন: Statistical inference critical (research, healthcare, social science)। MAR assumption justify করা যাচ্ছে।
(৩) Missingness-as-feature:
- আইডিয়া: Imputation করুন (যেকোনো method-এ) + একটি অতিরিক্ত boolean column "
was_missing_X"। মডেল নিজে শিখবে — missingness নিজেই signal কিনা। - সুবিধা: MNAR-এ চমৎকার — যেখানে missing pattern নিজে predictive। Tree-based model (XGBoost, RandomForest) এই pattern সহজে exploit করে।
- অসুবিধা: Feature dimensionality দ্বিগুণ। Linear model-এ collinearity issue হতে পারে।
- কখন: Production tabular ML (Kaggle, business analytics)। Missing-এর কারণ informative হতে পারে এমন domain — credit scoring, medical, churn prediction।
Decision matrix:
- Quick prototype: SimpleImputer (median)।
- Tabular ML production: SimpleImputer + missingness flag (XGBoost natively handles)।
- Statistical research: MICE।
- Image/sensor time series: ffill / interpolation।
- Mid-size mixed data: KNNImputer।
আধুনিক LLM-যুগে: Tabular foundation models (TabPFN, ২০২২+) missingness internally handle করতে পারে — pretraining-এ শেখা। ভবিষ্যতে hand-crafted imputation কম দরকার হবে। কিন্তু interpretability ও audit-এর জন্য classical approach এখনও essential।
মূল উপলব্ধি: "Best imputation" বলে কিছু নেই — context, dataset size, downstream model, এবং statistical assumption সব মিলিয়ে decision। Missing data handling-এ অভিজ্ঞতা — production data scientist-এর সবচেয়ে valuable skill-গুলোর একটি।
অনুশীলন
-
Detect missing: নিচের DataFrame-এ প্রতিটি column-এ কত শতাংশ missing — গণনা করুন।
df = pd.DataFrame({ "a": [1, np.nan, 3, np.nan, 5], "b": [np.nan, np.nan, 3, 4, 5], "c": [1, 2, 3, 4, 5] })print((df.isna().mean() * 100).round(2)) # a 40.0 # b 40.0 # c 0.0Column
c-তে কোনো missing নেই।aওb-তে ৪০% করে missing। -
Fill with median per group: একটি
citycolumn ওsalarycolumn আছে। প্রতি city-র median salary দিয়ে missing salary পূরণ করুন।df["salary"] = df.groupby("city")["salary"].transform( lambda s: s.fillna(s.median()) )transformgroup size preserve করে — অর্থাৎ result-এর shape original df-এর সমান। প্রতিটি group-এর missing শুধু সেই group-এর median দিয়েই পূরণ। -
Drop rows with > 50% missing: যেসব row-এ অর্ধেকের বেশি column missing — drop করুন।
# thresh = কতগুলো non-NaN value থাকতে হবে min_non_na = df.shape[1] // 2 + 1 # > 50% df_clean = df.dropna(thresh=min_non_na) # বিকল্প — direct ratio দিয়ে mask = df.isna().mean(axis=1) <= 0.5 df_clean = df[mask]threshparameter "অন্তত কতগুলো non-NaN value চাই" specify করে। আমরা যদি >৫০% non-NaN চাই — column count-এর অর্ধেকের বেশি।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পাঠ ১৭ · Matplotlib — প্রথম গ্রাফ পরবর্তী পাঠ Pandas-এ data clean হলে — visualization-এ চলে যান। Matplotlib দিয়ে প্রথম chart।
- পাঠ ১৫ · groupby, merge ও pivot আগের পাঠ Group-wise imputation শিখতে — groupby revisit করুন।
- পাঠ ০৮ · Error handling এই পাঠের সাথে সম্পর্কিত Missing data pipeline-এ exception handling — robust ETL-এর ভিত্তি।
- সব AI Courses দেখুন ABCL TECH Python, ML, DL, NLP, CV, GenAI, RL, MLOps — সব AI কোর্স একসাথে।