Pandas এবং NumPy লাইব্রেরী দুইটি ইন্সটল থাকতে হবে । যদি ইন্সটল না থাকে তাহলে ইন্সটল করে নিতে cmd-তে গিয়ে লিখতে হবে,
pip install pandas numpy
শ্যানন এন্ট্রপি :
অ্যালগরিদম:
- প্রথমে নির্দিষ্ট (csv or others) ফাইলটা রিড করতে হবে
import pandas as pd
import numpy as np
data = pd.read_csv("data/^TWII.csv")
- তারিখ অনুযায়ী সর্ট করবো
data.loc[:, 'Date'] = pd.to_datetime(data['Date'])
data.sort_values(by='Date', ascending=True, inplace=True)
- এই ধাপে, আমরা যে কলামের ভ্যালু (ধরলাম Close) নিয়ে কাজ করতে চাই , তাই এর ভ্যালুকে নাম্বারে কাস্টিং করেছি আর যেসব ভ্যালু নাম্বার নয় বা নাম্বারে পরিণত করা যায় না তাদের NaN (not a number) হিসেবে ধরে নিয়েছি ।
data['Close'] = pd.to_numeric(data['Close'], errors='coerce')
- চাইলে আমরা সব NaN ভ্যালুকে বাদ দিতে পারি ।
data = data.dropna(subset=['Close'])
- এবার আমরা Rt বের করব এবং ঐ ভ্যালুটাই column ভ্যারিয়েবলে রেখে দিব । আর নাল ভ্যালু থাকলে তা বাদ দিয়ে দিবো ।
column= np.log(data['Close']) - np.log(data['Close'].shift(1))
column = column.dropna()
- এবার এন্ট্রপি বের করার পালা । এর জন্য আমরা এন্ট্রপি নামের একটা ফাংশন বানাবো । আমাদের Close এর ডেটাগুলা continuous । তাই আমরা এগুলোকে বেশ কয়েকটি গ্রুপে ভাগ করে ফেলবো (যেভাবে আমরা নবম-দশম শ্রেণিতে করতাম), এই পদ্ধতির একটা নাম আছে “ডেটা বিনিং” বা “ডিসক্রেটাইজেশন” । আমরা কয়টা গ্রুপে ভাগ করতে চাই, সেই সংখ্যাকে বললাম “Bins” বা ‘শ্রেণি সংখ্যা’ । তারপর প্রত্যেকটা বিনের রেঞ্জ (bin_edges ভ্যারিয়েবলে রাখা হয়েছে) এবং ফ্রিকুয়েন্সি (ঐ বিন বা রেঞ্জের মধ্যে কয়টি ভ্যালু আছে, binned_dist ভ্যারিয়েবলে রাখা হয়েছে) তা বের করার জন্য ব্যবহার করবো NumPy এর histogram ফাংশনটা । তারপর কোন একটা নির্দিষ্ট বিন বা রেঞ্জের probability নির্ণয়ের জন্য ঐ বিনের ফ্রিকুয়েন্সিকে সব বিনের ফ্রিকুয়েন্সির যোগফল দিয়ে ভাগ করতে হয় । তাই আমরা সব বিনের probability কাউন্ট করে একটি অ্যারে (probs) এ রেখে দিব ।
def entropy(column, bins) :
binned_dist, bin_edges = np.histogram(column, bins)
probs = binned_dist / np.sum(binned_dist)

bin_edges অথবা রেঞ্জ দেখানোর ভ্যারিয়েবলটা এভাবেই হয়ে থাকে, [0.09237332, 0.12021244, 0.148155, 0.17589067] । যেখান থেকে সহজেই অনুমান করা যায় বিনগুলোর রেঞ্জ কত থেকে কত এর মধ্যে হবে । যেমন একদম প্রথম বিনের রেঞ্জ হবে 0.09237332 থেকে 0.12021244 এর আগ পর্যন্ত । যেটা লেখা যায় [ 0.09237332, 0.12021244) । বাকিগুলোও এভাবেই পাওয়া যায় ।
- Probability যেহেতু আছে তাহলে আমরা Shannon entropy বের করে নিতে পারি । তবে এর আগেই আমাদের সেসব Probability 0 তা বাদ দিয়ে দিতে হবে, কেননা log2 (0) = Undefined । তারপর Shannon Entropy’র সূত্র বসিয়ে দিলে হয়ে যাবে ।
def entropy(column, bins) :
binned_dist, bin_edges = np.histogram(column, bins)
probs = binned_dist / np.sum(binned_dist)
probs = probs[np.nonzero(probs)]
entropy = 0
for i in range(len(probs)):
entropy -= probs[i] * np.log2(probs[i]);
return entropy
- এখন আমরা entropy ফাংশনটা ব্যবহার করতে পারি
print("Entropy :", entropy(column, 12).round(4))
এবার বেশ কিছু “অতিরিক্ত” আউটপুটসহ পুরো কোডটি রিভিউ করা যাক ।
import pandas as pd
import numpy as np
data = pd.read_csv("data/canada.csv")
data.loc[:, 'Date'] = pd.to_datetime(data['Date'])
data.sort_values(by='Date', ascending=True, inplace=True)
data['Close'] = pd.to_numeric(data['Close'], errors='coerce')
column= np.log(data['Close']) - np.log(data['Close'].shift(1))
column = column.dropna()
print(f"Max Value : {column.max()}")
print(f"Min Value : {column.min()}")
print(f"Differece : {column.max() - column.min()}")
print("Bins : 12")
def entropy(column, bins) :
binned_dist, bin_edges = np.histogram(column, bins)
probs = binned_dist / np.sum(binned_dist)
bin_table = pd.DataFrame({
'Symbol' : [f"{i}" for i in range(1, len(bin_edges))],
'Bin Range': [f"[{bin_edges[i]:.4f}, {bin_edges[i+1]:.4f})" for i in range(len(bin_edges)-1)],
'Frequency': binned_dist,
'Probability': probs
});
probs = probs[np.nonzero(probs)]
print(bin_table)
entropy = 0
for i in range(len(probs)):
entropy -= probs[i] * np.log2(probs[i]);
return entropy
print("Entropy :", entropy(column, 12).round(4))
মিউচুয়াল ইনফরমেশন
H(X, Y) যা Joint Entropy বোঝায় । এর জন্য আমাদের X এবং Y এর জয়েন্ট Probability জানা লাগবে । তাই আগের মতোই তবে আরেকটি ফাংশন ব্যবহার করে আমরা তা করতে পারি । প্রথমে X,Y এর একসাথে রেঞ্জ বা বিনগুলোর কম্বিনেশন এবং এদের ফ্রিকুয়েন্সি জানা লাগবে । এর জন্য আমরা ব্যবহার করবো histogram2d() ফাংশন । আর বাকি প্রসেস ঠিক আগের মতোই । histogram2d() প্রথমে একটা 2D অ্যারে রিটার্ণ করে (যা X এবং Y এর কোন নির্দিষ্ট রেঞ্জের মধ্যে ফ্রিক্যুয়েন্সি বোঝায়, binned_XY ভ্যারিয়েবলে রাখা হয়েছে), তারপর একটা অ্যারেতে X এর রেঞ্জ (binnedX ভ্যারিয়েবল) এবং সবশেষে আরেকটা অ্যারেতে Y এর রেঞ্জ (binnedY ভ্যারিয়েবল)। জয়েন্ট এন্ট্রপি বের করার কোড:
def jointEntropy(X, Y, bins):
binned_XY, binnedX, binnedY = np.histogram2d(X, Y, bins)
probsXY = binned_XY / np.sum(binned_XY)
probsXY = probsXY[np.nonzero(probsXY)]
jointEntropy = -np.sum(probsXY * np.log2(probsXY))
return jointEntropy
# Calling this function
joint = jointEntropy(X, Y, bins);
print(f"Joint Entropy H(X, Y) : {joint:.4f}");

নিচে দুইটি ভিন্ন ফাইল থেকে ডাটা নিয়ে মিউচুয়াল ইনফরমেশন বের করার সম্পূর্ণ কোড দেওয়া হলো:
import pandas as pd
import numpy as np
# X
data = pd.read_csv("data/^SSMI_swizerland.csv");
X = data[['Date', 'Close']]
X = X.copy()
X.loc[:, 'Date'] = pd.to_datetime(X['Date'])
X.loc[:, 'Close'] = np.log(X['Close']) - np.log(X['Close'].shift(1));
X = X[X['Close'].notna()];
X.sort_values(by='Date', ascending=True, inplace=True)
X = X.dropna()
# Y
data2 = pd.read_csv("data/^TWII.csv");
Y = data2[['Date', 'Close']];
Y = Y.copy()
Y.loc[:, 'Date'] = pd.to_datetime(Y['Date'])
Y['Close'] = pd.to_numeric(Y['Close'], errors='coerce')
Y.loc[:, 'Close'] = np.log(Y['Close']) - np.log(Y['Close'].shift(1))
Y = Y[Y['Close'].notna()];
Y.sort_values(by='Date', ascending=True, inplace=True)
Y = Y.dropna()
# Merge
XY = pd.merge(X, Y, on='Date', how='inner');
def entropy(X, bins):
print(f"Max Value : {X.max() : .4f}")
print(f"Min Value : {X.min() : .4f}")
print(f"Differece : {X.max() - X.min() : .4f}")
print(f"Bins : {bins}")
binned_dist, bin_edges = np.histogram(X, bins)
probs = binned_dist / np.sum(binned_dist)
bin_table = pd.DataFrame({
'Symbol' : [f"{i}" for i in range(1, len(bin_edges))],
'Bin Range': [f"[{bin_edges[i]:.4f}, {bin_edges[i+1]:.4f})" for i in range(len(bin_edges)-1)],
'Frequency': binned_dist,
'Probability': probs
});
print(bin_table)
probs = probs[np.nonzero(probs)]
entropy = -np.sum(probs * np.log2(probs))
return entropy
def jointEntropy(X, Y, bins):
binned_XY, binnedX, binnedY = np.histogram2d(X, Y, bins)
probsXY = binned_XY / np.sum(binned_XY)
# Show the joint distribution
rows = []
for i in range(len(binnedX) - 1):
for j in range(len(binnedY) - 1):
rows.append({
'Symbol' : f"({i+1},{j+1})",
'X Bin': f"[{binnedX[i]:.4f}, {binnedX[i+1]:.4f})",
'Y Bin': f"[{binnedY[j]:.4f}, {binnedY[j+1]:.4f})",
'Frequency': binned_XY[i, j],
'Probability': probsXY[i, j]
})
table = pd.DataFrame(rows)
print(table)
probsXY = probsXY[np.nonzero(probsXY)]
jointEntropy = -np.sum(probsXY * np.log2(probsXY))
return jointEntropy
def mutualInformation(X, Y, bins) :
print("X : ");
entropyX = entropy(X, bins);
print(f"Entropy X, H(X) : {entropyX:.4f}");
print("\n\nY : ");
entropyY = entropy(Y, bins);
print(f"Entropy Y, H(Y) : {entropyY:.4f}");
print("\nJoint : ");
joint = jointEntropy(X, Y, bins);
print(f"Joint Entropy H(X, Y) : {joint:.4f}");
return entropyX + entropyY - joint
print(f"\n Mutual Information: {mutualInformation(XY['Close_x'], XY['Close_y'], 12) : .4f}");