নামপাই পরিচিতি

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

আপনি কিভাবে এই নোটবুক রান করবেন সেটি এখানে পাবেন। আর নামপাই ইন্সটলেশন মেথড এখান থেকে পাবেন। আপনি যদি (আল্লাহ না করুন) উইন্ডোজ ইউজার হয়ে থাকেন তাহলে Python (x,y) ডাউনলোড করে নিন, আমি শুনেছি জিনিসটা ভাল। আপনি উপরের লিঙ্ক থেকে এই রিপোজিটরি নামিয়ে যথারীতি ফোল্ডারে গিয়ে ipython notebook টার্মিনালে লিখলেই চলবে।

এখন তাহলে শুরু করা যাক।

ডাটা নিয়ে খেলা করতে গেলে ক্ষুদ্রতম যে লাইব্রেরি (পাইথনের নিজস্ব ডাটা স্ট্রাকচার ব্যতিরেকে) মাথায় আসে তা হল নামপাই (Numpy)। এটি আপনাকে ক্ষমতাবান করে বহুমাত্রিক অ্যারে ও তাদের উপর বিভিন্ন অপারেশান (প্রতি সদস্যের উপর ফাংশন, পুরো অ্যারে এর উপর ফাংশন, অথবা বিভিন্ন সাহায্যকারী ফাংশন ইত্যাদি), গুরুত্বপূর্ণ গাণিতিক ফাংশন, লিনিয়ার অ্যালজেব্রা, এবং C/Fortran এর কোড শেয়ারিং। একটি গুরুত্বপূর্ণ ব্যাপার হলও যে নামপাই অত্যন্ত দ্রুত কাজ করে, দেখা যাক কত দ্রুত নিচের কোডেঃ

In [4]:
from numpy import arange

big_ndarray = arange(1e7)
big_list = big_ndarray.tolist()

%timeit [i**2 for i in big_list]
%timeit big_ndarray ** 2
1 loops, best of 3: 1.7 s per loop
10 loops, best of 3: 59.7 ms per loop

দেখলেন? দ্রুততর হওয়ার পাশাপশি সুন্দর APIও দিয়েছে আপনাকে নামপাই। এবার কিছু বর্ণনা করা যাক, আমাদের প্রথম টপিক, ndarray।

ndarray

ndarray হলও নামপাই এর স্পেশাল টাইপ। এটি পাইথনের লিস্টের মতই কাজ করে, কিন্তু আরও অনেক বেশি শক্তিশালী। এতে আপনি আরও পাচ্ছেন টুলসেট ও ফাংশন যেমন ম্যাট্রিক্স অপারেশান, র‍্যান্ডোম নাম্বার জেনারেশন, এবং লুপ/ব্র্যাঞ্ছের পরিবর্তে কিছু এক্সপ্রেশন।এক্সপ্রেশন। বহু অপেরাটর ভিন্নভাবে কাজ করে ndarray এর উপর। কিছু উদাহরণ নিচে দেয়া হল-

In [2]:
import numpy as np

# শূন্যে ভরা কিছু অ্যারে। মনে রাখুন RC Cola সূত্র, Row আগে, Column পরে
z5 = np.zeros(5) # মাত্রা ১, দৈর্ঘ্য ৫
z22 = np.zeros([2, 2]) # মাত্রা ২, রো ২, কলাম ২
z56 = np.zeros([5,6]) # মাত্রা ২, রো ৫, কলাম ৬
z567 = np.zeros([5,6,7]) # মাত্রা ৩, (৫, ৬, ৭)

# ক্রমানুযায়ী নাম্বার সৃষ্টি 
r1_10 = np.arange(1, 10) # এক থেকে দশ
r1_10_01 = np.arange(1, 10, 0.1) # এক থেকে দশ, ০.১ করে ব্রিদ্ধি
ls_1_10_2 = np.linspace(10, 50, 25) # দশ থেকে পঞ্চাশ পর্যন্ত ২৫ সদস্য সমান ভাগে বিভাজ্য
l2a = np.array(range(10), dtype=np.int32) # অ্যারে সৃষ্টি লিস্ট থেকে dtype না দিলেও চলত
identity_matrix = np.identity(5) # কর্ণ বরাবর ১, অন্য সব শূন্য, শুধুমাত্র স্কোয়ার ম্যাট্রিক্সের জন্য প্রযোজ্য
random_array = np.random.randn(5, 6) # বলার কি প্রয়োজন আছে? normal distribution থেকে। beta, normal, gamma, chisquare ইত্যাদিও রয়েছে। 

নামপাইয়ের মাধ্যমে ম্যাট্রিক্সের বহু অপারেশান করা যায়, তার সাথে সাথে বেশ কিছু অপারেশান রয়েছে যা অ্যারের প্রতিটি সদস্যের উপর কাজ করে (অনেকটা হাইয়ার অর্ডার ফাংশনের মত)। অর্থাৎ আপনি for loop/list comprehension না চালিয়েই লিস্টের প্রতিটি মেম্বারের উপর কাজ করাতে পারবেন। একইভাবে, if ছারাই লিস্টের উপর ফিল্টার করানো যায়, বেশ সুন্দরভাবে, [] অভারলোডিঙ্গের মাধ্যমে। বলতে পারেন, numpy আপনাকে সুন্দর একটা DSL দেয়, নাম্বার নিয়ে খেলা করার জন্য।

In [105]:
# ম্যাট্রিক্স 
matrix_ = np.matrix([[1, 2, 4], [5, 6, 7]])
matrix_multiplication = np.matrix([[1, 2, 3], [4, 5, 6]]) * np.matrix([[1, 2], [3, 4], [5, 6]])
# উদারহনেরস্বরূপ একটি ভ্যারিয়াবল 
lst = [1, 4, 6, -1, 5, 3]
nd_array = np.array(lst)

# বিভিন্ন ওপেরাটরের ফাংশন ndarray এর উপর-
square_ = nd_array ** 2 # প্রতিটি সদস্যের উপর ** এর প্রয়োগ 
double_ = nd_array * 2 # প্রতিটি সদস্যের উপর * এর প্রয়োগ
triple_ = nd_array + nd_array + nd_array # ভ্যারিয়েবলের প্রতিটি সদস্য একে অপরের সাথে যোগ করা হয়েছে। 
inverse_ = 1. / nd_array # ১ কে প্রতিটি সদস্য দিয়ে ভাগ দেয়া হয়েছে। 

# উপরের মতই, কিন্তু অ্যারেকে প্যারামেটার হিসেবে ব্যবহার করা হয়েছে। 
square_root = np.sqrt(nd_array) 
sin_ = np.sin(nd_array)
floor_ = np.floor(nd_array)

# বুলিয়ান অপেরাটর ম্যাপের কাজ করে, একে যখন আবার অন্য অ্যারের ভিতর ঢুকান হয়, তখন ওটি 
# ফিল্টারের কাজ করে, if এর প্রয়োজন হয় না, বরং আরও দ্রুত কাজ করে এই প্রক্রিয়া।
positive_elements = nd_array[nd_array > 0]
odd_elements = nd_array[nd_array%2 == 1]

# কিছু মেটা ফাংশন, যা অ্যারের ভিতরকার তথ্য দেয়- 
dim_ = nd_array.ndim
shape_ = nd_array.shape
type_ = nd_array.dtype

# আরও কিছ ফাংশন
convert_to_a23 = nd_array.reshape(2, 3) # ৬ -> (২, ৩)
flattened_back = convert_to_a23.ravel() # বহুমাত্রিক -> এক মাত্রিক 
repeatedly_ = np.tile(nd_array, 5) # ৫ বার অ্যারেটিকে রিপিট করবে।

# আরও কিছু ম্যাট্রিক্স 
m35 = np.arange(15).reshape(3, 5)
m35T = m35.T
dot_product = m35.dot(m35T)

# এবার এলো পরিসংখ্যান 
mean_ = nd_array.mean()
standard_deviation = nd_array.std()
cumulative_sum = nd_array.cumsum()
cumulative_product = nd_array.cumprod()

স্লাইচিং

নামপাইএর স্লাইচিং একটু ভিন্নধর্মী, কিন্তু যথেষ্ট লজিক্যাল। পাইথনের পরিচিত [][] এর পরিবর্তে [,] ব্যবহৃত হয়। অর্থাৎ আপনি x[0][0] না লিখে লিখবেন x[0,0]। এখন, নিচের কোড দেখুন

In [106]:
a34 = np.arange(12).reshape(3, 4)

print a34
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
In [107]:
a34[2, 3]
Out[107]:
11
In [108]:
a34[:, 1]
Out[108]:
array([1, 5, 9])
In [109]:
a34[0, :]
Out[109]:
array([0, 1, 2, 3])
In [110]:
a34[1:, 1:]
Out[110]:
array([[ 5,  6,  7],
       [ 9, 10, 11]])
In [111]:
a34[1:3, 2:3]
Out[111]:
array([[ 6],
       [10]])
In [ ]:
 

উদাহরণ-১ সমীকরণের সমাধান

দেখা যাচ্ছে আমি মেট্রিক্স পুরা ভুলে গেছি, তাও চেষ্টা করে দেখি। নিচের সমীকরণটি দেখা যাক-

2x + 3y + z = 24
4x - 6y + 2z = 36
x + 5y - 2z = 18

আমরা যদি একে ম্যাট্রিক্সে পরিণত করি তাহলে তা দাঁড়ায়

$$ \begin{bmatrix} 2 & 3 & 1 \\\ 4 & -6 & 2 \\\ 1 & 5 & -2 \end{bmatrix} \begin{bmatrix} x \\\ y \\\ z \end{bmatrix} = \begin{bmatrix} 24 \\\ 36 \\\ 18 \end{bmatrix} $$

এটি আছে $AX = B$ ফরম্যাটে আর এর সমাধান হল $X = A^{-1}B$

এবার পাইথনের পালা

In [93]:
A = np.matrix([[2, 3, 1],
               [4, -6, 2],
               [1, 5, -2]])
B = np.matrix([[24],
               [36],
               [18]])
result = A**(-1) * B

print result
[[ 11.]
 [  1.]
 [ -1.]]

উদাহরণ-২ ইউক্লিডীয় দূরত্ব নির্ণয়

আমরা জানি ইউক্লিডীয় দূরত্ব হল $d = \sqrt{(x_{1} - x_{2})^{2} + (y_{1} - y_{2})^{2}}$ যদিও পাইথন লিস্টের মাধ্যমে এর সমাধান সহজ তবুও এর একটি নামপাই সমাধান দিলাম কারণ এটি আরও শক্তিশালী, দ্রুততর এবং মেশিন লার্নিঙের অনেক বইতেই আপনি এই কৌশলটিই দেখবেন।

In [97]:
def dist(x,y):   
    return np.sqrt(np.sum((x-y)**2))

p1 = np.array([1, 2])
p2 = np.array([4, -1])
print dist(p1, p2)
4.24264068712

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