Series আর DataFrame — পান্ডাসের দুটি জিনিস
একটি কলাম কী, একটি টেবিল কী, আর কেন একটি কলামের সব মান একই ধরনের হতে বাধ্য। df["col"] আর df[["col"]]-এর পার্থক্যসহ।
কীভাবে শিখতে চান?
যে সমস্যাটা আমরা সমাধান করছি
গত অধ্যায়ে একটি লাইন লেখা হয়েছিল, আর সেটি কাজ করেছিল:
print(sales["quantity"].sum())19লাইনটি পড়তে সহজ, কিন্তু ভেতরে একটি প্রশ্ন লুকিয়ে আছে। sales["quantity"] কী ফিরিয়ে দিল? একটি তালিকা? তাহলে তালিকার তো .sum() নেই — পাইথনে [12, 5, 2].sum() লিখলে ত্রুটি হয়।
উত্তরটা জানা দরকার, কারণ পান্ডাসে আপনি সারাক্ষণ দুটি জিনিসের একটির সাথে কাজ করবেন, আর প্রায় প্রতিটি বিভ্রান্তির গোড়ায় থাকে এই প্রশ্নটি: এই মুহূর্তে আমার হাতে কোনটি? একটি কলাম, নাকি একটি গোটা টেবিল?
আজকের অধ্যায়ে সেই দুটি জিনিসের নাম, চেহারা আর পার্থক্য।
এই অধ্যায় শেষে আপনি পারবেন
SeriesআরDataFrame-এর পার্থক্য এক বাক্যে বলতে- একটি
Seriesবানাতে, আর তার ইনডেক্স, মান ও ধরন আলাদা করে দেখতে - বলতে কেন একটি কলামের সব মান একই ধরনের হতে বাধ্য
df["col"]আরdf[["col"]]-এর পার্থক্য বুঝে ব্যবহার করতেKeyErrorদেখে বুঝতে কলামের নামে ঠিক কী ভুল হয়েছে
আগে যা জানা লাগবে: পান্ডাস ইনস্টল করা ও প্রথম টেবিল।
Series — একটি কলাম
সবচেয়ে ছোট একক দিয়ে শুরু করি। একটি Series হলো এক সারি মান, আর তাদের সাথে জোড়া লাগানো একটি করে নাম:
import pandas as pd
quantity = pd.Series([12, 5, 2])
print(quantity)0 12
1 5
2 2
dtype: int64তিনটি মান দিয়েছিলাম, কিন্তু ছাপা হলো তিনটি জোড়া। বাঁ পাশের 0, 1, 2 হলো ইনডেক্স — প্রতিটি মানের নাম। আপনি দেননি, পান্ডাস শূন্য থেকে গুনে বসিয়ে দিয়েছে।
আর সবার নিচে dtype: int64 — গোটা সিরিজটির ধরন। একটিমাত্র ধরন, প্রতিটি মানের জন্য আলাদা নয়। এটাই পান্ডাসকে দ্রুত করে, আর এটাই তার প্রধান সীমাও।
তিনটি অংশ আলাদা করে দেখা যায়:
import pandas as pd
quantity = pd.Series([12, 5, 2])
print(quantity.index)
print(quantity.values)
print(quantity.dtype)RangeIndex(start=0, stop=3, step=1)
[12 5 2]
int64RangeIndex মানে ইনডেক্সটি নিছক একটি গণনা — শূন্য থেকে শুরু, এক করে বাড়ে। values হলো মানগুলো, নাম ছাড়া। আর dtype গোটা কলামের ধরন।
ইনডেক্স সংখ্যা হতেই হবে এমন নয়:
import pandas as pd
quantity = pd.Series([12, 5, 2], index=["pen", "notebook", "bag"])
print(quantity)
print(quantity["notebook"])pen 12
notebook 5
bag 2
dtype: int64
5এখন সারিগুলোর নাম পণ্যের নাম, আর সেই নাম দিয়েই মান তোলা যাচ্ছে। এটি পাইথনের ডিকশনারির মতো শোনায়, আর মিলটা আছে — তবে একটি বড় পার্থক্যসহ:
import pandas as pd
quantity = pd.Series([12, 5, 2], index=["pen", "notebook", "bag"])
print(quantity * 2)pen 24
notebook 10
bag 4
dtype: int64গোটা কলামটিকে একবারে দুই দিয়ে গুণ করা গেল, কোনো লুপ ছাড়া। ডিকশনারিতে এটি করতে হলে প্রতিটি মানের উপর ঘুরতে হতো। একটি Series-এর উপর একটি কাজ চালালে পান্ডাস সেটি প্রতিটি মানে প্রয়োগ করে — এই আচরণটির উপরেই বাকি সব অধ্যায় দাঁড়িয়ে আছে।
DataFrame — একগুচ্ছ কলাম, একটি ইনডেক্স
একটি DataFrame হলো কয়েকটি Series পাশাপাশি বসানো, যারা একই ইনডেক্স ভাগ করে নেয়।
import pandas as pd
sales = pd.DataFrame({
"product": ["pen", "notebook", "bag"],
"quantity": [12, 5, 2],
"price": [15.0, 60.0, 850.0],
})
print(sales)
print(sales.shape)
print(sales.columns)product quantity price
0 pen 12 15.0
1 notebook 5 60.0
2 bag 2 850.0
(3, 3)
Index(['product', 'quantity', 'price'], dtype='object')ওই «একই ইনডেক্স ভাগ করে নেওয়া» কথাটাই আসল। 0 নম্বর সারির product, quantity আর price — তিনটিই একই জিনিসের কথা বলছে, একটি কলম। ইনডেক্সই তিনটি কলামকে সারিতে বেঁধে রাখে।
এক টেবিল, একাধিক ধরন — কিন্তু এক কলাম, এক ধরন
import pandas as pd
sales = pd.DataFrame({
"product": ["pen", "notebook", "bag"],
"quantity": [12, 5, 2],
"price": [15.0, 60.0, 850.0],
})
print(sales.dtypes)product object
quantity int64
price float64
dtype: objectতিনটি কলামের তিনটি ধরন। int64 পূর্ণসংখ্যা, float64 দশমিক, আর object — এটি সাধারণত লেখা বোঝায়।
খেয়াল করুন, str লেখা নেই। পান্ডাসের ধরনগুলো NumPy থেকে আসা, আর সেখানে প্রতিটি মান সমান জায়গা নেয় — সংখ্যার ক্ষেত্রে যা সম্ভব, বিভিন্ন দৈর্ঘ্যের লেখার ক্ষেত্রে নয়। তাই লেখা রাখার সময় পান্ডাস কেবল পাইথনের বস্তুর দিকে নির্দেশ রাখে, আর সেই ব্যবস্থাটির নাম object।
এখন গুরুত্বপূর্ণ নিয়মটি: একটি কলামের সব মান একই ধরনের। এক কলামে সংখ্যা আর লেখা মেশালে কী হয়?
import pandas as pd
mixed = pd.Series([1, "two", 3.0])
print(mixed)
print(mixed.dtype)0 1
1 two
2 3.0
dtype: objectপান্ডাস ত্রুটি দেয় না — সে গোটা কলামটিকে object বানিয়ে ফেলে, অর্থাৎ সবচেয়ে ঢিলে ধরনে নামিয়ে আনে। ফল ভয়াবহ কিছু নয়, কেবল নীরব: কলামটি আর সংখ্যার কলাম নয়, তাই .mean() চালাতে গেলে ব্যর্থ হবে আর .sum() লেখা জোড়া লাগাতে শুরু করবে। CSV ফাইলে একটি সারিতে ভুল করে N/A লেখা থাকলে ঠিক এটাই ঘটে, আর সেটি ষষ্ঠ অধ্যায়ের বিষয়।
কোনটি হাতে — Series না DataFrame?
import pandas as pd
sales = pd.DataFrame({
"product": ["pen", "notebook", "bag"],
"quantity": [12, 5, 2],
"price": [15.0, 60.0, 850.0],
})
column = sales["quantity"]
print(type(column))
print(type(sales))
print(column.mean())<class 'pandas.core.series.Series'>
<class 'pandas.core.frame.DataFrame'>
6.333333333333333এবার অধ্যায়ের শুরুর প্রশ্নটির উত্তর মিলল। sales["quantity"] একটি তালিকা নয়, একটি Series — আর Series-এর .sum(), .mean(), .max() সবই আছে।
আর এখানে একটি জোড়া আছে যা প্রথমবার সবাইকে থমকে দেয়:
import pandas as pd
sales = pd.DataFrame({
"product": ["pen", "notebook", "bag"],
"quantity": [12, 5, 2],
"price": [15.0, 60.0, 850.0],
})
print(sales["quantity"])
print("---")
print(sales[["quantity"]])0 12
1 5
2 2
Name: quantity, dtype: int64
---
quantity
0 12
1 5
2 2একটি বন্ধনী বেশি, আর ফলটি সম্পূর্ণ অন্য জিনিস। উপরেরটি একটি Series — একটি কলাম, যার নিচে তার নাম আর ধরন লেখা। নিচেরটি একটি DataFrame — একটিমাত্র কলামওয়ালা একটি টেবিল, তাই উপরে কলামের শিরোনাম আছে।
নিয়মটি হলো: ভেতরের বর্গবন্ধনী মানে কলামের একটি তালিকা, আর তালিকা চাইলে ফলও টেবিলই হয়। তাই sales[["product", "price"]] লিখলে দুই কলামের একটি টেবিল পাওয়া যায়।
কেন এই পার্থক্যটা মনে রাখা দরকার? কারণ ত্রুটির বার্তাগুলো এখানেই আলাদা হয়ে যায়।Series-এ.mean()আছে, কিন্তু কলামের নাম নেই;DataFrame-এ কলাম বাছাই আছে, কিন্তু সরাসরি একটি সংখ্যা দেয় না। «অবজেক্টটির এই অ্যাট্রিবিউট নেই» ধরনের বার্তা দেখলে প্রথম প্রশ্নটি হওয়া উচিত: আমার হাতে আসলে কোনটি?
একটা সম্পূর্ণ উদাহরণ
inventory.py:
import pandas as pd
# Product names become the index, so a row can be reached by name.
stock = pd.DataFrame(
{
"quantity": [12, 5, 2, 30],
"price": [15.0, 60.0, 850.0, 8.0],
},
index=["pen", "notebook", "bag", "eraser"],
)
print(stock)
print()
print("Shape :", stock.shape)
print("Columns :", list(stock.columns))
print("Index :", list(stock.index))
print()
# A column is a Series, so an operation applies to every value at once.
value = stock["quantity"] * stock["price"]
print("Value per product:")
print(value)
print()
print("Total value:", value.sum())quantity price
pen 12 15.0
notebook 5 60.0
bag 2 850.0
eraser 30 8.0
Shape : (4, 2)
Columns : ['quantity', 'price']
Index : ['pen', 'notebook', 'bag', 'eraser']
Value per product:
pen 180.0
notebook 300.0
bag 1700.0
eraser 240.0
dtype: float64
Total value: 2420.0দুটি জিনিস লক্ষ করুন।
প্রথমত, stock["quantity"] * stock["price"] দুটি Series-কে গুণ করেছে, আর ফলটি আরেকটি Series। পান্ডাস মিলিয়েছে ইনডেক্স ধরে — pen-এর সংখ্যার সাথে pen-এর দাম, bag-এর সাথে bag-এর। সারির ক্রম নয়, নাম মিলিয়ে। এই আচরণটিই পরে দুটি টেবিল জোড়া লাগানোর ভিত্তি।
দ্বিতীয়ত, ফল কলামটির ধরন float64, যদিও quantity ছিল int64। পূর্ণসংখ্যা আর দশমিক গুণ করলে দশমিকই আসে — সংখ্যার এই নিয়মটি পাইথনেও একই, পান্ডাস আলাদা কিছু করছে না।
কিছু ভাঙা অবস্থা ও তার সমাধান
KeyError: 'Quantity' এমন কোনো কলাম নেই। কলামের নাম ছোট-বড় হাতের অক্ষরের পার্থক্য মানে, আর CSV ফাইল থেকে আসা নামে প্রায়ই শুরুতে বা শেষে একটি স্পেস লেগে থাকে। print(list(df.columns)) চালিয়ে ঠিক কী আছে তা দেখে নিন — তালিকা হিসেবে ছাপালে স্পেসগুলো কোটেশনের ভেতরে ধরা পড়ে।
AttributeError: 'DataFrame' object has no attribute 'mean_price' df.column_name লিখেও কলাম পাওয়া যায়, কিন্তু সেটি কেবল সহজ নামের ক্ষেত্রে কাজ করে — নামে স্পেস থাকলে, নামটি সংখ্যা দিয়ে শুরু হলে, বা count-এর মতো পান্ডাসের নিজের কোনো মেথডের সাথে মিলে গেলে নয়। df["column_name"] সবসময়ই কাজ করে, তাই সেটিই লেখার অভ্যাস করুন।
TypeError: unsupported operand type(s) — একটি সংখ্যার কলামে গণনা করতে গিয়ে কলামটি সম্ভবত সংখ্যার নয়। df.dtypes দেখুন; object থাকলে ভেতরে কোথাও লেখা মিশে আছে।
.sum() সংখ্যা যোগ না করে লেখা জোড়া লাগাচ্ছে একই কারণ — কলামটি object। একটিমাত্র সারিতে N/A বা একটি ফাঁকা ঘর গোটা কলামের ধরন বদলে দিতে যথেষ্ট।
ValueError: All arrays must be of the same length pd.DataFrame({...})-এ দেওয়া তালিকাগুলোর দৈর্ঘ্য এক নয়। প্রতিটি কলামে সমান সংখ্যক মান থাকতে হবে, কারণ সারিগুলোকে একই ইনডেক্সে বাঁধতে হয়।
ধাপ ৪ / ৬ — অনুমান
যাচাই করুন
তিনটি মান দেওয়া হয়েছে। ছাপা হলে ঠিক কী দেখা যাবে?
import pandas as pd
quantity = pd.Series([12, 5, 2])
print(quantity)- A12 5 2
- B[12, 5, 2]
- C12 5 2 dtype: int64
- D0 12 1 5 2 2 dtype: int64
দুটি লাইনের পার্থক্য কেবল এক জোড়া বাড়তি বন্ধনী। type() কী বলবে?
import pandas as pd
sales = pd.DataFrame({
"product": ["pen", "notebook", "bag"],
"quantity": [12, 5, 2],
"price": [15.0, 60.0, 850.0],
})
print(type(sales["quantity"]))
print(type(sales[["quantity"]]))- Aদুটিই Series
- Bদুটিই DataFrame
- Cপ্রথমটি Series, দ্বিতীয়টি DataFrame
- Dপ্রথমটি DataFrame, দ্বিতীয়টি Series
একটি কলামে সংখ্যা আর লেখা মেশানো হয়েছে। পান্ডাস কী করবে?
import pandas as pd
mixed = pd.Series([1, "two", 3.0])
print(mixed.dtype)- Aকোনো ত্রুটি দেয় না — গোটা কলামটির ধরন `object` বানিয়ে ফেলে
- B`TypeError` — এক কলামে দুই ধরন রাখা যায় না
- Cলেখাটিকে বাদ দিয়ে কলামটিকে `float64` রাখে
- Dপ্রতিটি মানের নিজের ধরন আলাদা রাখে
উত্তর দিতে অ্যাকাউন্ট লাগবে
উত্তর মিলিয়ে দেখতে সাইন ইন করুন
প্রশ্নগুলো উপরে আছে, আর মাথায় মাথায় উত্তর ভেবে নেওয়াই আসল কাজ। সঠিক উত্তর, ব্যাখ্যা আর তিন ধাপের ইঙ্গিত দেখতে সাইন ইন করুন।
নিজে করুন
library.py নামে একটি ফাইল লিখুন, যেখানে থাকবে অন্তত পাঁচটি বইয়ের একটি DataFrame — বইয়ের নাম ইনডেক্স হিসেবে, আর কলামে থাকবে কপির সংখ্যা (পূর্ণসংখ্যা), দাম (দশমিক) আর লেখকের নাম (লেখা)।
তারপর ছাপান:
- পুরো টেবিল,
shape, আরdtypes - দামের কলামটি একা — এবং
type()দিয়ে দেখান সেটি একটিSeries - দামের কলামটি দুই বন্ধনী দিয়ে — এবং দেখান এবার সেটি একটি
DataFrame - প্রতিটি বইয়ের মোট মূল্য (কপি × দাম), আর সব মিলিয়ে মোট
শেষে একটি পরীক্ষা করুন: কপির সংখ্যার তালিকায় একটি মানের জায়গায় "unknown" লিখে দিন আর ফাইলটি আবার চালান। dtypes কী বলছে এখন, আর মোট মূল্যের হিসাবটির কী হলো?
এটাই এই অধ্যায়ের সবচেয়ে দামি পাঁচ মিনিট। একটি মাত্র ঘরে একটি শব্দ গোটা কলামের ধরন বদলে দেয়, আর কোনো ত্রুটি ছাড়াই হিসাব ভেঙে দেয় — বাস্তব ডেটায় এটি নিয়মিত ঘটে।
ধাপ ৬ / ৬
কঠিন করা — অধ্যায়ের কুইজ
সহজ থেকে কঠিন — দশটি প্রশ্ন, শেষেরগুলো ইচ্ছে করেই কঠিন।
সাইন ইন করে কুইজ দিন