صفحه اصلی / پایتون / پیش‌پردازش داده‌ها با پایتون

پیش‌پردازش داده‌ها با پایتون

معمولا مجموعه‌داده‌های یادگیری ماشینی، دارای داده‌های خام هستند. در این مجموعه‌داده‌ها، داده‌های رشته‌ای و عددی در کنار هم هستند. برخی از این داده‌ها، اطلاعات مفیدی ندارند. برخی دیگر، مقیاس‌های متفاوتی دارند. وجود این‌گونه مسائل، باعث می‌شود که قبل از استفاده از داده‌های خام، آن‌ها را پیش پردازش کنیم تا به فرمت مناسب برای الگوریتم‌های یادگیری ماشینی در بیایند. هرچند پیش‌پردازش داده‌ها بحث مفصلی است و نیاز به مطالعه و تمرین زیادی دارد، اما نکاتی که در این مقاله گفته می‌شود برای اکثر کاربرد‌ها، کافی است.

روش‌های توضیح داده شده در این مقاله بر روی یک مجموعه داده نمونه اعمال شده‌اند. فایل کد و همه فایل‌های استفاده شده و ایجاد شده در آن از طریق لینک‌های زیر قابل دریافت است.

1. معرفی ابزارها

برای پردازش داده‌های جدولی در پایتون، دو کتابخانه بسیار مفید وجود دارد. اولین کتابخانه Pandas است. این کتابخانه، به تنهایی بسیاری از نیازمندی‌های ما را بر طرف می‌کند. کتابخانه دوم، کتابخانه مشهور scikit-learn است. برای برخی از پردازش‌ها ما از این کتابخانه استفاده می‌کنیم. برای استفاده از Pandas در پردازش‌ها، از کد زیر استفاده می‌کنیم.

import pandas as pd

علاوه بر آن، در این مقاله فرض می‌کنیم که شما این کد‌ها را در محیط jupyter notebook (مانند google colab) اجرا می‌کنید.

2. خواندن داده‌ها

برای خواندن داده‌ها از تابع read_csv موجود در Pandas استفاده می‌کنیم. ورودی این تابع، مسیر فایل csv است. خروجی آن یک DataFrame است که اطلاعات فایل csv در آن موجود است.

data = pd.read_csv("drive/MyDrive/csv/data.csv");

3. نمایش داده‌ها

برای نمایش داده‌ها، می‌توانیم متغیر data را چاپ کنیم. همچنین با کمک تابع head می‌توانیم چند سطر اول از data را چاپ کنیم، برای چاپ کردن چند سطر آخر از تابع tail کمک می‌گیریم. برای چاپ کردن یک (و یا چند) متغیر از تابع print استفاده می‌کنیم. توجه داشته باشید که در notebook، نیازی به نوشتن تابع print نیست. اگر در یک خط، نام متغیر را بنویسیم، آن متغیر چاپ می‌شود. برای دسترسی به یک ستون، در کنار متغیر، نام ستون را در داخل کروشه قرار می‌دهیم. توابع head و tail بر روی ستون‌ها هم اعمال می‌شوند.

print(data)
# or
data

data.head(10)

data.tail(3)

print(data["col"], 
      data["col"].head(2), 
      data["col"].tail(3))

4. بررسی ویژگی‌های ستون‌ها

برای بررسی اولیه ستون‌ها، از دو تابع info و describe استفاده می‌کنیم. تابع info تعداد کل سطر‌ها و ستون‌ها را نشان می‌دهند و برای هر ستون، نوع داده آن را مشخص می‌کند. نکته مهم دیگر در این تابع، مشخص کردن تعداد داده‌های غیر null در هر ستون است. اگر تعداد داده‌های غیر null کمتر از تعداد سطر‌ها باشد، یعنی در آن ستون، داده null وجود دارد.

data.info()

تابع describe ویژگی‌های آماری اولیه ستون‌ها را نشان می‌دهد. ویژگکی count تعداد داده‌های غیر null را نشان‌ می‌دهد. برای ستون‌های رشته‌ای، ویژگی‌های unique و top و freq ارائه شده است. unique نشان‌دهنده تعداد داده‌های یکتا است. اگر این عدد با count برابر باشد، یعنی همه داده ها با هم فرق دارند. اگر تعداد داده‌های یکتا خیلی کم باشد، احتمالا داده‌ها از نوع دسته‌بندی هستند. top نشان‌دهنده داده‌ای است که بیشترین تکرار را داشته است و freq، تعداد تکرار آن داده را مشخص می‌کند.

برای ستون‌های عددی، اطلاعات بیشتری نشان داده می‌شود. مثلا، mean، نشان دهنده میانگین، std، نشان دهنده انحراف از معیار، min، نشان دهنده کمترین مقدار و max، نشان دهنده بیشترین مقدار است.

تابع describe را می‌توان بر روی یک ستون هم اعمال کرد.

data.describe(include="all")

4.1. تکرار مقادیر

در برخی از موارد، مقادیر موجود در یک ستون تکرار می‌شوند، با استفاده از تابع value_counts می‌توانیم مقادیر و تعداد تکرار آن‌ها را ببینیم. برای مشاهده نمواری، از تابع plot بر روی خروجی value_counts استفاده می‌کنیم.

print(data['col'].value_counts())
data["col"].value_counts().plot(kind="bar")

5. پردازش ستون‌های رشته‌ای

از آنجایی که روش‌های یادگیری ماشینی با عدد کار می‌کنند، لازم است که ستون‌های رشته‌ای به ستون‌های عددی تبدیل شوند. البته در برخی از موارد، یک ستون رشته‌ای دارای اطلاعات خاصی نیست که در اینصورت باید حذف بشود. مورد دیگر، زمانی است که یک مقدار در ستون رشته‌ای وارد نشده است. این موارد در این بخش بررسی می‌شوند.

5.1. داده‌های بدون اطلاعات

در برخی از موارد، یک ستون رشته ای دارای اطلاعات خاصی نیست. در اینصورت باید این ستون از داده‌ها حذف بشود. اینکه یک ستون رشته‌ای دارای اطلاعات هست یا نه بستگی به مجموعه داده دارد. اگر داده های یکتای ستون رشته‌ای برابر با تعداد داده‌های آن ستون باشد، می‌تواند نشان دهنده آن باشد که داده‌های این ستون ارزشی ندارد. برای حذف یک ستون رشته ای از دستور‌های زیر می‌توانیم استفاده کنیم.

data.drop("col_name", axis=1, inplace=True)
# or
data.pop("col_name")

برای به دست آوردن ستون‌های عددی که فقط یک مقدار داده در آن‌ها وجود دارد، از کد زیر می‌توان استفاده کرد:

maxMin = data.max() - data.min()
zeroMaxMin = maxMin[maxMin==0]
zeroMaxMin

5.2. مواجهه با مقادیر غیر موجود و نامشخص

مقادیر غیر موجود، به صورت NaN خود را نشان می‌دهند. در مواجهه با این داده‌ها دو اقدام به صورت کلی انجام می‌شود: ۱) حذف داده‌های NaN و ۲) استفاده و جایگزینی این داده‌ها. حذف داده‌های NaN در صورتی است که این داده‌ها و یا سطر و ستون این داده‌ها، اطلاعات مفیدی نداشته باشد. برای حذف ستونی که داده NaN دارد، می‌توانیم از توابع drop و pop مثل قبل استفاده کنیم. برای حذف سطری که دارای NaN است، از dropna استفاده می‌کنیم. در این تابع باید مشخص کنیم که در صورت وجود NaN در چه ستون‌هایی، آن سطر حذف بشود.

data.dropna(subset=["col_name"])

اگر نخواهیم داده‌‌ی NaN را حذف کنیم، باید آن را با یک مقدار مجاز مربوط به آن ستون جایگزین کنیم. برای ستون‌های رشته‌ای، بهترین کار این است که یک مقدار جدید با نام مثلا missing ایجاد کنیم و در آن ستون، به جای NaN از این مقدار جدید استفاده کنیم.

data['col'].fillna('missing', inplace=True)

5.3. داده‌های دسته‌بندی

برای تبدیل داده‌های دسته‌بندی به داده‌های عددی، می‌توانیم از تبدیل one-hot استفاده کنیم. در این تبدیل، به تعداد دسته‌بندی، ستون جدید ایجاد می‌کنیم. همه مقادیر ستون‌های جدید صفر هستند، به جر مقدار یک ستون که یک است. بعد از تبدیل one-hot معمولا ستون دسته‌بندی حذف می‌شود. البته در صورتی که لازم باشد ساخت داده های آزمون بر اساس همان ستون دسته‌بندی باشد، باید آن ستون دسته بندی را در یک متغیر ذخیره کرد.

newColumns = pd.get_dummies(data['cat_col'], prefix="cat_col")

data = pd.concat([data, newColumns], axis=1)

oldCategoryColumn = data.pop('cat_col')

6. تقسیم داده‌ها به بخش یادگیری و آزمون

یکی از کارهای اولیه در یادگیری ماشین، تقسیم داده‌ها به بخش‌های یادگیری یا train و آزمون یا test است. هدف از اینکار این است که نشت اطلاعات از بخش آزمون به بخش یادگیری وجود نداشته باشد. البته تغییراتی در بخش یادگیری اعمال می‌شود، باید در بخش آزمون هم اعمال شود. نکته ای که وجود دارد این است که این تغییرات با توجه به داده‌های بخش یادگیری هستند و نه داده‌های بخش آزمون. ممکن است این سوال پیش بیاید که چرا ما تقسیم داده‌ها را قبل از پردازش ستون‌های رشته‌ای انجام ندادیم. جواب این است که تغییراتی که ما در این بخش اعمال کردیم، بین داده های یادگیری و آزمون مشترک است. در مورد ستون‌های عددی به این صورت نیست. هرچند تصمیم در این رابطه با شما به عنوان تحلیل‌گر داده است.

تقسیم داده‌ها به بخش یادگیری و آزمون باید به صورتی باشد که داده های آزمون، نمونه‌ای از داده‌های اصلی باشد. مثلا اگر داده‌های اصلی مربوط به سه دسته بندی هستند، داده‌های آزمون هم باید مربوط به همان سه دسته بندی باشند. اگر داده‌های اصلی دسته بندی خاصی ندارند، داده های آزمون را می‌توان به صورت تصادفی استخراج کرد. یک تابع خیلی خوب که داده‌ها را به صورت تصادفی استخراج می‌کند، تابع train_test_split از پکیج sklearn.model_selection است. سه پارامتر اصلی این تابع عبارتند از: ۱) داده‌ای که باید تقسیم شود، ۲) مقدار داده آزمون نسبت به کل داده‌ها، و ۳) seed اولیه برای تولید اعداد تصادفی. پارامتر‌های اول و دوم واضحند، پارامتر سوم، برای این است که هر بار اجرای این تابع، خروجی های متفاوتی تولید نکند. خروجی این تابع، دو برابر تعداد داده‌هایی است که باید تقسیم شوندودر اینجا ما یک داده داریم، پس تعداد خروجی‌های آن، دو مورد است.

from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(data, 
   test_size=0.2, random_state=23)

اگر داده‌ها دسته بندی دارند و باید داده‌ها آزمون هم همان دسته‌بندی‌ها را داشته باشند، از ابزار StratifiedShuffleSplit موجود در sklearn.model_selection استفاده می‌کنیم. نحوه استفاده از این ابزار در قطعه کد زیر نشان داده شده است. در این کد n_splits نشان دهنده تعداد جدا کنند‌ها است که برای مثال ما، برابر است با یک. test_size نیز نسبت داده‌های آزمون به کل داده‌ها را نشان می‌دهد. در کد پایین، categoryColumn ستونی است که داده های دسته‌بندی در آن قرار دارد.

from sklearn.model_selection import StratifiedShuffleSplit

split = StratifiedShuffleSplit(n_splits=1, 
   test_size=0.2, random_state=12)

for train_index, test_index in split.split(data, categoryColumn):
  train_set = data.loc[train_index]
  test_set = data.loc[test_index]

6.1. اعمال تبدیل‌ها بر روی داده‌های آزمون

در برخی از تبدیل‌های ستون‌های عددی، نیاز به استخراج برخی داده‌های آماری از مجموعه یادگیری را داریم. ما باید این آمار‌ها را ذخیره کنیم و سپس با همین آمار‌ها، تبدیل‌های مورد نظر را بر روی داد‌های آزمون اعمال کنیم. مثلا برای نرمال سازی ستون col در داده‌های آزمون به صورت زیر عمل می کنیم. توجه کنید که مقادیر minValueOfCol و maxValueOfCol از روی داده‌های یادگیری استخراج شده‌اند.

minValueOfCol = train_set['col'].min()
maxValueOfCol = train_set['col'].max()

#train_set['col'] = (train_set['col'] - minValueOfCol) / (maxValueOfCol-minValueOfCol)
train_set.loc[:,'col'] = (train_set['col'] - minValueOfCol) / (maxValueOfCol-minValueOfCol)
#test_set['col'] = (test_set['col'] - minValueOfCol) / (maxValueOfCol-minValueOfCol)
test_set.loc[:,'col'] = (test_set['col'] - minValueOfCol) / (maxValueOfCol-minValueOfCol)

7. پردازش ستون‌های عددی

برای ستون‌های عددی، ابتدا باید داده‌های NaN را حذف کنیم. پس از آن، بسته به الگوریتم یادگیری ماشین، نرمال‌سازی و یا استاندارد سازی را انجام می‌دهیم. مورد دیگر، مواجهه با داده های دسته بندی است که به صورت عددی خود را نشان می‌دهند. در موارد خاص،می‌توان یک تابع را بر مقادیر یک ستون اعمال کرد تا ستون جدیدی ایجاد شود.

دقت کنید که ما این تغییرات را بر روی داده‌های یادگیری اعمال می‌کنیم و همین تغییرات را بر روی داده‌های آزمون هم تکرار می‌کنیم.

7.1. مواجهه با مقادیر غیر موجود و نامشخص

رفتار ما در مواجهه با مقادیر غیر موجود در ستون‌های عددی، مثل رفتاری است که با این مقادیر در ستون های رشته ای داشتیم. در هر دو مورد، اگر بخواهیم می‌توانیم داده را حذف کنیم و یا اینکه از آن استفاده کنیم. تنها تفاوت در حالتی است که می‌خواهیم از داده استفاده کنیم و به مقادیر NaN آن ستون، مقدار اولیه بدهیم. اما قبل از آن بهتر است که یک ستون جدید ایجاد کنیم و جایگاه مربوط به NaN ها را در آن مشخص کنیم. برای اینکار از کد زیر می‌توانیم استفاده کنیم.

train_set["col_nan"] = train_set['col'].apply(
   lambda v: 1 if pd.isnull(v) else 0)
test_set["col_nan"] = test_set['col'].apply(
   lambda v: 1 if pd.isnull(v) else 0)

پس از ایجاد این ستون جدید، می‌توانیم مقدار NaN را با یک مقدار مجاز جایگزین کنیم.در این حالت، چند استراتژی برای تعیین مقدار جایگزین وجود دارد. جایگزینی مقدار NaN با میانکین و یا داده پرتکرار، دو نمونه از این استراتژی‌ها است. حالت دیگر این است که آن را با یک عدد ثابت مثلا کوچکترین عدد، منهای یک جایگزین کنیم. برای اینکار از کد زیر استفاده می‌کنیم. توجه داشته باشید که باید متغیر minValues را ذخیره کنیم تا بتوانیم همین تبدیل را بر روی داده‌های آزمون هم اعمال کنیم.

minValues = train_set.min()
train_set.fillna(minValues - 1, inplace=True)
test_set.fillna(minValues - 1, inplace=True)

7.2. نرمال‌سازی

برخی از روش‌های یادگیری ماشینی مانند شبکه‌های عصبی، بر روی داده های بین صفر تا یک بهتر عمل می‌کنند. نرمال‌سازی، زمانی اتفاق می‌افتد که ما می‌خواهیم داده‌های ستون بین عدد 0 تا 1 باشند. برای نرمال سازی مقادیر یک ستون، باید مقدار هر خانه ستون را از کمترین مقدار ستون کم کرد و بر تفاضل بیشترین و کمترین مقدار ستون تقسیم کرد. کد زیر این مورد را نشان می‌دهد. در اینجا هم باید مقادیر تعریف شده را برای اعمال تبدیل بر داده‌های آزمون ذخیره کنیم.

minValueOfCol = train_set['col'].min()
maxValueOfCol = train_set['col'].max()

#train_set['col'] = (train_set['col'] - minValueOfCol) / (maxValueOfCol-minValueOfCol)
train_set.loc[:,'col'] = (train_set['col'] - minValueOfCol) / (maxValueOfCol-minValueOfCol)
#test_set['col'] = (test_set['col'] - minValueOfCol) / (maxValueOfCol-minValueOfCol)
test_set.loc[:,'col'] = (test_set['col'] - minValueOfCol) / (maxValueOfCol-minValueOfCol)

7.3. استاندارد سازی

استاندارد سازی، تبدیلی است که در آن، مانگین اعداد به 0 و انحراف از معیار به 1 تغییر می‌یابد. این تبدیل معمولا برای الگوریتم‌های یادگیری ماشین مانند PCA که به انحراف از معیار وابسته هستند اعمال می‌شود.

meanValueOfCol = train_set['col'].mean()
stdValueOfCol = train_set['col'].std()

#train_set['col'] = (train_set['col'] - meanValueOfCol) / stdValueOfCol
train_set.loc[:,'col'] = (train_set['col'] - meanValueOfCol) / stdValueOfCol

#test_set['col'] = (test_set['col'] - meanValueOfCol) / stdValueOfCol
test_set.loc[:,'col'] = (test_set['col'] - meanValueOfCol) / stdValueOfCol

7.4. داده‌های دسته‌بندی

ممکن است که ما مقادیر عدی داشته باشیم که این مقادیر، نشان دهنده دسته‌بندی باشند. در این صورت، همان تبدیل مربوط به ستون‌های رشته ای دسته بندی را اعمال می‌کنیم. بهتر است این تبدیل، قبل از جدا سازی داده‌های یادگیری و آزمون باشد.

7.5. اعمال تابع بر روی مقادیر ستون

درصورتی که بخواهیم تغییری بر روی تک تک مقادیر یک ستون انجام بدهیم، می‌توانیم از این تکنیک استفاده کنیم. در تکنیک، یک تابع می‌نویسیم که یک پارامتر می‌گیرد و یک خروجی دارد. این تابع با کمک apply بر روی مقادیر ستون اعمال می‌شود و یک ستون جدید ایجاد می‌کند.

def normalizeV(value):
  if value > 0 :
    return 1
  else:
    return 0

#train_set['col'] = train_set['col'].apply(normalizeV)
train_set.loc[:,'col'] = train_set['col'].apply(normalizeV)
#test_set['col'] = test_set['col'].apply(normalizeV)
test_set.loc[:,'col'] = test_set['col'].apply(normalizeV)

8. جدا سازی ویژگی‌ها از پارامتر هدف

فرض کنی که ستون tg، همان ستون هدف شما باشد. با استفاده از کد زیر، می‌توانیم ویژگی‌ها و پارامتر هدف را از همدیگر جدا کنیم.

test_target = test_set.pop("tg")
test_features = test_set

train_target = train_set.pop("tg")
train_features = train_set

9. ذخیره مجموعه داده‌های یادگیری و آزمون

برای ذخیره داده ها از تابع to_csv استفاده می‌کنیم.

test_target.to_csv("drive/MyDrive/csv/test-target.csv",
   header='tg', index=False)
test_features.to_csv("drive/MyDrive/csv/test-features.csv", 
   index=False)

train_target.to_csv("drive/MyDrive/csv/train-target.csv", 
   header='tg', index=False)
train_features.to_csv("drive/MyDrive/csv/train-features.csv", 
   index=False)

10. جمع‌بندی

پیش‌پردازش داده‌ها، یکی از گام‌های مهم برای بررسی روش‌های یادگیری ماشینی است. در این مقاله ما چندین نوع پیش‌پردازش برای پردازش ستون‌های عددی و رشته‌ای را توضیح دادیم. این موارد هر چند کامل نیستند، اما برای اکثر کاربرد‌ها کافی هستند. در نهایت برای بررسی دقیق‌تر می‌توانید کد‌ و فایل‌های استفاده شده و پردازش شده را از طریق لینک‌های زیر دریافت کنید:

ارسال دیدگاه