Pandas Part 2: groupby, merge, missing values, duplicates

সম্পূর্ণ data wrangling workflow (Series 03, Episode 04)

🟡 INTERMEDIATE Series 03 — Data — AI-এর ভিত্তি Episode 04 / 09

📑 এই পর্বে যা যা আছে

🧩 ১. গল্প: Rahim-এর "গড় salary" প্রশ্ন

Rahim এখন Pandas দিয়ে filter করতে পারে। কিন্তু Boss একটা প্রশ্ন করলেন যা filter দিয়ে হচ্ছে না — "প্রতিটা শহরে গড় salary কত, আর প্রতিটা role-এ কতজন কাজ করে?"

Rahim: "আপা, আমি তো Dhaka-র job আলাদা করে গড় বের করতে পারি, তারপর Chattogram, তারপর Sylhet... কিন্তু ৩০টা শহর হলে তো ৩০ বার লিখতে হবে!"

Nila: "একদম না। এই 'প্রতিটা group-এর জন্য একই হিসাব' — এটাই data analysis-এর সবচেয়ে দরকারি জিনিস, আর Pandas এটাকে এক লাইনে করে দেয়: groupby। আর real data নিয়ে কাজ করতে গেলে তোমার আরও দুটো জিনিস লাগবেই — missing value আর duplicate সামলানো। আজ সবই শিখবে।"

২. সমস্যা: filter দিয়ে সব প্রশ্নের উত্তর হয় না

Part 1-এ আমরা শিখেছি single condition-এ row বাছাই। কিন্তু বাস্তবে প্রশ্নগুলো আরও জটিল:

এই episode এই চারটা core wrangling skill শেখায় — যেগুলো ছাড়া কোনো real project এগোয় না।

🪜 ৩. তিন Level-এ groupby বোঝা

Level 1 — সহজ intuition

ভাবুন একগাদা job post-এর কাগজ টেবিলে ছড়ানো। আপনি সেগুলো শহর অনুযায়ী আলাদা স্তূপে ভাগ করলেন (Dhaka-র স্তূপ, Chattogram-এর স্তূপ)। তারপর প্রতিটা স্তূপের গড় salary হিসাব করলেন। groupby ঠিক এই কাজটাই করে — ভাগ করো, হিসাব করো, ফল একসাথে সাজাও।

Level 2 — technical ভাবে (split-apply-combine)

Pandas groupby-এর তিনটা ধাপ: split (একটা column-এর value অনুযায়ী data ভাগ), apply (প্রতিটা group-এ একটা function চালানো, যেমন mean/count), combine (ফলগুলো একটা নতুন DataFrame-এ জোড়া)। এটাই "split-apply-combine" pattern।

Level 3 — engineer দৃষ্টিতে

engineer রা groupby-কে aggregation ও feature engineering-এর মূল হাতিয়ার হিসেবে দেখে। যেমন "প্রতিটা customer-এর গড় transaction" একটা শক্তিশালী feature হতে পারে ML model-এর জন্য (Series 05-এ দেখব)।

🔀 ৪. groupby: split → apply → combine

import pandas as pd df = pd.DataFrame({ "title": ["Python Dev", "ML Engineer", "Data Analyst", "Backend Dev", "ML Engineer", "Python Dev"], "salary": [40000, 90000, 45000, 70000, 110000, 50000], "city": ["Dhaka", "Dhaka", "Chattogram", "Sylhet", "Dhaka", "Chattogram"], }) # প্রতিটা শহরে গড় salary print(df.groupby("city")["salary"].mean()) # city # Chattogram 47500.0 # Dhaka 80000.0 # Sylhet 70000.0 # প্রতিটা role-এ কতজন print(df.groupby("title").size()) # প্রতিটা শহরে সর্বোচ্চ salary print(df.groupby("city")["salary"].max())
groupby("city")["salary"].mean() যেভাবে কাজ করে: SPLIT (শহর অনুযায়ী) APPLY (mean) COMBINE Dhaka: 40000,90000,110000 -> 80000 ┐ Chatto: 45000,50000 -> 47500 ├─> একটা ফলাফল table Sylhet: 70000 -> 70000 ┘

📊 ৫. একাধিক aggregation একসাথে

# প্রতিটা শহরে একসাথে গড়, সর্বোচ্চ, গণনা result = df.groupby("city")["salary"].agg(["mean", "max", "count"]) print(result) # ভিন্ন column-এ ভিন্ন হিসাব df2 = df.assign(experience=[1, 3, 2, 4, 5, 2]) summary = df2.groupby("city").agg( avg_salary=("salary", "mean"), max_salary=("salary", "max"), avg_exp=("experience", "mean"), job_count=("title", "count"), ) print(summary)
উপরের agg(avg_salary=("salary", "mean"), ...) ধরনটা (named aggregation) পড়তে সবচেয়ে সহজ ও modern। ফলে column-এর নাম পরিষ্কার থাকে — report বানানোর জন্য আদর্শ।

🕳️ ৬. Missing value: চেনা ও সামলানো

Real data-তে missing value (NaN) থাকবেই। প্রথমে চেনা, তারপর সিদ্ধান্ত:

import numpy as np import pandas as pd df = pd.DataFrame({ "title": ["Python Dev", "ML Engineer", "Data Analyst", "Backend Dev"], "salary": [40000, np.nan, 45000, np.nan], "city": ["Dhaka", "Dhaka", None, "Sylhet"], }) # কোথায় কত missing? print(df.isnull().sum()) # salary 2 # city 1 # উপায় ১ — missing row বাদ (কম missing হলে) df.dropna(subset=["salary"]) # উপায় ২ — একটা value দিয়ে পূরণ (fill) df["salary"] = df["salary"].fillna(df["salary"].median()) # median দিয়ে df["city"] = df["city"].fillna("Unknown") # category-তে
কোন উপায় কখন?
• খুব কম missing (কয়েক %) → dropna চলে।
• সংখ্যার column → সাধারণত median দিয়ে fill (mean outlier-এ প্রভাবিত হয়)।
• category column → "Unknown" বা সবচেয়ে common value।
সতর্কতা: কোন value দিয়ে fill করবেন তা ঠিক করতে হবে শুধু training data দেখে — নাহলে data leakage হয় (E08-এ বিস্তারিত)। এই একটা ভুল অনেক junior করে।

👯 ৭. Duplicate: চেনা ও সরানো

df = pd.DataFrame({ "title": ["Python Dev", "ML Engineer", "Python Dev", "ML Engineer"], "salary": [40000, 90000, 40000, 90000], "city": ["Dhaka", "Dhaka", "Dhaka", "Dhaka"], }) # কতগুলো পুরো-row duplicate? print(df.duplicated().sum()) # 2 # duplicate সরানো (প্রথমটা রেখে) df_clean = df.drop_duplicates() # শুধু নির্দিষ্ট column-এর ভিত্তিতে duplicate df.drop_duplicates(subset=["title", "city"], keep="first")
সব duplicate খারাপ নয়! দুটো ভিন্ন কোম্পানি একই title ও salary দিতে পারে — সেটা বৈধ। তাই অন্ধভাবে drop_duplicates() নয়; আগে ভাবুন "কোন column-গুলো মিললে এটা সত্যিই একই job?" (যেমন একটা unique job_id থাকলে সেটার ভিত্তিতে)।

🔗 ৮. Merge/Join: দুটো table জোড়া

প্রায়ই data একাধিক table-এ ছড়ানো থাকে। যেমন এক table-এ job, আরেকটায় company তথ্য — একটা common key দিয়ে জোড়া লাগাতে হয়:

jobs = pd.DataFrame({ "job_id": [1, 2, 3], "title": ["Python Dev", "ML Engineer", "Data Analyst"], "company_id": [10, 10, 20], }) companies = pd.DataFrame({ "company_id": [10, 20], "company": ["bKash", "Daraz"], "city": ["Dhaka", "Dhaka"], }) # common key "company_id" দিয়ে জোড়া merged = jobs.merge(companies, on="company_id", how="left") print(merged[["title", "company", "city"]])
how=মানেকখন
innerদুই table-এ যেগুলো মেলে শুধু সেগুলোশুধু মেলানো data চাই
leftবাঁ table-এর সব, ডানেরটা যেখানে মেলেমূল table পুরো রাখতে চাই (সবচেয়ে common)
rightডান table-এর সবউল্টো ক্ষেত্রে
outerদুই table-এর সব (না মিললে NaN)কিছুই বাদ দিতে চাই না
এই merge-এর how ধারণাটা SQL-এর JOIN-এর সাথে হুবহু মেলে — E06 (SQL)-এ আবার দেখব। একই ধারণা, দুই জায়গায়।

🔧 ৯. Transformation ও apply

# string পরিষ্কার — casing ঠিক করা (Part 1-এর messy data মনে আছে?) df["city"] = df["city"].str.strip().str.title() # " dhaka " -> "Dhaka" # comma সরিয়ে salary-কে সংখ্যায় df["salary"] = (df["salary"].astype(str) .str.replace(",", "", regex=False) .astype(float)) # map — category-কে সংখ্যায় (সরল mapping) df["city_code"] = df["city"].map({"Dhaka": 1, "Chattogram": 2, "Sylhet": 3}) # apply — জটিল custom logic (সম্ভব হলে vectorized-কে অগ্রাধিকার) df["salary_band"] = df["salary"].apply( lambda s: "high" if s > 80000 else ("mid" if s > 45000 else "low") )

লক্ষ করুন .str accessor — এটা পুরো column-এর প্রতিটা string-এ একসাথে কাজ করে (vectorized)। Part 1-এ দেখা messy data ("Python"/"python", "40,000") এখন এসব দিয়েই পরিষ্কার করা যায়।

🇧🇩 ১০. বাস্তব উদাহরণ: সম্পূর্ণ wrangling flow

import numpy as np import pandas as pd # messy raw data (Part 1-এর সেই সমস্যাগুলো সহ) raw = pd.DataFrame({ "title": ["Python Dev", "python dev", "ML Engineer", "Data Analyst", "Data Analyst"], "salary": ["40000", "40,000", "", "4000000", "45000"], "city": ["Dhaka", "dhaka", "Chattogram", "Dhaka", "Dhaka"], }) # 1) salary পরিষ্কার -> সংখ্যা raw["salary"] = (raw["salary"].str.replace(",", "", regex=False) .replace("", np.nan).astype(float)) # 2) city consistent করা raw["city"] = raw["city"].str.strip().str.title() # 3) title consistent (ছোট হাতের অক্ষরে তুলনার জন্য) raw["title"] = raw["title"].str.strip().str.title() # 4) outlier ধরা — অস্বাভাবিক salary NaN করি (40 লাখ ভুল) raw.loc[raw["salary"] > 1_000_000, "salary"] = np.nan # 5) missing salary -> median দিয়ে fill raw["salary"] = raw["salary"].fillna(raw["salary"].median()) # 6) duplicate সরানো raw = raw.drop_duplicates() # এখন প্রশ্নের উত্তর নির্ভরযোগ্য: print(raw.groupby("city")["salary"].mean())

দেখুন — Part 1-এর সেই "garbage" data এখন পরিষ্কার। এই পুরো flow-টাই একজন junior AI Engineer-এর দৈনন্দিন কাজের নমুনা।

🔍 ১১. একজন AI Engineer-এর দৃষ্টিতে

💼 ১২. Boss Question

Boss: "এই groupby-merge দিয়ে আমার business-এ আসলে কী লাভ?"

উত্তর: এগুলোই decision-এর ভিত্তি। groupby দিয়ে আমরা বলতে পারি "কোন শহরে/role-এ salary বেশি" — যা hiring ও budget সিদ্ধান্তে লাগে। merge দিয়ে বিচ্ছিন্ন data (job + company + location) একসাথে এনে সম্পূর্ণ ছবি পাই। আর missing/duplicate পরিষ্কার না করলে সব রিপোর্ট ভুল হবে — ভুল রিপোর্ট মানে ভুল সিদ্ধান্ত, যার খরচ অনেক বেশি। মানে: নির্ভরযোগ্য insight = ভালো সিদ্ধান্ত = business value।

🔎 ১৩. Job Requirement Decoder

"Experience with data cleaning, aggregation, and joining datasets (Pandas)."
প্রশ্নউত্তর
এর মানে কী?আপনি messy data পরিষ্কার করতে, groupby দিয়ে summarize করতে ও একাধিক table জোড়া দিতে পারেন।
কোম্পানি কেন চায়?real data সবসময় messy ও বিভিন্ন উৎসে ছড়ানো; এগুলো একত্র ও পরিষ্কার করা মূল কাজ।
কোন সমস্যা সমাধান করে?অসম্পূর্ণ/ভুল/বিচ্ছিন্ন data থেকে ভুল সিদ্ধান্ত এড়ায়।
Junior-এর কী জানা লাগে?groupby+agg, isnull/fillna/dropna, drop_duplicates, merge (how types), str cleaning।
এখনই কী master লাগে না?জটিল MultiIndex, বিশাল merge-এর performance tuning, big-data join — পরে।
GitHub-এ কীভাবে দেখাবেন?"raw → cleaned" notebook যেখানে প্রতিটা cleaning সিদ্ধান্ত ব্যাখ্যা করা।
Interview-তে কী জিজ্ঞেস করতে পারে?"missing value কীভাবে handle করো?", "inner vs left join?", "split-apply-combine কী?"

⚠️ ১৪. সাধারণ ভুল ধারণা

ভুল ১: "সব missing value drop করে দাও।" → ঠিক: অনেক data হারালে model দুর্বল হয়; পরিস্থিতি বুঝে fill নাকি drop ঠিক করুন।

ভুল ২: missing সংখ্যা mean দিয়ে fill করা (সবসময়)। → ঠিক: outlier থাকলে mean বিভ্রান্ত করে; median প্রায়ই নিরাপদ।

ভুল ৩: পুরো data (train+test) দেখে fill/scale করা। → ঠিক: এটা data leakage; মান শুধু training থেকে নিন (E08)।

ভুল ৪: merge-এর পর row বেড়ে গেলেও খেয়াল না করা। → ঠিক: many-to-many merge অজান্তে row বাড়ায়; merge-এর পর shape যাচাই করুন।

ভুল ৫: অন্ধভাবে drop_duplicates()। → ঠিক: আগে ঠিক করুন কোন column মিললে সেটা "সত্যিই" duplicate।

🎤 ১৫. Interview Prep

প্রশ্ন ১: groupby-এর split-apply-combine ব্যাখ্যা করো।
উত্তর: data একটা key-তে ভাগ (split), প্রতিটা group-এ function চালানো (apply), ফল একসাথে জোড়া (combine)।

প্রশ্ন ২: missing value কীভাবে handle করো?
উত্তর: আগে isnull().sum() দিয়ে পরিমাণ দেখি; কম হলে drop, নাহলে সংখ্যায় median/ category-তে mode বা "Unknown" দিয়ে fill — মান শুধু training থেকে (leakage এড়াতে)।

প্রশ্ন ৩: inner আর left join-এর পার্থক্য?
উত্তর: inner শুধু মেলানো row রাখে; left বাঁ table-এর সব row রাখে, ডানে না মিললে NaN।

প্রশ্ন ৪: mean বনাম median দিয়ে fill — কোনটা কখন?
উত্তর: outlier/ skewed data-তে median নিরাপদ; symmetric distribution-এ mean-ও চলে।

✍️ ১৬. হাতে-কলমে (Mini Exercise)

🧪 হাতে-কলমে: section ১০-এর messy DataFrame নিয়ে:

১. cleaning-এর আগে ও পরে isnull().sum() print করে পার্থক্য দেখুন।
২. প্রতিটা title-এ গড় salary বের করুন (groupby)।
৩. একটা companies table বানিয়ে city-র ভিত্তিতে region column merge করুন।
৪. drop_duplicates() চালানোর আগে ও পরে shape তুলনা করুন।
৫. একটা salary_band column বানান (low/mid/high)।

প্রতিটা ধাপে নিজেকে জিজ্ঞেস করুন: "এই সিদ্ধান্তটা কেন নিলাম?"

🚀 ১৭. Project Connection

flagship V2 (Data Analyzer)-এ এই episode-ই আসল কাজের কেন্দ্র। আমরা raw bangladesh job data পরিষ্কার করব (skill নাম standardize, salary সংখ্যায়, duplicate সরানো), তারপর groupby দিয়ে "কোন skill-এ salary বেশি", "কোন শহরে কত job" — এমন প্রশ্নের উত্তর বের করব। প্রয়োজনে company/location data merge করব।

এই পরিষ্কার-করা data পরে Series 05-এ ML model-এর input হবে। তাই এখানে করা cleaning-এর মান পুরো project-এর মান নির্ধারণ করবে — মনে রাখুন GIGO (E01)।

📌 ১৮. সারসংক্ষেপ

এই Episode-এ আমরা শিখলাম:

groupby — split-apply-combine দিয়ে প্রতিটা group-এ aggregate (mean/count/max)
agg দিয়ে একসাথে একাধিক হিসাব (named aggregation পড়তে সহজ)
missing value: isnull → dropna বা fillna (median/mode/Unknown), leakage-সচেতনভাবে
duplicate: duplicated/drop_duplicates — তবে অন্ধভাবে নয়
merge: common key-তে join (inner/left/right/outer) — SQL JOIN-এর মতো
str/apply/map দিয়ে transformation ও পরিষ্কার

➡️ ১৯. পরবর্তী পর্বে কী শিখব

পরবর্তী Episode (S03E05): "Matplotlib দিয়ে data-কে চোখে দেখা: distribution, correlation, outlier।"

সংখ্যায় হিসাব তো করলাম, কিন্তু মানুষ সংখ্যার চেয়ে ছবিতে pattern ভালো ধরে। পরের পর্বে আমরা visualization শিখব — histogram দিয়ে distribution, scatter দিয়ে সম্পর্ক, box plot দিয়ে outlier — যাতে data-কে শুধু হিসাব নয়, "চোখে দেখে" বুঝতে পারি।
© 2025 Sheikh Thanbir Alam. All Rights Reserved. thanbirtamim.github.io
এই লেখা মূল লেখকের সম্পত্তি — লিখিত অনুমতি ছাড়া কপি করে অন্য কোনো ওয়েবসাইট, ব্লগ, বই বা প্ল্যাটফর্মে প্রকাশ/বিতরণ করা কঠোরভাবে নিষিদ্ধ। Content may not be copied or republished without permission.