مدیریت missing value

در مواردی ممکن است دیتایی داشته باشیم که دارای مقادیر ناقص باشد که اصطلاحا به آن missing values می‌گوییم. در ادامه این پست بلاگ می‌خواهیم نحوه مدیریت این پدیده را بررسی کنیم.

در پایان این نوشته قادر خواهیم بود مقادر از دست رفته یا missing values را مدیریت کنیم. فرمت داده صحیحی انتخاب کنیم و همچنین داده‌های خود را استاندارد و نرمال‌سازی کنیم.

هدف از data cleaning تغییر وضعیت دیتای اولیه به حالتی است که بتوان آن را بهتر آنالیز نمود.

فراخوانی

در ابتدا کتابخانه‌های مورد نظر را فراخوانی می‌کنیم:

import pandas as pd
import numpy as np
import seaborn as sns 
import matplotlib.pyplot as plt
%matplotlib inline

با دستور زیر دیتا را می‌خوانیم:

df = pd.read_csv('missed.txt') 

EDA

حال با دستورhead یه نگاه اجمالی به داده‌های خود می‌اندازیم و همچنین با describe مقادیر مرتبط با میانگین و مد و … را در مورد دیتای خود می‌بینیم و با دستور info نوع داده و تعداد مقادیر ناموجود را مشاهده می‌کنیم:

 df.head() 
df.describe()
df.info() 

حال با دستور زیر مقادیر یکتای هر ستون از دیتا را مشاهده خواهیم کرد:

 () df['normalized-losses'].unique

حال اگر بخواهیم مقادیر یکتای تمام ستون‌ها را مشاهده کنیم، از حلقه استفاده می‌کنیم:

 for i in headers:
    print('-----------')
    print(i)
    print(df[i].unique()) 

با اجرای حلقه فوق مشاهده می‌کنیم که در برخی از ستون‌ها مقادیر نامشخص با علامت ? وجود دارد. این مقادیر همان مقادیرmissing value هستند که ممکن است روند تحلیل داده ما را تحت تاثیر قرار دهند. با هدف افزایش سرعت محاسبات، در ابتدا مقادیر ? را با nan null جایگزین می‌کنیم.

  import numpy as np
df.replace("?", np.nan, inplace = True)
df.head(5)  
df.info()

مدیریت Missing values:

یکی از راه‌های مدیریت missing values حذف مقادیر است که به طرق زیر امکان‌پذیر می‌باشد:

  • . Drop the whole row
  • . Drop the whole column

جایگذاری روش دیگری است که ممکن است یکی از راه‌های زیر به کار رود:

  • . Replace it by mean -یعنی جایگزاری با مقدار میانگین
  • . Replace it by frequency
  • جایگذاری متناسب با تکرار؛ مثلا اینکه پر تکرار‌ترین داده را بعنوان مقدار جایگزین انتخاب کنیم.
  • . Replace it based on other functions –

با دستور زیر می‌بینیم که سطرهایی که مقادیر صفر دارند، حذف می‌شوند. تعداد سطرهای باقی مانده 159 سطر است. با توجه به اینکه در ابتدا 205 سطر داشته‌ایم، متوجه می‌شویم که تعداد 46 سطر ما حذف شده است و این درصد زیادی از داده‌ها را در بر می‌گیرد(حدود 25 درصد داده‌های ما حذف شده است).

df.dropna(axis=0)

در دستور بعدی ستون‌هایی که مقادیر صفر دارند را حذف می‌کنیم و می‌بینیم که تنها 19 ستون باقی می‌ماند:

df.dropna(axis=1)

بنابراین حذف داده، همیشه روش مناسبی برای مدیریت داده‌های گم شده نیست.

اگر مقادیر عددی باشند، یکی از روش‌های مناسب برای مدیریت داده‌های گم شده، جایگذاری با مقدار میانگین است. برای مثال با وارد کردن دستور df.info() می‌بینیم که ستون موسوم به normalised-losses تنها دارای 164 مقدار است و تعداد 205-164 مقدار آن وجود ندارد.
با فرض اینکه در مورد این ردیف دانش داریم و می‌دانیم که نوع داده آن از نوع عددی هستند، اقدام به جایگذاری آن با مقادیر میانگین می‌کنیم. اما ابتدا باید نوع داده این ردیف را از نوع object به float تبدیل کنیم. (به دلیل اینکه داده‌ها از منابع مختلف جمع‌آوری شده است، نوع داده در حالت obj ذخیره شده است. اما می‌دانیم که داده‌های این ستون عدد هستند. پس باید به نوع int یا float تبدیل گردد.) از متود astype استفاده می‌کنیم.

میانگین:

در خط دوم کد زیر از داده‌ها میانگین می‌گیریم:

avg_norm_loss_float =df["normalized-losses"].astype("float")
avg_norm_loss= avg_norm_loss_float.mean()
print('avg_norm_loss=',avg_norm_loss) 

حال با دستور زیر مقادیر nan را با مقادیر میانگین جایگذاری می‌کنیم:

df["normalized-losses"].replace\
(np.nan, avg_norm_loss, inplace=True)

بعد از دستور بالا اگر df.info بگیریم، می‌بینیم که تعداد مقادیر Non-nul عدد کامل 205 را نشان می‌دهد و این یعنی مقادیر جایگزین شده است و این یعنی دیگر خیالمان بابت ستون normalized-losses راحت شده است.

این کار را برای سایر مقادیر نیز انجام می‌دهیم(بعنوان تمرین):

ave_stroke = df['stroke'].astype('float').mean(axis=0)
print("Average stroke:", ave_stroke)
---
avg_bore=df['bore'].astype('float').mean(axis=0)
print("Average of bore:", avg_bore)
---
ave_horsepower = df['horsepower'].astype('float').mean(axis=0)
print("Average horsepower:", ave_horsepower)
---
ave_peak_rpm = df['peak-rpm'].astype('float').mean(axis=0)
print("Average peak_rpm:", ave_peak_rpm)
---

حال با دستورات زیر مقادیر میانگین بدست آمده را جایگزین می‌کنیم:

df["stroke"].replace(np.nan,    ave_stroke, inplace=True)
df["bore"].replace(np.nan,      avg_bore, inplace=True)
df["horsepower"].replace(np.nan,ave_horsepower, inplace=True)
df["peak-rpm"].replace(np.nan,  ave_peak_rpm, inplace=True)

حال به سراغ دیتا‌های دسته‌بندی شده می‌رویم. با دستور زیر می‌خواهیم ببینیم از هر دسته چه تعداد وجود دارد:

df['num-of-doors'].value_counts()


از جواب کد بالا مشخص است که ماشین‌های 4 درب رایج‌ترین خودروها هستند.خودروهای چهار درب 114 تا هستند و دو درب‌ها 89 تا . پس جاهای خالی را با چهار پر می‌کنیم. همچنین با دستور زیر می‌توان تعداد مقادیر پرتکرار را پیدا کرد و مقادیر nan را با آن جایگذاری کرد:

df['num-of-doors'].value_counts().idxmax()
df["num-of-doors"].replace(np.nan, "four", inplace=True)


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

# simply drop whole row with NaN in "price" column
df.dropna(subset=["price"], axis=0, inplace=True)

# reset index, because we droped two rows
df.reset_index(drop=True, inplace=True)
df.head()
df.info()


حالا دیتایی داریم که هیچگونه missing values نداشته و برای آنالیز کردن آماده هستند.

فرمت داده صحیح:

آخرین مرحله از data cleaning بررسی این مورد است که نوع داده ما در حالت صحیحی باشند. در کتابخانه پانداس از دو دستور زیر برای تشخیص و تغییر نوع داده استفاده می‌شود:

dtype : برای چک کردن نوع داده

astype : برای تغییر نوع داده

با دستور زیر نوع داده هر ستون را مشخص می‌کنیم:

df.dtypes

همانگونه که می‌بینیم، نوع برخی از داده‌ها در حالت صحیحی قرار ندارند. برای مثال داده‌های عددی باید در حالت float یا Int باشند. و یا اینکه داده‌های categorical باید در حالت object باشند. اما برای مثال دو ستون bore وstroke که مربوط به engine هستند، انتظار می‌رود که داده‌های عددی باشند، اما می‌بینیم که به صورت object نمایش داده شده‌اند. در این چنین مواردی ما باید با استفاده از دستور astype نوع داده را به فرمت صحیح تبدیل کنیم:

df[["bore", "stroke","peak-rpm","price"]] = df[["bore", "stroke","peak-rpm","price"]].astype("float")
df[["normalized-losses"]] = df[["normalized-losses"]].astype("int")

سپس مجددا نوع داده‌ها را بررسی می‌کنیم:

df.dtypes

در نتیجه دستور بالا مشاهده می‌کنیم که همه داده‌ها در فرمت صحیح قرار دارند.

Data Standardization

دیتاها معمولا از منابع متعددی بدست می‌آیند. استاندارد‌سازی داده نوعی از data normalization است و برای رسیدن به داده‌های استاندارد باید اختلاف داده‌ها از مقدار میانگین را بدست بیاوریم و بر standard deviation تقسیم کنیم.

What is standardization

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

Data Normalization

نرمال سازی داده چیست؟

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

مثال:

فرض کنیم که می‌خواهیم مقادیر ستون‌های length ,width و height را تغییر مقیاس دهیم:

Target: would like to normalize those variables so their value ranges from 0 to 1

Approach: replace original value by (original value)/(maximum value)

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

# replace (original value) by (original value)/(maximum value)
df['length'] = (df['length']-df['length'].min())/(df['length'].max()-df['length'].min())
df['width'] = (df['width']-df['width'].min())/(df['width'].max()-df['width'].min())
--> df['width'] 
--> df['width']>1
-->set(list(df['width']>1))



همانطور که مشاهده می‌شود، توانستیم داده‌های مرتبط با طول و عرض را نرمال‌سازی کنیم.

Binning

پروسه‌ای که طی آن متغیر‌های پیوسته را به متغیر‌های گسسته تبدیل می‌کنیم، binning گویند. مثال:

در داده‌های ما ستون horsepower در بازه 48 تا 288 قرار می‌گیرد و جمعا 59 متغیر یکتا دارد. اگر بخواهیم این تعداد داده‌ها را در سه دسته اسب بخار کم، اسب بخار متوسط و اسب بخار بالا دسته‌بندی کنیم از روش Binning استفاده می‌کنیم. برای این هدف از متود cut از کتابخانه pandas استفاده می‌کنیم.

df["horsepower"]=df["horsepower"].astype(int, copy=True)
sns.distplot(df["horsepower"],kde=False,bins=3)

-->bins = np.linspace(min(df["horsepower"]), max(df["horsepower"]), 4)
bins

--> group_names = ['Low', 'Medium', 'High']


از متد cut استفاده می‌کنیم که ببینیم هر داده از df[‘horsepower’] در کدام دسته قرار می‌گیرد:

df['horsepower-binned'] = pd.cut(df['horsepower'], bins, labels=group_names)
df[['horsepower','horsepower-binned']].head(20)

حال با دستور زیر می‌خواهیم ببینیم که در هر دسته چه تعداد خودرو قرار دارد:

df["horsepower-binned"].value_counts()


و با استفاده از دستور زیر میزان پراکندگی را ترسیم می‌کنیم:

sns.countplot(x='horsepower-binned',data=df)


Indicator Variable (or Dummy Variable)

متغیر‌های عددی که برای برچسب‌گذاری دسته‌ها استفاده می‌شود، را indicator variables یا dummy variableگویند (متغیر دامی). کلمه dummy به معنای ساختگی یا مصنوعی است. دلیل این نام‌گذاری این است که آنها به تنهایی هیچ معنایی ندارند. در این روش نمی‌توان گفت که داده‌های نوع numerical را به categorical تبدیل کرده‌ایم. بلکه می‌توانیم بگوییم برای پیش بردن اهداف محاسبات و تحلیلی، یک عدد را به هر یک از دسته‌های داده‌های categorical نسبت داده‌ایم.

-->df.tail()
-->dummy_variable_1 = pd.get_dummies(df["fuel-type"])
dummy_variable_1.tail()

تا اینجا کار Data Cleaning تا حد خوبی پیش رفت و در نهایت داده‌های تمیز شده را در یک متغیر مستقل ذخیره می‌کنیم:

df.to_csv('clean_df.csv')

منبع:

جلسه هفتم از کانال یوتیوب مونا حاتمی لینک +

گیت هاب monogram +

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد.