Pandas DataFrame পরিচিতি
এই পাঠে যা শিখবেন
- Pandas কী — কেন NumPy-র উপর আরেকটি library
- Series ও DataFrame — দু'টি core data structure
- DataFrame তৈরির ৩ উপায় — dict, list of dicts, NumPy array
- Inspection methods — data দেখার আগের ধাপ
- Column selection — bracket ও attribute access
.locবনাম.iloc— label-based বনাম positional indexing- Boolean filter — শর্ত মতে row বাছাই
- Column add/drop, basic arithmetic — feature engineering
১ · Pandas কী — NumPy-র উপর tabular layer
PandasPandasPython-এর সবচেয়ে জনপ্রিয় data analysis library — Wes McKinney ২০০৮-এ AQR Capital-এ লিখেছিলেন। নাম এসেছে "panel data" থেকে। NumPy-র উপর তৈরি — labeled tabular structure যোগ করে। হলো Python-এর "Excel"। NumPy দ্রুত, কিন্তু সব data শুধু সংখ্যা নয় — থাকে নাম, তারিখ, category, missing value। Pandas এই বাস্তবতাকে আলিঙ্গন করে — labeled, heterogeneous, missing-aware tabular structure দিয়ে।
AI কাজের ৮০% সময় data নিয়ে — file পড়া, পরিষ্কার করা, feature engineer করা, train/test ভাগ করা। এই সব Pandas-এ। ML library (scikit-learn, PyTorch)-এ data পাঠানোর আগে — Pandas-এ গোছানো হয়।
১) Series — labeled 1D array (একটি column)।
২) DataFrame — labeled 2D table (rows × columns)। প্রতিটি column একটি Series।
২ · Series — labeled 1D array
SeriesSeriesPandas-এর 1D labeled array। NumPy array + index। index মানে label — শুধু 0,1,2,... না, যেকোনো hashable value (নাম, তারিখ, string)। হলো একটি column — মানে একটি labeled 1D array। প্রতিটি value-র সাথে একটি label থাকে — যাকে বলে indexIndexPandas-এ row-এর label। Default — 0,1,2,...; কিন্তু সেট করা যায় তারিখ, নাম, ID যেকোনো কিছু। index unique হতে হয় না, কিন্তু সাধারণত হয়।।
import pandas as pd
# একটি Series — দোকানের দৈনিক বিক্রি (BDT)
sales = pd.Series(
[12500, 18300, 9800, 22100, 15600],
index=["শনি", "রবি", "সোম", "মঙ্গল", "বুধ"],
name="daily_sales"
)
print(sales)
print()
print("সোমবারের বিক্রি:", sales["সোম"]) # label দিয়ে
print("প্রথম দিন:", sales.iloc[0]) # position দিয়ে
print("গড়:", sales.mean())
print("সর্বোচ্চ দিন:", sales.idxmax())
৩ · DataFrame — labeled 2D table
DataFrameDataFramePandas-এর 2D labeled table। Rows-এ index, columns-এ column names। প্রতিটি column একটি Series — মানে heterogeneous: এক column int, পরেরটা string, পরেরটা datetime। হলো অনেক Series-এর সমাহার — সব একই index ভাগ করে। row × column structure।
- axis=0 = rows (নিচে চলা)
- axis=1 = columns (পাশে চলা)
- index = row labels
- columns = column labels
৪ · DataFrame তৈরি — তিন উপায়
import pandas as pd
import numpy as np
# উপায় ১ — dict of lists (সবচেয়ে common)
students = pd.DataFrame({
"নাম": ["রহিম", "করিম", "সাবিনা", "ফাতিমা", "আলী"],
"জেলা": ["ঢাকা", "চট্টগ্রাম", "সিলেট", "রাজশাহী", "ঢাকা"],
"বয়স": [22, 25, 21, 23, 24],
"score": [85, 72, 91, 68, 79]
})
print("=== Dict উপায় ===")
print(students)
# উপায় ২ — list of dicts (record-style, JSON থেকে)
records = [
{"নাম": "রহিম", "score": 85},
{"নাম": "করিম", "score": 72},
{"নাম": "সাবিনা", "score": 91},
]
df2 = pd.DataFrame(records)
print("\n=== List of dicts ===")
print(df2)
# উপায় ৩ — NumPy array + columns
arr = np.array([[12500, 45], [18300, 62], [9800, 31]])
df3 = pd.DataFrame(arr, columns=["sales", "customers"])
print("\n=== NumPy array ===")
print(df3)
৫ · Inspection — data দেখার ধাপ
কোনো নতুন DataFrame পাওয়ামাত্র — প্রথম কাজ "এতে কী আছে?" বোঝা। Pandas-এ কিছু standard method আছে।
import pandas as pd
import numpy as np
# একটি বড় DataFrame — দোকানের ১০০ দিনের বিক্রি
np.random.seed(42)
shop = pd.DataFrame({
"তারিখ": pd.date_range("2024-01-01", periods=100, freq="D"),
"বিক্রি": np.random.randint(8000, 25000, 100),
"গ্রাহক": np.random.randint(20, 80, 100),
"ছাড়_শতাংশ": np.random.choice([0, 5, 10, 15], 100),
})
print("=== shape ===", shop.shape) # (rows, cols)
print("\n=== dtypes ===")
print(shop.dtypes) # প্রতিটি column-এর type
print("\n=== head() — প্রথম ৫ row ===")
print(shop.head())
print("\n=== tail(3) — শেষ ৩ row ===")
print(shop.tail(3))
print("\n=== describe() — সংখ্যার summary ===")
print(shop.describe())
head()/tail() — sample দেখা। shape — মাপ। dtypes — প্রতিটি column-এর type (int64, float64, object/string, datetime64)। describe() — count, mean, std, min, 25%, 50%, 75%, max। info() — non-null count + memory usage।
isna().sum() — কত missing? এই ৫ check প্রথম মিনিটেই করুন।
৬ · Column selection — কয়েক উপায়
import pandas as pd
students = pd.DataFrame({
"নাম": ["রহিম", "করিম", "সাবিনা", "ফাতিমা", "আলী"],
"জেলা": ["ঢাকা", "চট্টগ্রাম", "সিলেট", "রাজশাহী", "ঢাকা"],
"বয়স": [22, 25, 21, 23, 24],
"score": [85, 72, 91, 68, 79]
})
# একটি column → Series
print("=== একটি column ===")
print(students["score"])
print("type:", type(students["score"]).__name__)
# একাধিক column → DataFrame (নোট: list ভেতরে list)
print("\n=== একাধিক column ===")
print(students[["নাম", "score"]])
# Attribute access (শুধু valid Python identifier হলে — Bangla হবে না)
print("\n=== attribute access ===")
print(students.score.head())
# গণনা
print("\nগড় score:", students["score"].mean())
print("সর্বোচ্চ:", students["score"].max())
print("জেলা গণনা:")
print(students["জেলা"].value_counts())
df['col'] → Series; df[['a','b']] → DataFrame (double bracket!)। df.col attribute access সুন্দর, কিন্তু — column নাম যদি space, hyphen, বা reserved word হয় — কাজ করে না। Bracket notation নিরাপদ।
৭ · Row selection — .loc, .iloc, ও boolean filter
Row বাছাইয়ের তিনটি প্রধান উপায়:
- .loc.loclabel-based indexing — index-এর actual label দিয়ে row select। যদি index 0,1,2,... হয়, তাহলে .loc[0] মানে "যে row-এর label 0"। Slice inclusive: .loc[1:3] মানে label 1, 2, ও 3। — label দিয়ে।
- .iloc.ilocpositional indexing — 0-based integer position দিয়ে। NumPy-র মতো। Slice exclusive: .iloc[1:3] মানে position 1 ও 2 (3 না)। — integer position দিয়ে।
- Boolean filter — শর্ত মতে।
import pandas as pd
students = pd.DataFrame({
"নাম": ["রহিম", "করিম", "সাবিনা", "ফাতিমা", "আলী"],
"জেলা": ["ঢাকা", "চট্টগ্রাম", "সিলেট", "রাজশাহী", "ঢাকা"],
"বয়স": [22, 25, 21, 23, 24],
"score": [85, 72, 91, 68, 79]
}, index=["S01", "S02", "S03", "S04", "S05"])
# .loc — label দিয়ে
print("=== .loc — label ===")
print(students.loc["S03"]) # একটি row → Series
print()
print(students.loc["S02":"S04"]) # range — INCLUSIVE
print()
print(students.loc["S01", "score"]) # row + col
# .iloc — position দিয়ে
print("\n=== .iloc — position ===")
print(students.iloc[0]) # প্রথম row
print()
print(students.iloc[1:4]) # row 1,2,3 — EXCLUSIVE
print()
print(students.iloc[-1, -1]) # শেষ row, শেষ col
# Boolean filter — সবচেয়ে শক্তিশালী
print("\n=== Boolean filter ===")
print(students[students["score"] >= 80])
# একাধিক শর্ত — & এবং | (ছোট bracket গুরুত্বপূর্ণ)
mask = (students["জেলা"] == "ঢাকা") & (students["বয়স"] > 22)
print("\n=== ঢাকার ও বয়স > 22 ===")
print(students[mask])
.loc slice inclusive ("S02":"S04" → তিন row); .iloc slice exclusive (1:4 → তিন row, position 1,2,3)। Boolean filter — শত হাজার row-এ এক লাইনে শর্ত। AI-তে train/test বাছাই, outlier remove সব এতে।
.loc ব্যবহার করুন (intent clear)। শুধু "প্রথম ৫টা" বা "শেষেরটা" — .iloc। কখনো mix করবেন না — confusion-এর মূল কারণ।
৮ · Column add/drop ও basic arithmetic
import pandas as pd
shop = pd.DataFrame({
"তারিখ": ["২০২৪-০১-০১", "২০২৪-০১-০২", "২০২৪-০১-০৩"],
"বিক্রি": [14500, 19800, 9200],
"গ্রাহক": [42, 58, 28],
"খরচ": [9000, 12000, 6500],
})
# নতুন column যোগ — অন্য column থেকে হিসাব
shop["লাভ"] = shop["বিক্রি"] - shop["খরচ"]
shop["গড়_টিকিট"] = shop["বিক্রি"] / shop["গ্রাহক"]
shop["margin_%"] = (shop["লাভ"] / shop["বিক্রি"] * 100).round(1)
print("=== নতুন column যোগের পর ===")
print(shop)
# Constant column
shop["শাখা"] = "ধানমন্ডি"
# Column drop
shop_lite = shop.drop(columns=["খরচ", "শাখা"])
print("\n=== drop-এর পর ===")
print(shop_lite)
# Rename
shop_renamed = shop.rename(columns={"বিক্রি": "sales", "গ্রাহক": "customers"})
print("\n=== rename-এর পর columns ===")
print(shop_renamed.columns.tolist())
drop() default-এ নতুন DataFrame ফেরায়, original অপরিবর্তিত। inplace=True মূল object change — কিন্তু modern Pandas-এ recommend না (chainability ভাঙে)।
df[df.x > 0]['y'] = 5 chained assignment — কাজ করতে পারে বা না-ও পারে (view vs copy ambiguity)। সবসময় df.loc[df.x > 0, 'y'] = 5 single .loc indexer ব্যবহার করুন।
৯ · AI workflow — feature engineering shorthand
ML model train করার আগে — raw column থেকে নতুন feature derive করতে হয়। Pandas-এ এক লাইনে।
import pandas as pd
import numpy as np
# একটি ই-কমার্স dataset
np.random.seed(0)
orders = pd.DataFrame({
"order_id": range(1, 11),
"জেলা": np.random.choice(["ঢাকা", "চট্টগ্রাম", "সিলেট"], 10),
"items": np.random.randint(1, 8, 10),
"amount": np.random.randint(500, 5000, 10),
"delivery_days": np.random.randint(1, 7, 10),
})
# Feature engineering — এক লাইনে
orders["per_item"] = orders["amount"] / orders["items"]
orders["fast_delivery"] = orders["delivery_days"] <= 2
orders["high_value"] = orders["amount"] > 2000
orders["জেলা_ঢাকা"] = (orders["জেলা"] == "ঢাকা").astype(int) # one-hot
print(orders)
print("\n=== summary by জেলা ===")
print(orders.groupby("জেলা")["amount"].agg(["mean", "count"]))
print("\n=== top 3 high-value orders ===")
print(orders.nlargest(3, "amount")[["order_id", "জেলা", "amount"]])
df['ratio'] = df['x'] / df['y'] — feature engineering-এর সবচেয়ে common pattern। Boolean expression-ও column হতে পারে (True/False)। .astype(int) দিয়ে 0/1-এ রূপান্তর — model-এর জন্য ready। এই সব কাজ NumPy-তেও possible, কিন্তু labeled column থাকায় Pandas-এ অনেক বেশি readable।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ Pandas কেন NumPy-র উপর আরেকটি library? শুধু label যোগ করে এত value কী? Real data কেন tabular এবং heterogeneous — এই বাস্তবতা কেন matter?
NumPy ১৯৯৫-এ Numeric নামে শুরু — গাণিতিক কাজে অসাধারণ। কিন্তু ১৯৯০-এর দশকে যারা finance, biology, social science-এ কাজ করতেন — তাদের data NumPy array-এর কাঠামোয় ফিট হতো না। Wes McKinney AQR Capital-এ এই সমস্যা থেকেই Pandas (২০০৮) লেখেন।
NumPy-র সীমাবদ্ধতা real-world data-তে:
- Homogeneous: সব element একই type। কিন্তু গ্রাহক table-এ নাম (string), বয়স (int), আয় (float), date (datetime) — সব mixed।
- No labels: column ৩ মানে কী? row ৪২ কোন গ্রাহক? — মনে রাখতে হয় বা আলাদা track।
- No missing handling: NaN আছে, কিন্তু string-এ "missing" নেই; integer column-এ NaN নেই।
- No alignment: দুই array যোগ — শুধু position মিললে। ভিন্ন date series — manual sync।
- No groupby: "জেলা ভিত্তিক গড়" — নিজেই লিখতে হয়।
Pandas যা যোগ করে:
- Index + columns: "অর্ডার ID 1042-এর amount" — সরাসরি query।
- Heterogeneous columns: এক column int, পরের string, পরের datetime — কোনো সমস্যা নেই।
- Missing data first-class: NaN, NaT, NA — সব consistent।
fillna,dropna। - Automatic alignment: দুই Series ভিন্ন index-এ — যোগ করলে union index, mismatch-এ NaN।
- Groupby/pivot/merge: SQL-এর সব power, কিন্তু in-memory + Python।
- Time-series first-class: resample, rolling window — finance/sensor data-এর জন্য essential।
Real data কেন এমন? মানুষের তৈরি data — কোনো এক "source of truth" থেকে নয়, অনেক system-এর join। CRM-এ গ্রাহক info, ERP-তে order, GA-তে behavior। তাদের union — heterogeneous, missing-ridden, label-যুক্ত। এটাই বাস্তবতা।
Performance trade-off:
- Pandas DataFrame internally — column-wise NumPy arrays। তাই vectorized ops দ্রুত।
- কিন্তু label lookup, alignment overhead — pure NumPy-র চেয়ে ২-৫× slow।
- হট numerical loop — NumPy-তে drop করুন (
df.valuesবা.to_numpy())। - Pandas 2.0+ — Arrow backend দিয়ে আরও দ্রুত, memory-efficient।
বিকল্প landscape:
- Polars: Rust-এ লেখা — ১০-১০০× দ্রুত, lazy evaluation, modern API। Pandas-এর successor হতে পারে।
- DuckDB: in-process SQL — analytical queries-এ অসাধারণ।
- Dask/Modin: Pandas-API, distributed/parallel।
- PySpark: বড় cluster-এ।
মূল উপলব্ধি: "Label" trivial মনে হলেও — সেটাই AI/data engineering-এর backbone। Pandas-এর ১৫ বছরের dominance এই কারণেই — মানুষের data আসলে heterogeneous + labeled, NumPy সেটা handle করে না। AI workflow-এ ৮০% সময় এই layer-এ — তাই Pandas মাস্টারি অনিবার্য।
প্র ০২
.loc বনাম .iloc — কেন দু'টোই দরকার? Label-based বনাম positional indexing-এর philosophical পার্থক্য কী? নতুনদের কেন এই দু'টোই বিভ্রান্ত করে?
.loc ও .iloc — Pandas-এর সবচেয়ে গুরুত্বপূর্ণ design decision। অনেক confusion এখানেই, কিন্তু এই separation deliberate এবং essential।
মূল পার্থক্য:
- .loc[label] — index-এ যা actually লেখা — সেই label খোঁজে।
- .iloc[i] — row-এর position (0-based, NumPy-র মতো)।
যদি index 0,1,2,... হয়, তাহলে দু'টোই same দেখায় — কিন্তু "accidentally same"। Index যদি ['a','b','c'] বা [101, 205, 309] হয় — তখন বিশাল পার্থক্য।
উদাহরণ:
df = pd.DataFrame({"x": [10, 20, 30]}, index=[100, 200, 300])
df.loc[100] # label 100 → 10
df.iloc[0] # position 0 → 10 (label 100)
df.loc[200] # label 200 → 20
df.iloc[1] # position 1 → 20 (label 200)
df.loc[0] # KeyError! label 0 নেই
df.iloc[100] # IndexError! position 100 নেই
Slice behavior — সবচেয়ে বড় ফাঁদ:
df.loc['a':'c']— inclusive ('a', 'b', ও 'c')।df.iloc[0:3]— exclusive (0, 1, 2 — Python convention)।
কেন আলাদা? Label-based-এ "upper bound exclusive" অর্থহীন (label কোনো ordered integer নয়, "a" এর পরে কী?)। Position-based-এ Python convention follow করা সহজ।
কেন দু'টোই দরকার?
- Intent clarity: "কোনটা" (label) বনাম "কত নম্বর" (position) — দুই ভিন্ন প্রশ্ন। API-তে ভিন্ন express।
- Error catching: typo করলে fail loud — silent wrong answer নয়।
- Performance: .iloc fast (direct array access)। .loc-এ index lookup overhead।
- Refactoring safety: column reorder করলে .iloc[2] ভেঙে যাবে; .loc['name'] ভাল থাকবে।
Philosophical depth:
- NumPy-তে শুধু position — array সংখ্যার তালিকা।
- Pandas data semantic — "এই row সেই অর্ডার"। Label = identity।
- Position = accident of storage order; Label = meaningful reference।
- SQL-এ
WHERE id = 1042≠OFFSET 5— একই concept।
নতুনদের কেন বিভ্রান্ত?
- Default index 0,1,2,... → দু'টো একই মনে হয়।
- Filter/reset_index-এর পর position বদলায় — label same।
- চয়েস:
df['col']column,df.loc[5]row — উভয়ই subscript-এ confusing। - Slice convention-এর inversion — habit ভাঙার বিরুদ্ধে যায়।
Best practices:
- Index meaningful রাখুন (set_index দিয়ে)।
- Filter-এর পর সবসময়
reset_index(drop=True)বা সচেতনভাবে .loc। - Chained indexing এড়িয়ে চলুন:
df[mask].loc[5, 'col']ভাঙা — single .loc ব্যবহার। - Production code-এ — শুধু .loc বা শুধু .iloc consistent ব্যবহার।
- Boolean mask-এ —
df[mask]বাdf.loc[mask]দু'টোই ঠিক, .loc explicit।
মূল উপলব্ধি: .loc/.iloc শুধু syntax না — দু'টি ভিন্ন data philosophy। Label = "what", Position = "where in array"। Real-world data analysis-এ "what" সবসময় primary। নতুনদের কাছে — এই দুই API আত্মস্থ করতে পারলে — Pandas-এর ৮০% complexity শেষ।
প্র ০৩
SettingWithCopyWarning কেন আসে? View vs copy ambiguity কী? Modern .loc-based assignment ও Pandas 2.0-এর copy-on-write কীভাবে এই সমস্যা সমাধান করে?
এই warning Pandas-এর সবচেয়ে কুখ্যাত — প্রায় প্রতিটি পেশাদার কোনো এক সময়ে confused হয়েছেন। মূলে — Python-এর dynamic indexing ও NumPy-র view/copy behavior-এর mismatch।
সমস্যাটা কী?
df = pd.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
# Chained indexing — দু'টি __getitem__
df[df.x > 1]['y'] = 100 # SettingWithCopyWarning!
এই line-এ যা ঘটে: ১) df[df.x > 1] → একটি নতুন subset (কখনো view, কখনো copy — Pandas নিজেই sure না)। ২) সেই subset-এ ['y'] = 100 assign। ৩) যদি copy হতো — original df অপরিবর্তিত। যদি view — change হবে। Predictable নয় — তাই warning।
View vs copy — NumPy heritage:
- NumPy slice — view (memory share)।
- NumPy fancy index — copy।
- Boolean mask — সাধারণত copy, কিন্তু কখনো optimization-এ view।
- Pandas এই behavior inherit করে — boolean filter copy হতে বাধ্য নয়।
সঠিক উপায়:
# Single .loc — atomic, explicit
df.loc[df.x > 1, 'y'] = 100
# Pandas এখানে জানে: "এই rows-এর এই column update"
# একটি indexing operation — ambiguity নেই
কেন .loc নিরাপদ?
- একটি indexing operation — Pandas সরাসরি original-এ assign করে।
- View/copy question উঠেই না — Pandas internally manage।
- Intent explicit — code reader-এর জন্য clear।
আরও সাধারণ pitfalls:
# Subset নিয়ে কাজ করতে চাইলে — explicit copy
sub = df[df.x > 1].copy()
sub['y'] = 100 # নিরাপদ — সম্পূর্ণ আলাদা object
# বনাম
sub = df[df.x > 1]
sub['y'] = 100 # warning — view না copy?
Pandas 2.0 — Copy-on-Write (CoW):
- ২০২৩-এ release। Default নয় (এখনও opt-in)।
- Setting:
pd.set_option("mode.copy_on_write", True)। - সব subset operation — virtually copy। Real copy শুধু modify-এর সময়।
- Memory efficient + predictable behavior।
- Future Pandas 3.0-এ default হবে।
CoW-এর সাথে কী বদলায়?
# CoW enabled
sub = df[df.x > 1]
sub['y'] = 100 # df-এ effect নেই (গ্যারান্টি)
# Original ও subset সম্পূর্ণ আলাদা — semantic clear
Migration tips:
- Production code-এ এখনই CoW enable করুন — future-proof।
- সব
inplace=Trueএড়িয়ে চলুন — confusing, slow, deprecated path। - Chained indexing সম্পূর্ণ avoid — শুধু single
.loc। - Subset নিয়ে কাজ — সবসময়
.copy()explicit।
Performance impact:
- CoW lazy — overhead minimal।
- Many "copy" ops actually free (just reference count)।
- Real copy যখন modify — তখনই।
- Production benchmark — neutral to faster (memory savings)।
Historical context:
- ২০১১ থেকে Pandas-এ এই issue — design debt।
- Wes McKinney নিজে পরে বলেছেন — "View/copy ambiguity আমার সবচেয়ে বড় regret।"
- Polars-এর design — এই ভুল avoid (everything immutable + lazy)।
- CoW = Pandas-এর "redemption arc"।
মূল উপলব্ধি: SettingWithCopyWarning শুধু একটা message না — Python dynamic + NumPy memory model + Pandas labeled abstraction-এর tension-এর সংকেত। Modern best practice — single .loc assignment + CoW enable। এই দু'টি habit আত্মস্থ করলে — এই warning আর কখনো আসবে না।
প্র ০৪ Pandas বনাম SQL বনাম PySpark — কখন কোনটা use করব? Scale, ergonomics, ecosystem — তিন দৃষ্টিতে বিচার করুন। বাংলাদেশের একটি ই-কমার্স startup-এর জন্য কোনটা বেছে নেবেন?
Data tooling-এর তিন major paradigm — এদের boundaries blur, কিন্তু core strength ভিন্ন। সঠিক বাছাই = engineering maturity-র চিহ্ন।
Pandas (in-memory, single-node):
- Scale: ~১-১০ GB RAM-এ আরামে। ১০০ GB-এ struggle।
- Ergonomics: অসাধারণ — REPL, notebook, instant feedback।
- Ecosystem: NumPy, scikit-learn, matplotlib, plotly — সব native integration।
- Use case: EDA, prototyping, ML feature engineering, reporting।
SQL (declarative, RDBMS):
- Scale: Postgres ~১০০ GB-TB; BigQuery/Snowflake — petabyte।
- Ergonomics: declarative — "what" বলো, "how" engine বোঝে। Optimizer অসাধারণ।
- Ecosystem: ৫০+ বছরের mature। সব BI tool support।
- Use case: Production reporting, dashboards, OLAP, transactional data।
PySpark (distributed):
- Scale: TB-PB, ১০০-১০,০০০ node cluster।
- Ergonomics: Pandas-like API, কিন্তু lazy + distributed quirks।
- Ecosystem: Hadoop ecosystem, MLlib, Kafka integration।
- Use case: ETL pipeline, big data ML, streaming।
Decision matrix:
- Data < 1 GB: Pandas (no question)।
- 1-100 GB on disk, 10 GB RAM: Pandas + chunking, বা DuckDB, বা Polars।
- Aggregations on warehoused data: SQL (Snowflake, BigQuery, Postgres)।
- 1 TB+ ETL daily: PySpark বা Snowflake।
- Real-time stream: Spark Streaming, Flink, বা Kafka Streams।
Performance reality:
- Single-node Polars/DuckDB — সাম্প্রতিক benchmark-এ Spark-কে হারায় ১০০ GB পর্যন্ত।
- "Big data" এখন ১ TB+ — অনেক startup এই scale-এ পৌঁছায় না।
- Pandas + Parquet + DuckDB combo = ৯০% কাজ cover।
- Spark complexity worth শুধু সত্যিকারের scale-এ।
Hybrid pattern (modern stack):
- Source: SQL warehouse (Postgres/BigQuery)।
- Heavy ETL: dbt + SQL (set-based, scalable)।
- ML feature engineering: Pandas/Polars in notebook।
- Production training: Spark বা Ray (যদি বড়)।
- Inference: NumPy/PyTorch (no Pandas overhead)।
বাংলাদেশের ই-commerce startup-এর জন্য recommendation:
- Year 1 (10K orders/day): Postgres + Pandas notebook। কোনো Spark complexity না।
- Year 2-3 (1M orders/day): Postgres → BigQuery/ClickHouse migration। Pandas-এ analytics তৈরি, dbt-এ production transformation।
- Year 4+ (real big data): Spark/Databricks যদি event tracking explode করে।
- ML side: Pandas + scikit-learn/XGBoost — বেশিরভাগ business problem এতেই solve।
খরচের দৃষ্টি (BD context):
- Pandas — free, ১ engineer-এই চলে।
- Postgres — VPS-এ free; managed (DigitalOcean) ~$25/mo।
- Spark cluster — minimum $500/mo, devops দরকার।
- BigQuery — pay-per-query, BD-তে latency issue।
- Snowflake — expensive কিন্তু amazing UX।
Career strategy:
- SQL — অপরিহার্য (যেকোনো data role)।
- Pandas — অপরিহার্য (ML/DS)।
- Spark — niche, কিন্তু premium salary (FAANG, fintech)।
- Modern alternative (Polars, DuckDB, dbt) — শিখুন future-proof হতে।
মূল উপলব্ধি: "Big data" hype overblown — অধিকাংশ business এই scale-এ কখনো পৌঁছায় না। Pandas + SQL দু'টোই master করুন — এতেই বেশিরভাগ AI/data career run। Spark-এ jump করার আগে সততার সাথে জিজ্ঞাসা — "আমার data সত্যিই কি এই scale-এ?" উত্তর সাধারণত "না"।
অনুশীলন
-
DataFrame তৈরি করুন: বাংলাদেশের ৫টি বড় শহরের জন্য — শহর, জনসংখ্যা (লাখে), বিভাগ — এই তিন column-এর একটি DataFrame বানান। তারপর
describe()চালান।import pandas as pd cities = pd.DataFrame({ "শহর": ["ঢাকা", "চট্টগ্রাম", "খুলনা", "রাজশাহী", "সিলেট"], "জনসংখ্যা_লাখ": [105.0, 35.0, 16.5, 9.0, 8.5], "বিভাগ": ["ঢাকা", "চট্টগ্রাম", "খুলনা", "রাজশাহী", "সিলেট"] }) print(cities) print() print(cities.describe()) print() print("সবচেয়ে বড় শহর:", cities.loc[cities["জনসংখ্যা_লাখ"].idxmax(), "শহর"]) -
Filter rows: উপরের cities DataFrame থেকে — যেসব শহরের জনসংখ্যা ১০ লাখের বেশি — শুধু সেগুলো বের করুন।
big_cities = cities[cities["জনসংখ্যা_লাখ"] > 10] print(big_cities) # .loc ব্যবহার করেও same কাজ big_cities_v2 = cities.loc[cities["জনসংখ্যা_লাখ"] > 10, ["শহর", "জনসংখ্যা_লাখ"]] print(big_cities_v2) -
Computed column: একটি দোকানের ৫ দিনের বিক্রি (sales) ও খরচ (cost) দিয়ে DataFrame বানান। নতুন column "profit" ও "margin_pct" যোগ করুন।
import pandas as pd shop = pd.DataFrame({ "দিন": ["শনি", "রবি", "সোম", "মঙ্গল", "বুধ"], "sales": [12500, 18300, 9800, 22100, 15600], "cost": [ 8000, 11500, 6200, 13800, 9700], }) shop["profit"] = shop["sales"] - shop["cost"] shop["margin_pct"] = (shop["profit"] / shop["sales"] * 100).round(2) print(shop) print() print("মোট লাভ:", shop["profit"].sum()) print("সর্বোচ্চ margin দিন:", shop.loc[shop["margin_pct"].idxmax(), "দিন"])
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পাঠ ১৪ · CSV পড়া ও পরিষ্কার করা পরবর্তী পাঠ Real data file থেকে DataFrame load — read_csv, missing handling, type fix।
- পাঠ ১২ · NumPy linalg আগের পাঠ Pandas-এর নিচে NumPy। linear algebra ভিত্তি।
- পাঠ ১৫ · GroupBy ও aggregation এই পাঠের সাথে সম্পর্কিত "জেলা ভিত্তিক গড় বিক্রি" — SQL GROUP BY-র Pandas রূপ।
- সব AI Courses দেখুন ABCL TECH Python, ML, DL, NLP, CV, GenAI, RL, MLOps — সব AI কোর্স একসাথে।