পাঠ ১৩ · ২৫-এর মধ্যে · মডিউল ২

Pandas DataFrame পরিচিতি

Pandas DataFrame intro
৮ মিনিট পড়া শুরু · Beginner Pandas কোডসহ

এই পাঠে যা শিখবেন

  • 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-এ গোছানো হয়।

দু'টি core structure

১) Series — labeled 1D array (একটি column)।
২) DataFrame — labeled 2D table (rows × columns)। প্রতিটি column একটি Series।

Excel-এর Python rendition। Excel-এ যা mouse দিয়ে করেন — sort, filter, formula, pivot — সবই Pandas-এ code-এ। কিন্তু Excel ১০ লাখ row-তে hang; Pandas ১ কোটি row সহজে। আর reproducible — পরের সপ্তাহে আবার script চালালেই একই ফলাফল।

২ · 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 হতে হয় না, কিন্তু সাধারণত হয়।।

Python · Pandas
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())

    
Series-এর সাথে index print হয় — বাঁ পাশে label, ডান পাশে value। NumPy array-তে শুধু সংখ্যা; Series-এ সংখ্যা + label। এই label-ই Pandas-কে শক্তিশালী করে।

৩ · 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 তৈরি — তিন উপায়

Python · Pandas
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)

    
তিন উপায়েই একই কাঠামো — rows × columns। বাস্তবে CSV/JSON/SQL থেকে পড়া বেশি common (পরের পাঠে)। কিন্তু testing-এ dict সবচেয়ে দ্রুত।

৫ · Inspection — data দেখার ধাপ

কোনো নতুন DataFrame পাওয়ামাত্র — প্রথম কাজ "এতে কী আছে?" বোঝা। Pandas-এ কিছু standard method আছে।

Python · Pandas
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।
প্রথম inspection checklist: ১) shape কি প্রত্যাশিত? ২) dtypes কি সঠিক (string vs number)? ৩) head দেখে header ঠিক আছে কি? ৪) describe-এ অস্বাভাবিক mean/std/max আছে কি? ৫) isna().sum() — কত missing? এই ৫ check প্রথম মিনিটেই করুন।

৬ · Column selection — কয়েক উপায়

Python · Pandas
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 — শর্ত মতে।
Python · Pandas
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 vs .iloc — কখন কোনটা? Index যদি meaningful (তারিখ, ID, নাম) — .loc ব্যবহার করুন (intent clear)। শুধু "প্রথম ৫টা" বা "শেষেরটা" — .iloc। কখনো mix করবেন না — confusion-এর মূল কারণ।
DataFrame কাঠামো — index, columns, ও Series rows × columns + labels DataFrame (shop) index তারিখ বিক্রি গ্রাহক ছাড়% 0 2024-01-01 14523 42 10 1 2024-01-02 19872 58 5 2 2024-01-03 9134 28 0 3 ... axis=0 (rows) axis=1 (columns) df["বিক্রি"] → Series index বিক্রি 0 14523 1 19872 2 9134 3 ... name='বিক্রি', dtype: int64 extract Selection methods df["col"] → Series df.loc[label] → row df.iloc[i] → row df[mask] → filter rows df.head(n) → top n df.describe() → stats shape ও dtype df.shape → (100, 4) df.dtypes → per col df.info() → memory df.columns / df.index
DataFrame = labeled 2D table। বাঁ পাশে index (rows-এর label), উপরে columns। axis=0 rows বরাবর, axis=1 columns বরাবর। ডানে — একটি column extract করলে Series পাওয়া যায়।

৮ · Column add/drop ও basic arithmetic

Python · Pandas
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())

    
নতুন column যোগ — element-wise arithmetic (NumPy-র মতো)। drop() default-এ নতুন DataFrame ফেরায়, original অপরিবর্তিত। inplace=True মূল object change — কিন্তু modern Pandas-এ recommend না (chainability ভাঙে)।
SettingWithCopyWarning — Pandas-এ সবচেয়ে confusing warning। 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-এ এক লাইনে।

Python · 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-এ?" উত্তর সাধারণত "না"।

অনুশীলন

  1. 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(), "শহর"])
  2. 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)
  3. 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-এ আপনার পরবর্তী পদক্ষেপ

কোড রানার কাজ না করলে? ব্রাউজারে কাজ না করলে Google Colab ব্যবহার করুন — Google-এর ফ্রি অনলাইন Python পরিবেশ, Pandas pre-installed, শুধু Gmail অ্যাকাউন্ট লাগে।
পূর্ববর্তী পাঠ
পাঠ ১২ · NumPy linalg