আমার আগের পোস্টে আমি পান্ডাসের বেসিক পরিচিতি এবং এর একমাত্রিক ডাটা স্ট্রাকচার সিরিজ নিয়ে কথা বলেছি। এখন আমি মাত্রা বাড়িয়ে দিব ও আমাদের এখনকার টপিক হবে ডাটাফ্রেম।
ডাটাফ্রেম হল পান্ডাসের দ্বিমাত্রিক ডাটা স্ট্রাকচার। রো ও কলাম দুই বরাবর আপনি ইনডেক্স করতে পারবেন আর সর্টিং, সারচিং, গ্রুপিং ইত্যাদি করা যাবে। প্লটিং তো আছেই। বলতে পারেন, পাইথনের ভিতর এক্সেল টাইপ কাজ করতে পারবেন, প্রোগ্রামেটিকালী। (অনেকে বলে এটি R এর data.frame এর মত কিন্তু আমি R পারি না তো তা বলতে পারছি না)।
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# দ্বিমাত্রিক অ্যারে (অথবা লিস্টের ভিতর লিস্ট) দিয়ে আমরা ডাটাফ্রেম ইনিসিয়ালাইজ করতে পারি।
fake_2d_data = np.random.randn(25).reshape(5, 5)
df = pd.DataFrame(fake_2d_data,
index=["alpha", "beta", "gamma", "delta", "epsilon"],
columns=["A", "B", "C", "D", "E"])
df.plot(kind="barh", stacked=True)
<matplotlib.axes.AxesSubplot at 0xb9c0b6c>
আমরা উপরের ডাটাফ্রেমের অক্ষ ঘুরাতে পারি ট্রান্সপোজের মাধ্যমে।
df_t = df.T
df_t.plot(kind='barh', stacked=True)
<matplotlib.axes.AxesSubplot at 0xb9f70ac>
যখন আইপাইথন নোটবুকে কাজ করা হবে তখন নিচের কোড দিয়ে সুন্দরভাবে এইচটিএমএল টেব্ল প্রিন্ট করা যায়।
from IPython.core.display import HTML
display(HTML(df.to_html()))
A | B | C | D | E | |
---|---|---|---|---|---|
alpha | 0.960272 | -0.826986 | -0.466003 | -2.164979 | -0.515156 |
beta | -0.552988 | -0.377938 | -0.221681 | -0.198346 | 0.023387 |
gamma | 1.961069 | 1.710462 | -0.438632 | 1.606601 | -1.558174 |
delta | -0.674267 | 0.091664 | 0.262994 | -1.355863 | -1.075137 |
epsilon | -1.406780 | 0.781694 | -0.257940 | -0.012960 | 0.142525 |
display(HTML(df_t.to_html()))
alpha | beta | gamma | delta | epsilon | |
---|---|---|---|---|---|
A | 0.960272 | -0.552988 | 1.961069 | -0.674267 | -1.406780 |
B | -0.826986 | -0.377938 | 1.710462 | 0.091664 | 0.781694 |
C | -0.466003 | -0.221681 | -0.438632 | 0.262994 | -0.257940 |
D | -2.164979 | -0.198346 | 1.606601 | -1.355863 | -0.012960 |
E | -0.515156 | 0.023387 | -1.558174 | -1.075137 | 0.142525 |
JSON, CSV, HTML ইত্যাদি ফরম্যাটও রয়েছে। নিচের কোড থেকে দেখা যাবে।
print filter(lambda i: i.startswith("to_"), dir(df))
['to_clipboard', 'to_csv', 'to_dense', 'to_dict', 'to_excel', 'to_gbq', 'to_hdf', 'to_html', 'to_json', 'to_latex', 'to_msgpack', 'to_panel', 'to_period', 'to_pickle', 'to_records', 'to_sparse', 'to_sql', 'to_stata', 'to_string', 'to_timestamp', 'to_wide']
আমরা আমাদের ডাটা সম্বন্ধিত পরিসংখ্যান পেতে পারি ডিস্ক্রাইব ফাংশনের মাধ্যমে।
df.describe()
A | B | C | D | E | |
---|---|---|---|---|---|
count | 5.000000 | 5.000000 | 5.000000 | 5.000000 | 5.000000 |
mean | 0.057461 | 0.275779 | -0.224253 | -0.425109 | -0.596511 |
std | 1.368472 | 0.998950 | 0.292811 | 1.435495 | 0.723005 |
min | -1.406780 | -0.826986 | -0.466003 | -2.164979 | -1.558174 |
25% | -0.674267 | -0.377938 | -0.438632 | -1.355863 | -1.075137 |
50% | -0.552988 | 0.091664 | -0.257940 | -0.198346 | -0.515156 |
75% | 0.960272 | 0.781694 | -0.221681 | -0.012960 | 0.023387 |
max | 1.961069 | 1.710462 | 0.262994 | 1.606601 | 0.142525 |
একটু নন-র্যান্ডম ডাটা নিয়ে কাজ করা যাক যাতে আমাদের কাজগুলো ভেরিফাইড হয়।
df = pd.DataFrame(np.arange(16).reshape(4, 4),
index=list("ABCD"),
columns=list("WXYZ"))
display(HTML(df.to_html()))
W | X | Y | Z | |
---|---|---|---|---|
A | 0 | 1 | 2 | 3 |
B | 4 | 5 | 6 | 7 |
C | 8 | 9 | 10 | 11 |
D | 12 | 13 | 14 | 15 |
এবার পালা কিছু অপারেশানের। মনে রাখবেন রো হল ১, আর কলাম হল ০।
total_by_columns = df.sum(axis=0)
total_by_rows = df.sum(axis=1)
cumsum_by_columns = df.cumsum(axis=0)
cumsum_by_rows = df.cumsum(axis=1)
percentage_change_by_columns = df.pct_change(axis=0)
percentage_change_by_row = df.pct_change(axis=1)
# See also: mean, median, mad, var, skew, curt etc. Use ipython's ? and ?? for help.
ডাটা কালেকশনের সময়ে প্রায়েই আপনি মিসিং ডাটা পাবেন। যা কিনা আপনার কাঠামোতে গ্যাপ হিসাবে থাকবে। এগুলো হ্যান্ডল করার টুল দিয়েছে আপনাকে পান্ডাস। যার বেশিরভাগই "na" (অর্থাৎ not available) দিয়ে শেষ হয়। উদাহরণস্বরূপ দেখি-
print filter(lambda i: i.endswith("na") or "null" in i, dir(df))
['dropna', 'fillna', 'isnull', 'notnull']
pd.DataFrame([[1, None, 2, 4, None], [2, 3, 3, 5]]).dropna()
0 | 1 | 2 | 3 | 4 |
---|
pd.DataFrame([[1, None, 2, 4, None], [2, 3, 3, 5]]).fillna("শূন্য")
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | 1 | শূন্য | 2 | 4 | শূন্য |
1 | 2 | 3 | 3 | 5 | শূন্য |
আপনি যেই অক্ষই চিন্তা করেননা কেন, আপনি এক ধাপ ধরলে একটি অ্যারে পাবেন। যদি অক্ষ হয় ০ তাহলে কলাম, অন্যথায় রো। এখন, আপনি sum, cumsum, mean, median, var ইত্যাদি পাচ্ছেন সংশ্লিষ্ট অপারেশানের জন্য। কিন্তু আপনি যদি নিজের কিছু চান তাহলে? তাহলে আপনার এমন ফাংশন লিখতে হবে যার প্রথম প্যারামিটার অ্যারে, এবং আপনি আপনার ডাটাফ্রামের সাথে তাকে আপ্লাই করবেন। যেমন নিচের কোডটিকেই ধরুন-
df = pd.DataFrame(np.arange(10).reshape(5, 2))
df
0 | 1 | |
---|---|---|
0 | 0 | 1 |
1 | 2 | 3 |
2 | 4 | 5 |
3 | 6 | 7 |
4 | 8 | 9 |
def multiply_with(df, n):
return df * n
df.apply(multiply_with, n=10)
0 | 1 | |
---|---|---|
0 | 0 | 10 |
1 | 20 | 30 |
2 | 40 | 50 |
3 | 60 | 70 |
4 | 80 | 90 |
df = pd.DataFrame(np.random.randn(3, 5))
df
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | 0.080426 | 2.012099 | 0.919150 | -0.379672 | -1.204189 |
1 | 0.231752 | 0.451970 | -1.732385 | 0.702375 | -0.860986 |
2 | -0.551936 | 0.911473 | -1.215514 | -0.694774 | 1.312840 |
এইবার ইন্ডেক্সিং। বিশেষ এক ডাটাফ্রেম তৈরি করি যেন ইন্ডেক্সিং বুঝতে সুবিধা হয়।
df = pd.DataFrame([["00", "01", "02", "03"],
["10", "11", "12", "13"],
["20", "21", "22", "23"],
["40", "41", "42", "43"],],
index=["r0", "r1", "r2", "r3"],
columns=["c0", "c1", "c2", "c3"])
df
c0 | c1 | c2 | c3 | |
---|---|---|---|---|
r0 | 00 | 01 | 02 | 03 |
r1 | 10 | 11 | 12 | 13 |
r2 | 20 | 21 | 22 | 23 |
r3 | 40 | 41 | 42 | 43 |
ডাটাফ্রেম আর নামপাই অ্যারে একই রকম ইন্ডেক্সিং ব্যবহার করে এক বিশেষ ফাংশন ix এর মাধ্যমে।
df.ix[2, :] # রো ২ এ স্থির।
c0 20 c1 21 c2 22 c3 23 Name: r2, dtype: object
df.ix[:, 3] # কলাম ৩ এ স্থির।
r0 03 r1 13 r2 23 r3 43 Name: c3, dtype: object
df.ix[1:2, 1:3] # ১ থেকে ২ এর আগ পর্যন্ত রো, ১ থেকে ৩ এর আগ পর্যন্ত কলাম।
c1 | c2 | |
---|---|---|
r1 | 11 | 12 |
কিন্তু ix কেন? সরাসরি [] কেন ইউজ করলাম না? কারণ, [] দিয়ে কলাম বের করব। আর প্রতিটি কলাম হল ডাটাফ্রেমের অ্যাট্রিবিউট। আর তা আপনাকে দিবে একটি সিরিজ যার অ্যাট্রিবিউট হবে রো।
df["c1"] # df.c1 দিলেও চলত। জাভাস্ক্রিপ্টের মত।
r0 01 r1 11 r2 21 r3 41 Name: c1, dtype: object
type(df.c1)
pandas.core.series.Series
df.c1.r1
'11'
এতক্ষণ দেখলাম যে ২-ডি অ্যারে দিয়েই ডাটাফ্রেম তৈরি করেছি। কিন্তু আসলে আরেকভাবে ডাটাফ্রেম তৈরি করা যায়। ডিকশনারি দিয়ে। এটি zip নিয়ম ফলো করে নামপাইএর মত।
data = {
"names": ["Uruguay", "Brazil", "Argentina", "Germany", "Italy", "Spain", "England"],
"participated": [12, 20, 16, 18, 18, 14, 14,],
"continent": ["SA", "SA", "SA", "E", "E", "E", "E"],
"wins": [1, 5, 2, 4, 4, 1, 1],
"runnerup": [2, 7, 5, 8, 6, 1, 1]
}
df = pd.DataFrame(data,
index=["Uruguay", "Brazil", "Argentina", "Germany", "Italy", "Spain", "England"],)
df
continent | names | participated | runnerup | wins | |
---|---|---|---|---|---|
Uruguay | SA | Uruguay | 12 | 2 | 1 |
Brazil | SA | Brazil | 20 | 7 | 5 |
Argentina | SA | Argentina | 16 | 5 | 2 |
Germany | E | Germany | 18 | 8 | 4 |
Italy | E | Italy | 18 | 6 | 4 |
Spain | E | Spain | 14 | 1 | 1 |
England | E | England | 14 | 1 | 1 |
df.plot(kind="barh", grid=True, stacked=True, rot=30)
<matplotlib.axes.AxesSubplot at 0xdfc81cc>
df.continent.value_counts().plot(kind="pie", label="Continents").axis("equal")
(-1.0000000210975502, 1.0000000424121425, -1.0253279992249265, 1.0083560303418613)
অনেক কিছুই দেখলাম, এখন চলুন কিছু অ্যাডভান্সড কয়েরি করি।
ধরুন, আপনি চাচ্ছেন যে সমান সংখ্যকবার চ্যাম্পিয়ন হওয়া দেশগুলি একত্রিত হোক। এর জন্য আপনাকে বুঝতে হবে পিভটইং ও স্টয়াকিং। পিভট হল কোন একটি অ্যাট্রিবিউটকে ইউনিক রেখে সেই ভ্যালুর সাথেকার অন্যান্য অ্যাট্রিবিউটকে সাজান। অনেকটা সেট এর ক্রস প্রোডাক্টের মত, একটু রিয়ারেঞ্জ আরকি। মনে করুন আপনি ইনডেক্সকে ধরে পুরা টাব্লকে ঘুরালেন। stack এর মাধ্যমে টেব্লে আঁকারে সাজান হয় আর unstack এর মাধ্যমে লিস্ট আঁকারে। মনে রাখবেন, কলাম ইউনিক থাকবে, আর ইনডেক্স অনুযায়ী একত্রিত হবে। অর্থাৎ, যদি কলাম হয় names আর wins হয় ইনডেক্স, তাহলে, এমন হবে, "প্রতিটি কলাম ভ্যালু বনাম ইনডেক্সের প্রতিটি ভ্যালু, থাকলে সেই ভ্যালু, না থাকলে NaN.
pivoted = df.pivot(index="wins", columns="names")
pivoted.unstack()
# বিশাল এক ক্রস প্রোডাক্ট। NaN ভ্যালু হল খালি ডাটার জন্যে।
names wins participated Argentina 1 NaN 2 16 4 NaN 5 NaN Brazil 1 NaN 2 NaN 4 NaN 5 20 England 1 14 2 NaN 4 NaN 5 NaN Germany 1 NaN 2 NaN 4 18 5 NaN Italy 1 NaN 2 NaN 4 18 5 NaN Spain 1 14 2 NaN 4 NaN 5 NaN Uruguay 1 12 2 NaN 4 NaN 5 NaN runnerup Argentina 1 NaN 2 5 4 NaN 5 NaN Brazil 1 NaN 2 NaN 4 NaN 5 7 England 1 1 2 NaN 4 NaN 5 NaN Germany 1 NaN 2 NaN 4 8 5 NaN Italy 1 NaN 2 NaN 4 6 5 NaN Spain 1 1 2 NaN 4 NaN 5 NaN Uruguay 1 2 2 NaN 4 NaN 5 NaN Length: 56, dtype: float64
pivoted.stack()
# (wins, names) কে ধরে ডাটার চেহারা, unstack হয়েছে কলয়াপ্সেড। stack সমস্ত N/A কে চুপসে দিয়েছে।
continent | participated | runnerup | ||
---|---|---|---|---|
wins | names | |||
1 | England | E | 14 | 1 |
Spain | E | 14 | 1 | |
Uruguay | SA | 12 | 2 | |
2 | Argentina | SA | 16 | 5 |
4 | Germany | E | 18 | 8 |
Italy | E | 18 | 6 | |
5 | Brazil | SA | 20 | 7 |
ডাটাফ্রেমের শেষ এখানেই না, আরও আলোচনা হবে পরবর্তী পোস্টগুলতে। শেষ করার আগে দেখে নেই কি কি ডিসকাস হল এখানে
আগামীতে আমি ডাটা কালেকশন নিয়ে কথা বলবত কিছু, আরও কিছু কথা বলব পিভট নিয়ে হয়ত। আর কথা বলব গ্রউপিং নিয়ে। তো দেখা হবে আগামীকাল।