Skip to content

পাইথনের ডেটা স্ট্রাকচার – সেট ও ডিকশনারি

সেট আমরা অনেকেই পড়েছি, না পড়লে নবম-দশম শ্রেণীর গণিত বই থেকে পড়ে নিতে হবে। তবে এখনই সেটি করা দরকার নেই, সেটের মূল ধারণাগুলো আমি আলোচনা করবো।

সেট হচ্ছে বিভিন্ন জিনিসের সমাবেশ, ইংরেজিতে বলা যায় collection of items। যেমন, আমি যদি বলি যে আমার টেবিলে এখন আছে একটি ল্যাপটপ, একটি মোবাইল, একটি গ্লাস, একটি নোটবুক, একটি কলম, তাহলে এগুলোকে একটি সেট বলা যায়। সেটের ভেতরের জিনিসগুলো দ্বিতীয় বন্ধনীর ভেতরে লিখতে হয়। দ্বিতীয় বন্ধনীকে ইংরেজিতে বলে curly braces।

{laptop, cellphone, glass, notebook, pen}

আবার আমরা যেমন বলি, সপ্তম শ্রেণীর পাঠ্যবইয়ের একটি সেট। সেটের উপাদানগুলো যেকোনো ক্রমে লিখা যায়। যেমন, সপ্তম শ্রেণীর পাঠ্যবইয়ের সেটে আমরা বাংলা, ইংরেজি, গণিত, সমাজবিজ্ঞান … এভাবে লিখতে পারি, কিংবা, ইংরেজি, বাংলা, সমাজবিজ্ঞান, গণিত … এভাবেও লেখা যায়, তাতে কোনো সমস্যা নেই, সেট একই থাকছে। তবে সেটের কোনো উপাদান একাধিকবার লেখা যায় না। আমার টেবিলে যদি দুটি কিংবা আরো বেশি কলম থাকতো, তাহলেও আমি সেটের মধ্যে pen একবারই লিখতাম।

দুটো সেটের সবগুলো উপাদান মিলে নতুন সেট তৈরি করা যায়। এটিকে বলে ইউনিয়ন (union) করা, মানে, ইউনিয়ন এখানে একটি অপারেশন। যেমন : A = {1, 2, 3, 4, 5} ও B = {2, 4, 6, 8, 10} দুটি সেট হলে A ইউনিয়ন B হবে : {1, 2, 3, 4, 5, 6, 8, 10}। আবার দুটো সেটের সাধারন উপাদানগুলো অর্থাৎ যেসব উপাদান দুটো সেটেই আছে, তাদেরকে নিয়ে একটি নতুন সেট তৈরি করা যায়। যে অপারেশনের মাধ্যমে এটি করা হয়, তাকে বলে ইন্টারসেকশন (intersection)। A ও B-এর ইন্টারসেকশন করলে আমরা পাবো : {2, 4}।

পাইথনে সেট নিয়ে কাজ করার জন্য সেট নামেই একটি ডেটা স্ট্রাকচার আছে। যেমন আমরা একটি ফাঁকা সেট তৈরি করতে পারি এভাবে : A = set()। নিচে উদাহরণ দেওয়া হলো :

>>> A = set()
>>> A
set()
>>> type(A)
<class 'set'>

আবার আমাদের কাছে যদি ইতিমধ্যেই কিছু উপাদান থাকে, সেগুলো থেকেও সেট তৈরি করা যায়। যেমন :

>>> items = {"pen", "laptop", "cellphone"}
>>> items
{'cellphone', 'pen', 'laptop'}
>>> type(items)
<class 'set'>
>>>

আবার আমাদের কাছে যদি কোনো লিস্ট বা টাপল থাকে, সেই লিস্ট বা টাপল থেকেও সেট তৈরি করা যায়।

>>> li = [1, 2, 3, 4]
>>> tpl = (1, 2, 3)
>>> A = set(li)
>>> A
{1, 2, 3, 4}
>>> B = set(tpl)
>>> B
{1, 2, 3}
>>> type(A)
<class 'set'>
>>> type(B)
<class 'set'>
>>>

স্ট্রিং থেকেও সেট তৈরি করা যায়, সে ক্ষেত্রে স্ট্রিংয়ের প্রতিটি অক্ষর সেটের একটি উপাদান হবে। আর সেটের ক্ষেত্রে কিন্তু একটি জিনিস মনে রাখতে হবে, সেটের উপাদান আগে-পরে থাকতে পারে, কোনো ক্রম (order) অনুসরণ করতে হয় না।

>>> A = set("Bangladesh")
>>> A
{'e', 'g', 'h', 'a', 's', 'l', 'B', 'd', 'n'}
>>> type(A)
<class 'set'>
>>> B = set("Sri Lanka")
>>> B
{'i', 'k', 'L', ' ', 'r', 'a', 'S', 'n'}
>>> type(B)
<class 'set'>
>>>

কোনো জিনিস একটি সেটের সদস্য কি না, সেটি আমরা বের করতে পারি এভাবে :

>>> A = {1, 2, 3}
>>> 1 in A
True
>>> 2 in A
True
>>> 5 in A
False

এখন, আমার যদি দুটি সেট থাকে, আমি সেই দুটি সেটের ওপর বিভিন্ন অপারেশন চালাতে পারি। আমরা যদি চাই যে, A ও B দুটি সেটের সাধারণ উপাদানগুলো নিয়ে একটি সেট C তৈরি করবো, তাহলে আমরা লিখতে পারি : C = A & B (মানে ইন্টারসেকশন করা)। আর আমরা যদি ইউনিয়ন করতে চাই, অর্থাৎ, A কিংবা B যেকোনো একটি সেটের সদস্য (সেসব সদস্য আবার দুটো সেটের মধ্যেও থাকতে পারে), তাহলে লিখবো C = A | B। আবার আমরা যদি চাই, A কিংবা B, যেকোনো একটি সেটে আছে কিন্তু একই সঙ্গে দুটি সেটে নেই, সেসব সদস্য পেতে, তখন লিখবো C = A ^ B। আর যদি এমন হয় যে, আমরা চাই যেসব সদস্য A সেটে আছে কিন্তু B সেটে নেই, তাদের পাওয়া যাবে এভাবে : C = A – B। নিচের উদাহরণ দেখলে আমরা বুঝতে পারবো :

>>> A = {1, 2, 3, 4, 5}
>>> B = {2, 4, 6, 8}
>>> C = A & B
>>> C
{2, 4}
>>> C = A | B
>>> C
{1, 2, 3, 4, 5, 6, 8}
>>> C = A ^ B
>>> C
{1, 3, 5, 6, 8}
>>> C = A - B
>>> C
{1, 3, 5}
>>> C = B - A
>>> C
{8, 6}

ডিকশনারি (Dictionary)

আমাদের যদি এখন একটি কাজ করতে হয় যে আমরা একটি ক্লাসের সকল শিক্ষার্থীর বাংলা পরীক্ষায় প্রাপ্ত নাম্বার রাখবো, তাহলে আমরা সেটি কিভাবে করতে পারি? আমরা তাহলে একটি লিস্ট ব্যবহার করতে পারি, যেখানে লিস্টের প্রথম উপাদান হবে ক্লাসের যেই শিক্ষার্থীর রোল নম্বর 1, তার পরীক্ষার নম্বর, দ্বিতীয় উপাদান হবে, যেই শিক্ষার্থীর রোল নম্বর 2, তার প্রাপ্ত নম্বর, এরকম। যেমন আমরা এভাবে লিস্টে রাখতে পারি :

marks = [78, 72, 68, 80, 63, 75]

তাহলে যার রোল নম্বর 3, তার বাংলা পরীক্ষায় প্রাপ্ত নম্বর হবে marks[2]। নিচের প্রোগ্রামটি একটি ফাইলে সেভ করে রান করতে হবে :

marks = [77, 76, 65, 78, 62, 64, 60, 77, 75, 79]

roll = input("Please enter your roll number: ")

print("Your marks is", marks[int(roll)-1])

রান করলে, আউটপুট আসবে এরকম :

$ python marks.py 
Please enter your roll number: 4
Your marks is 78

আবার, আমাদের যদি এরকম করতে হত যে, বাংলা ও ইংরেজি, দুটি বিষয়ের পরীক্ষায় প্রাপ্ত নম্বর রাখতে হবে। তখন কিভাবে করবো? তখন আমরা প্রতি শিক্ষার্থীর জন্য একটি লিস্ট রাখতে পারি, যেখানে প্রথম উপাদান হচ্ছে বাংলা ও দ্বিতীয় উপাদান হচ্ছে ইংরেজি পরীক্ষায় প্রাপ্ত নম্বর। আর সকল শিক্ষার্থীর নম্বরের লিস্টগুলোকে রোল নম্বরের ক্রম (ক্রম মানে সিরিয়াল) অনুযায়ী সাজাতে পারি। যেমন :

>>> marks = [[74, 73], [70, 75], [68, 72], [73, 73]]
>>> marks[0]
[74, 73]
>>> marks[1]
[70, 75]
>>> marks[1][0]
70
>>> marks[1][1]
75

এভাবে কোড লিখার সমস্যা হচ্ছে, অনেককিছু আমাদেরকে মনে রাখতে হবে। যেমন, আমি যদি জানতে চাই যে রাফি ইংরেজি পরীক্ষায় কত নম্বর পেয়েছে, তখন আমাদের জানতে হবে যে রাফির রোল নম্বর কত, আর লিস্টে ইংরেজি কত নম্বর অবস্থানে আছে, সেটিও জানা থাকতে হবে। তো এই সমস্যা থেকে উদ্ধার পাওয়ার জন্য পাইথনে একটি চমৎকার ডেটা স্ট্রাকচার আছে, যার নাম ডিকশনারি।

ডিকশনারিতে ডেটা থাকে জোড়ায় জোড়ায়। জোড়ার প্রথম জিনিসটিকে বলে কি (key) আর দ্বিতীয় জিনিসটিকে বলে ভ্যালু (value)। কোনো ডেটা পেতে হলে, আমাদের কি (key) জানতে হয়, তাহলে আমরা ভ্যালু থেকে ডেটা উদ্ধার করতে পারি। যেমন, আমি যদি বাংলা পরীক্ষার নম্বরগুলো ডিকশনারিতে রাখি, তাহলে এভাবে রাখতে পারি :

>>> marks = {1: 77, 2: 76, 5: 62, 4: 78, 3: 65}
>>> type(marks)
<class 'dict'>
>>> marks[3]
65
>>> print("Marks of roll number 4 is", marks[4])
Marks of roll number 4 is 78
>>>

আমরা দেখতে পেলাম, কিভাবে ডিকশনারি ব্যবহার করতে হয়। ডিকশনারির টাইপ হচ্ছে dict সেটিও দেখলাম। আবার কোনো কি (key) জানলে ভ্যালু কিভাবে পেতে হয়, সেটিও দেখলাম। আরেকটি বিষয় খেয়াল করতে হবে যে, ডিকশনারিতে আমাদের কিন্তু কোনো ক্রম অনুসরণ করতে হয় না। আরেকটি মজার ব্যাপার হচ্ছে, রোল নম্বর যদি 1 থেকে শুরু না হয়ে 1001 থেকে শুরু হতো, তাহলেও কোনো সমস্যা ছিল না।

>>> marks = {1005: 75, 1003: 72, 1002: 70, 1001: 75, 1004: 77}
>>> marks[1003]
72

আবার এমন যদি হয়, রোল নম্বরটি আসলে সংখ্যা নয়, তাহলেও সমস্যা নেই। যেমন –

>>> marks = {"DH2001": 75, "DH1777": 72, "KH2050": 70} 
>>> marks["DH2001"]
75

আমরা চাইলে একটি খালি ডিকশনারি (যেটিতে কোনো ডেটা নেই) তৈরি করতে পারি। কারণ, ডিকশনারি তৈরি করার পরেও এতে নতুন কি-ভ্যালু যোগ করা যায়।

>>> dt = {}
>>> dt
{}
>>> print(dt)
{}
>>> type(dt)
<class 'dict'>
>>> dt[1] = "one"
>>> print(dt)
{1: 'one'}
>>> dt[2] = "two"
>>> print(dt)
{1: 'one', 2: 'two'}

কোনো কি (key) যদি ডিকশনারিতে না থাকে, তাহলে সেটি একসেস করার সময় পাইথন এরর দেবে।

>>> dt = {"a": "A", "b": "B", "c": "C"}
>>> dt["a"]
'A'
>>> dt["b"]
'B'
>>> dt["c"]
'C'
>>> dt["d"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'd'
>>> dt["d"] = "D"
>>> dt["d"]
'D'

ডিকশনারিতে মিউটেবল নয়, এরকম যেকোনো কিছু কি (key) হিসেবে ব্যবহার করা যায়। যেমন: সংখ্যা, স্ট্রিং, টাপল। কিন্তু লিস্ট, সেট ব্যবহার করা যাবে না, কারণ লিস্ট তো মিউটেবল, মানে পরিবর্তন করা যায়। সেটও তেমনি, মিউটেবল হওয়ায় ডিকশনারির কি হিসেবে ব্যবহার করা যায় না। নিচের উদাহরণে আমরা এগুলো দেখব।

>>> dt = {"a": "A", "b": "B", "c": "C"}
>>> dt[(1, 2, 3)] = "tuple"
>>> dt
{(1, 2, 3): 'tuple', 'b': 'B', 'd': 'D', 'c': 'C', 'a': 'A'}
>>> 
>>> li = [1, 2, 3]
>>> dt[li] = "list"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> 
>>> s = {1, 2, 3, 4}
>>> s
{1, 2, 3, 4}
>>> type(s)
<class 'set'>
>>> dt[s] = "set"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'
>>>

এখন পর্যন্ত ডিকশনারির যেসমস্ত উদাহরণ দেওয়া হয়েছে সেগুলো নিজে নিজে চর্চা করতে হবে। এছাড়া মনে কোনো প্রশ্ন জাগলে কোড লিখে সেই প্রশ্নের উত্তর খুঁজে বের করার চেষ্টা করতে হবে।

অনুশীলনী :

  • আমি যদি পরীক্ষার ফল রাখার জন্য একটি ডিকশনারি ব্যবহার করি আর সেখানে কি (key) হিসেবে শিক্ষার্থীর নাম ব্যবহার করি, তাহলে কী রকম সমস্যা সৃষ্টি হতে পারে?আমরা কিন্তু এখন ডিকশনারি ব্যবহার করে বিভিন্ন শিক্ষার্থীর বাংলা ও ইংরেজি পরীক্ষার নম্বর এভাবে রাখতে পারি :
>>> marks = {"DH1001": {"Bangla": 74, "English": 73}, "DH1002": {"Bangla": 70, "English": 75}}
>>> print(marks["DH1001"])
{'Bangla': 74, 'English': 73}
>>> print(marks["DH1001"]["English"])
73
>>> marks["DH1003"] = {"Bangla": 68, "English": 72}
>>> print(marks)
{'DH1003': {'Bangla': 68, 'English': 72}, 'DH1002': {'Bangla': 70, 'English': 75}, 'DH1001': {'Bangla': 74, 'English': 73}}
>>> print(marks["DH1003"])
{'Bangla': 68, 'English': 72}
>>> print(marks["DH1003"]["Bangla"])
68

এখন কিন্তু কোড লিখতে ও বুঝতে অনেক সহজ হয়ে গেল। আমাদের রোল নম্বর জানা থাকলে শিক্ষার্থীর সব বিষয়ের নম্বর দেখাতে পারি। আবার বিষয়ের নাম জানা থাকলে সেই বিষয়ে প্রাপ্ত নম্বরও দেখানো যায় সহজেই।

প্রোগ্রামিং হচ্ছে চর্চার বিষয়, আর তাই আমরা এখন ডিকশনারি ব্যবহারের আরো একটি উদাহরণ দেখবো। আমি যদি বাংলাদেশের সবগুলো বিভাগ সম্পর্কে কিছু তথ্য, যেমন, বিভাগের কয়টি জেলা, উপজেলা ও ইউনিয়ন রয়েছে, এগুলো নিয়ে কাজ করতে চাই, তাহলে পাইথনে ডিকশনারি হচ্ছে আদর্শ ডেটা স্ট্রাকচার। উইকিপিডিয়া থেকে আমি নিচের তথ্য সংগ্রহ করলাম –

এখন আমি ওপরের তথ্যগুলো একটি ডিকশনারিতে রাখতে পারি :

>>> bd_division_info = {}
>>> bd_division_info["Barishal"] = {"district": 6, "upazila": 39, "union": 333}
>>> bd_division_info["Chittagong"] = {"district": 11, "upazila": 97, "union": 336}
>>> bd_division_info["Dhaka"] = {"district": 13, "upazila": 93, "union": 1833}
>>> bd_division_info["Khulna"] = {"district": 10, "upazila": 59, "union": 270}
>>> bd_division_info["Mymensingh"] = {"district": 4, "upazila": 34, "union": 350}
>>> bd_division_info["Rajshahi"] = {"district": 8, "upazila": 70, "union": 558}
>>> bd_division_info["Rangpur"] = {"district": 8, "upazila": 58, "union": 536}
>>> bd_division_info["Sylhet"] = {"district": 4, "upazila": 38, "union": 334}
>>> 
>>> print(bd_division_info)
{'Sylhet': {'upazila': 38, 'union': 334, 'district': 4}, 'Barishal':
{'upazila': 39, 'union': 333, 'district': 6}, 'Rangpur': {'upazila': 58,
'union': 536, 'district': 8}, 'Rajshahi': {'upazila': 70, 'union': 558,
'district': 8}, 'Chittagong': {'upazila': 97, 'union': 336, 'district': 11},
'Mymensingh': {'upazila': 34, 'union': 350, 'district': 4}, 'Dhaka':
{'upazila': 93, 'union': 1833, 'district': 13}, 'Khulna': {'upazila': 59,
'union': 270, 'district': 10}}
>>>

এখন আমি যদি চাই সবগুলো বিভাগের নাম প্রিন্ট করবো, তাহলে কী করবো? বিভাগের নামগুলো ডিকশনারির কি (key) হিসেবে ব্যবহার করা হয়েছে। সেগুলো পাওয়ার জন্য পাইথনে একটি মেথড রয়েছে .keys()।

>>> divisions = bd_division_info.keys()
>>> print(divisions)
dict_keys(['Sylhet', 'Barishal', 'Rangpur', 'Rajshahi', 'Chittagong',
'Mymensingh', 'Dhaka', 'Khulna'])

আমরা চাইলে এই কিগুলোর উপর লুপ চালিয়ে একে একে প্রিন্ট করতে পারি:

>>> for division in divisions:
...     print("Division", division)
... 
Division Sylhet
Division Barishal
Division Rangpur
Division Rajshahi
Division Chittagong
Division Mymensingh
Division Dhaka
Division Khulna

এখন যদি আমরা চাই, প্রতিটি বিভাগে কয়টি উপজেলা রয়েছে, সেই তথ্য প্রিন্ট করবো, তাহলে আমরা লুপ চালিয়ে কাজটি করতে পারি :

>>> for division in divisions:
...     print(division, "upazila", bd_division_info[division]["upazila"])
... 
Sylhet upazila 38
Barishal upazila 39
Rangpur upazila 58
Rajshahi upazila 70
Chittagong upazila 97
Mymensingh upazila 34
Dhaka upazila 93
Khulna upazila 59
>>>

আমরা যদি সরাসরি ডিকশনারির উপর লুপ চালাই, তাহলে কেবল কি-গুলো পাবো:

>>> for item in bd_district_info:
...     print(item)
... 
Sylhet
Barishal
Rangpur
Rajshahi
Chittagong
Mymensingh
Dhaka
Khulna

আমরা যদি কি ও ভ্যালু দুটোই পেতে চাই, তাহলে আমাদের হাতে দুটি পদ্ধতি রয়েছে। নিচের কোড দেখলেই বুঝতে পারা যাবে :

প্রথম পদ্ধতি:

>>> for key in bd_district_info:
...     print(key)
...     print(bd_district_info[key])
... 
Sylhet
{'upazila': 38, 'union': 334, 'district': 4}
Barishal
{'upazila': 39, 'union': 333, 'district': 6}
Rangpur
{'upazila': 58, 'union': 536, 'district': 8}
Rajshahi
{'upazila': 70, 'union': 558, 'district': 8}
Chittagong
{'upazila': 97, 'union': 336, 'district': 11}
Mymensingh
{'upazila': 34, 'union': 350, 'district': 4}
Dhaka
{'upazila': 93, 'union': 1833, 'district': 13}
Khulna
{'upazila': 59, 'union': 270, 'district': 10}

দ্বিতীয় পদ্ধতি:

>>> for key, value in bd_district_info.items():
...     print(key)
...     print(value)
... 
Sylhet
{'upazila': 38, 'union': 334, 'district': 4}
Barishal
{'upazila': 39, 'union': 333, 'district': 6}
Rangpur
{'upazila': 58, 'union': 536, 'district': 8}
Rajshahi
{'upazila': 70, 'union': 558, 'district': 8}
Chittagong
{'upazila': 97, 'union': 336, 'district': 11}
Mymensingh
{'upazila': 34, 'union': 350, 'district': 4}
Dhaka
{'upazila': 93, 'union': 1833, 'district': 13}
Khulna
{'upazila': 59, 'union': 270, 'district': 10}

অনুশীলনী

  • bd_district_info ডিকশনারি ব্যবহার করে, বাংলাদেশে মোট কয়টি জেলা, উপজেলা ও ইউনিয়ন রয়েছে, সেই তথ্য বের করে প্রিন্ট করতে হবে।

আমরা এতক্ষণ পাইথনে যেসব ডেটা স্ট্রাকচার তৈরি করে দেওয়া আছে, সেগুলোর ব্যবহার দেখলাম। কখন কোন ডেটা স্ট্রাকচার ব্যবহার করতে হবে, সেটি নিজে থেকে বুঝে নিতে হবে। আর যখন আমাদের অভিজ্ঞতা আরো বাড়বে, তখন আমরা ডেটা স্ট্রাকচারগুলো আরো বেশি ভালোভাবে ব্যবহার করতে পারবো। অভিজ্ঞতা বাড়ানোর উপায় হচ্ছে অনুশীলন করা ও নতুন নতুন প্রোগ্রাম করা। এজন্য সময় ও শ্রম দেওয়া লাগে। নতুনদের এগুলো শিখতে অনেক সময় লাগে, তাই কোনো কিছু না পারলে চিন্তিত কিংবা হতাশ হওয়ার কিছু নেই, চেষ্টা চালিয়ে যেতে হবে।

Published inUncategorized

Be First to Comment

Leave a Reply