آمار، داده کاوی ۱۲۷۰ بازدید

بسیاری از تحلیل‌های آماری، با وجود نقاط دور افتاده (Outlier) یا ناهنجار (Anomaly)، دچار مشکل شده و نتایج صحیحی نخواهند داشت. به همین دلیل شناسایی چنین نقاطی، اهمیت زیادی برای «متخصصین داده» (Data Scientist) دارد. در این نوشتار روش و راه کارهایی را برای شناسایی ناهنجاری با پایتون ارائه خواهیم کرد. در این بین از الگوریتم‌هایی که در کتابخانه‌های پایتون وجود دارد نیز استفاده می‌کنیم.

به منظور آشنایی با الگوریتم های شناسایی نقاط ناهنجار، بهتر است ابتدا نوشتار الگوریتم عامل دورافتاده محلی (Local Outlier Factor) جهت شناسایی داده های پرت — به زبان ساده و شناسایی داده پرت در SPSS — راهنمای کاربردی را به عنوان مقدمه بخوانید. همچنین خواندن مطالب آشنایی با خوشه‌بندی (Clustering) و شیوه‌های مختلف آن و تشخیص ناهنجاری (Anomaly Detection) — به زبان ساده نیز خالی از لطف نیست.

شناسایی ناهنجاری با پایتون

یکی از روش‌های تشخیص نقاط یا مشاهدات خارج از انتظار در یک مجموعه داده، شناسایی ناهنجاری (Anomaly Detection) است. چنین مشاهداتی معمولا از حدود عادی یا نرمال خارج هستند. شناسایی ناهنجاری معمولا روی داده‌های بدون برچسب (ٔNo-Label) صورت می‌گیرد و از این لحاظ می‌توان تکنیک‌های شناسایی ناهنجاری را در گروه روش‌های «غیر نظارتی» (Unsupervised) مانند «تکنیک‌های خوشه‌بندی» (Clustering) قرار داد.

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

  • نقاط ناهنجار، به ندرت رخ می دهند در نتیجه تعداد یا فراوانی آن‌ها در مجموعه داده بسیار کم است.
  • ویژگی‌های نقاط ناهنجار نسبت به دیگر مقادیر، به طور معنی داری، متفاوت است.

شناسایی ناهنجاری و نقاط پرت (Outlier) را در این نوشتار به دو بخش تقسیم می‌کنیم. زیرا هر یک با استفاده از تکنیک‌های جداگانه‌ای اجرا می‌شوند. در بخش اول، در مجموعه داده‌هایی خاص (مربوط به فروشگاه زنجیره‌ای SuperStore) با مقادیری به صورت تک متغیری یا یک بعدی به دنبال مشاهدات ناهنجار می گردیم. ولی در بخش دوم این کار را به کمک داده‌هایی چند بعدی از همان مجموعه داده، عملیاتی کرده و تکنیک های شناسایی ناهنجاری را مورد بررسی قرار خواهیم داد.

شناسایی ناهنجاری مشاهدات تک متغیره

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

در اینجا هدف پیدا کردن الگویی است که به واسطه آن بتوانیم نقاط ناهنجار را برای متغیرهای فروش (Sales) و سود (Profit) پیدا کنیم. همانطور که گفته شد این عمل در حقیقت روی مجموعه داده به صورت تک متغیره صورت می‌گیرد.

ابتدا کدهای پایتون را برای آماده سازی ابزارها، اجرا می‌کنیم.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib
from sklearn.ensemble import IsolationForest

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

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

df = pd.read_excel("Superstore.xls")
df['Sales'].describe()

نتیجه کار به صورت زیر خواهد بود. همانطور که مشاهده می‌کنید، دستور describe شاخص‌های آماری را محاسبه کرده و نمایش می‌دهد.

descirptive statistics
تصویر 1: خروجی دستور محاسبات آمار توصیفی فروش

همچنین محاسبه ضریب چولگی (Skewness) و کشیدگی (Kurtosis) نیز برای نمایش رفتار داده‌ها مناسب است. این شاخص‌ها میزان انحراف از تقارن نسبت به توزیع نرمال را اندازه‌گیری می‌کنند. دستورات زیر به این منظور نوشته شده‌اند.

print("Skewness: %f" % df['Sales'].skew())
print("Kurtosis: %f" % df['Sales'].kurt())

همانطور که از اجرای این دستورات متوجه شده‌اید، ضریب چولگی برابر با 12.97 و ضریب کشیدگی نیز برابر با 305.31 است که دلیل بر وجود چولگی و کشیدگی زیاد است.

این بار نمودار مقادیر را برای مشاهدات ترسیم می‌کنیم. محور افقی در تصویر ۲، نشانگر شماره مشاهده و محور عمودی نیز مقدار فروش را برای هر یک از مشاهدات نشان می‌دهد.

قطعه کدی که در ادامه قرار گرفته است به این کار اختصاص دارد.

plt.scatter(range(df.shape[0]), np.sort(df['Profit'].values))
plt.xlabel('index')
plt.ylabel('Profit')
plt.title("Profit distribution")
sns.despine()

نتیجه اجرای این کد به صورت نموداری مطابق با تصویر ۲، دیده می‌شود.

distibtioni individuals
تصویر ۲: نمودار مقادیر فروش برحسب شماره مشاهده

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

sns.distplot(df['Sales'])
plt.title("Distribution of Sales")
sns.despine()

حاصل اجرای این کد در تصویر ۳ دیده می‌شود.

distribution plot
تصویر ۳: نمودار فراوانی فروش برای مجموعه داده‌های فروشگاه‌های SuperStore

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

این بار همین عملیات را برای متغیر میزان سود (Profit) انجام می‌دهیم.

df['Profit'].describe()
distribution plot
تصویر ۴: خروجی دستور محاسبات آمار توصیفی سود
plt.scatter(range(df.shape[0]), np.sort(df['Profit'].values))
plt.xlabel('index')
plt.ylabel('Profit')
plt.title("Profit distribution")
sns.despine()

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

تصویر ۵: نمودار مقادیر سود برحسب شماره مشاهده

برنامه بعدی و نمودار حاصل از آن توزیع یا نمودار فراوانی داده‌ها است.

sns.distplot(df['Profit'])
plt.title("Distribution of Profit")
sns.despine()

هرچند نمودار مربوط به تصویر ۶، متقارن است و به نظر چولگی ندارد ولی برجستگی زیادی را نشان می‌دهد.

تصویر ۶: نمودار فراوانی سود برای مجموعه داده‌های فروشگاه‌های SuperStore

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

print("Skewness: %f" % df['Profit'].skew())
print("Kurtosis: %f" % df['Profit'].kurt())

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

توجه داشته باشید که نقطه مرکزی (میانگین) برابر با 28 واحد است که در مقایسه با بزرگی بقیه مقادیر می‌توان آن را تقریبا صفر در نظر گرفت.

شناسایی ناهنجاری برای متغیر فروش

در این قسمت با معرفی یک الگوریتم برای شناسایی ناهنجاری به نام الگوریتم «جنگل ایزوله» (Isolation Forest) آشنا می‌شویم. این الگوریتم برای هر یک از مشاهدات مقدار امتیاز یا نمره ناهنجاری را محاسبه کرده و براساس آن نقاط ناهنجار را مشخص می‌کند.

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

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

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

این کارها توسط برنامه‌ای که در ادامه مشاهده می‌کنید، صورت خواهد گرفت.

isolation_forest = IsolationForest(n_estimators=100)
isolation_forest.fit(df['Sales'].values.reshape(-1, 1))
xx = np.linspace(df['Sales'].min(), df['Sales'].max(), len(df)).reshape(-1,1)
anomaly_score = isolation_forest.decision_function(xx)
outlier = isolation_forest.predict(xx)
plt.figure(figsize=(10,4))
plt.plot(xx, anomaly_score, label='anomaly score')
plt.fill_between(xx.T[0], np.min(anomaly_score), np.max(anomaly_score), 
                 where=outlier==-1, color='r', 
                 alpha=.4, label='outlier region')
plt.legend()
plt.ylabel('anomaly score')
plt.xlabel('Sales')
plt.show();

نتیجه اجرای این برنامه نموداری است که در تصویر زیر مشاهده می‌کنید. ناحیه صورتی رنگ مربوط به مشاهدات ناهنجار است. به این ترتیب به نظر می‌رسد فروش‌های بزرگتر از 1000 توسط این الگوریتم به نظر ناهنجار می‌رسند.

univariate anomaly detection
تصویر ۷: نواحی مربوط به نقاط هنجار و ناهنجار برای متغیر فروش

منحنی به رنگ آبی در نمودار مربوط به تصویر ۷، نشانگر مقدار امتیاز ناهنجاری هر مشاهده است. مشخص است که مشاهداتی که دارای امتیاز ناهنجاری در بازه 0.1 تا 0.1- هستند، ناهنجار شناسایی نشده‌اند.

برای نمایش اطلاعات یکی از مشتریان ناهنجار (براساس میزان فروش) از دستور زیر کمک می‌گیریم.

df.iloc[10]

نکته: به یاد دارید که در پایتون اندیس از صفر شروع می‌شود. با ذکر شماره ۱۰، مشاهده شماره ۱۱ (Row ID) دیده خواهد شد. به همین علت در تصویر ۸، اطلاعات مشتری شماره ۱۱ دیده می‌شود.

به این ترتیب نتیجه مطابق با تصویر زیر است.

anomaly customer detail
تصویر ۸: اطلاعات مربوط به یک مشتری ناهنجار برحسب فروش

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

شناسایی ناهنجاری برای متغیر سود

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

  • این بار جنگل ایزوله را توسط داده‌های سود (Profit)، آموزش می‌دهیم.
  • مقادیر سود را در یک بردار توسط دستورات کتابخانه Numpy قرار می‌دهیم تا در مدل‌سازی قابل استفاده باشد.
  • برای هر یک از مشاهدات، امتیاز یا نمره ناهنجاری (Anomaly Score) را محاسبه کرده و برای هر خوشه میانگین امتیازات ناهنجاری را در نظر می‌گیریم.
  • هر یک از مشاهدات را بسته به ناهنجار یا هنجار بودن برحسب متغیر سود، طبقه بندی می‌کنیم.
  • نقاطی که ناهنجار یا داده‌های دور افتاده هستند در نواحی مختلف، به صورت متمایز نشان خواهیم داد.

این کارها توسط برنامه‌ای که در ادامه مشاهده می‌کنید، صورت خواهد گرفت.

isolation_forest = IsolationForest(n_estimators=100)
isolation_forest.fit(df['Profit'].values.reshape(-1, 1))
xx = np.linspace(df['Profit'].min(), df['Profit'].max(), len(df)).reshape(-1,1)
anomaly_score = isolation_forest.decision_function(xx)
outlier = isolation_forest.predict(xx)
plt.figure(figsize=(10,4))
plt.plot(xx, anomaly_score, label='anomaly score')
plt.fill_between(xx.T[0], np.min(anomaly_score), np.max(anomaly_score), 
                 where=outlier==-1, color='r', 
                 alpha=.4, label='outlier region')
plt.legend()
plt.ylabel('anomaly score')
plt.xlabel('Profit')
plt.show();

نتیجه مطابق با تصویر زیر خواهد شد.

تصویر ۹: نواحی مربوط به نقاط هنجار و ناهنجار برای متغیر سود

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

بر این اساس همه مقادیر سودی که در فاصله 100- تا 100 قرار دارند، هنجار و بقیه ناهنجار تلقی شده اند. برای مثال به اطلاعات توضیحی مربوط به یک مشاهده ناهنجار توجه کنید.

df.iloc[3]
descrptive statistics -2
تصویر ۱۰: اطلاعات مربوط به یک مشتری ناهنجار برحسب سود

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

df.iloc[1]
anomaly customer detail 2
تصویر ۱۱: اطلاعات مربوط به یک مشتری ناهنجار دیگر برحسب سود

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

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

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

شناسایی ناهنجاری مشاهدات چند متغیره

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

در اینجا به کمک هر دو متغیر فروش (Sales) و سود (Profit) سعی داریم عمل شناسایی ناهنجاری در مجموعه داده فروشگاه‌های SuperStore انجام دهیم.

برای انجام این کار از کتابخانه PyOD در پایتون استفاده کرده که به کمک آن می‌توانیم ترکیبی از روش‌ و مدل‌های شناسایی را به کار بریم.

شناسایی ناهنجاری بر اساس فروش و سود

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

برای مشاهده همبستگی بین مقدار فروش و سود، از یک نمودار نقطه‌ای (Scatter/Dot Plot) استفاده می‌کنیم. نمونه چنین نموداری توسط دستورات و کدهای زیر ایجاد شده است.

sns.regplot(x="Sales", y="Profit", data=df)
sns.despine();

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

scatter dot plot
تصویر ۱۲: نمودار پراکندگی و نمایش رابطه بین دو متغیر سود و فروش

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

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

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

عامل نقاط دورافتاده محلی بر مبنای خوشه‌بندی

یکی از شیوه‌های موثر در شناسایی ناهنجاری در مجموعه داده‌های چند متغیره، استفاده از تکنیک یا محاسبه «عامل نقاط دور افتاده محلی برمبنای خوشه‌بندی» (Cluster-based Local Outlier Factor) است که به اختصار CBLOF نامیده می‌شود.

در این تکنیک، برای هر یک از مشاهدات امتیاز دورافتادگی (Outlier Score) محاسبه شده و مشاهدات ناهنجار بوسیله محاسبه فاصله هر نقطه با مرکز خوشه‌اش مشخص می‌شود.

در  کتابخانه PyOD تکنیک CBLOF پیاده‌سازی شده است. مراحل انجام کار طی فرآیند زیر معرفی شده است.

  • تغییر مقایس مقادیر متغیرهای (فروش و سود) در بازه صفر تا یک.
  • تعیین درصد مورد نظر برای نقاط ناهنجار در داده‌ها که در اینجا یک درصد در نظر گرفته شده است. در کد زیر، این مقدار در متغیر outliers_fraction مشخص شده است.
  • برازش داده‌ها در مدل CBLOF و پیش‌بینی مقادیر حاصل از خوشه‌بندی.
  • با استفاده از محدودیت‌ها (از لحاظ تعداد یا آستانه مورد نظر) نقاط ناهنجار و هنجار از یکدیگر جدا می‌شوند.

کد پایتونی که در ادامه مشاهده می‌کنید، به انجام این کارها اختصاص دارد.

outliers_fraction = 0.01
xx , yy = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))
clf = CBLOF(contamination=outliers_fraction,check_estimator=False, random_state=0)
clf.fit(X)
scores_pred = clf.decision_function(X) * -1
y_pred = clf.predict(X)
n_inliers = len(y_pred) - np.count_nonzero(y_pred)
n_outliers = np.count_nonzero(y_pred == 1)

plt.figure(figsize=(8, 8))

df1 = df
df1['outlier'] = y_pred.tolist()
    
# sales - inlier feature 1,  profit - inlier feature 2
inliers_sales = np.array(df1['Sales'][df1['outlier'] == 0]).reshape(-1,1)
inliers_profit = np.array(df1['Profit'][df1['outlier'] == 0]).reshape(-1,1)
    
# sales - outlier feature 1, profit - outlier feature 2
outliers_sales = df1['Sales'][df1['outlier'] == 1].values.reshape(-1,1)
outliers_profit = df1['Profit'][df1['outlier'] == 1].values.reshape(-1,1)
         
print('OUTLIERS:',n_outliers,'INLIERS:',n_inliers)
threshold = percentile(scores_pred, 100 * outliers_fraction)        
Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) * -1
Z = Z.reshape(xx.shape)

plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), threshold, 7),cmap=plt.cm.Blues_r)
a = plt.contour(xx, yy, Z, levels=[threshold],linewidths=2, colors='red')
plt.contourf(xx, yy, Z, levels=[threshold, Z.max()],colors='orange')
b = plt.scatter(inliers_sales, inliers_profit, c='white',s=20, edgecolor='k')
    
c = plt.scatter(outliers_sales, outliers_profit, c='black',s=20, edgecolor='k')
       
plt.axis('tight')   
plt.legend([a.collections[0], b,c], ['learned decision function', 'inliers','outliers'],
           prop=matplotlib.font_manager.FontProperties(size=20),loc='lower right')      
plt.xlim((0, 1))
plt.ylim((0, 1))
plt.title('Cluster-based Local Outlier Factor (CBLOF)')
plt.show();

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

outlier in inlier detection
تصویر ۱۳: شناسایی نقاط دورافتاده بوسیله روش عامل نقاط دور افتاده برمبنای خوشه‌بندی (CBLOF)

همانطور که مشاهده می‌کنید، ۱۰۰ مشاهده نقطه پرت و ۹۸۹۴ نقطه نیز به هنجار شناسایی شده‌اند. از آنجایی که ۹۹۹۴ مشاهده در کل وجود دارد، حدود ۱٪ آن‌ها (یعنی تقریبا ۹۹ تا ۱۰۰) مشاهده به عنوان کران محدودیت برای تعداد نقاط پرت مشخص شده است.

شناسایی نقاط پرت برمبنای نمودار فراوانی

یکی دیگر از روش‌های شناسایی نقاط پرت و ناهنجاری‌ها، استفاده از تکنیک شناساسی نقاط پرت برمبنای نمودار فراوانی (Histogram-based Outlier Detection) است که به اختصار HBOS نامیده می‌شود.

در این روش، فرض بر این است که متغیرها یا ویژگی‌ها مربوط به مشاهدات نسبت به یکدیگر مستقل هستند. در این بین درجه ناهنجاری بوسیله نمودار فراوانی (Histogram) ایجاد می‌شود. به این ترتیب برای هر یک از متغیرها یک نمودار فراوانی ترسیم شده و امتیاز ناهنجاری به تفکیک متغیرها محاسبه می‌شود. این مقادیر بوسیله روش‌های خاصی برای هر مشاهده با یکدیگر ترکیب می‌شوند تا امتیاز ناهنجاری برای مشاهدات بدست آید.

زمانی که از کتابخانه PyOD استفاده می‌کنید، کدی که در ادامه قابل مشاهده است، برایتان شناسایی ناهنجاری چند متغیره را به روش نمودار فراوانی انجام می‌دهد.

نکته: در اینجا هم درصد مشاهدات ناهنجار یا دورافتاده یک درصد در نظر گرفته شده است.

outliers_fraction = 0.01
xx , yy = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))
clf = HBOS(contamination=outliers_fraction)
clf.fit(X)
scores_pred = clf.decision_function(X) * -1
y_pred = clf.predict(X)
n_inliers = len(y_pred) - np.count_nonzero(y_pred)
n_outliers = np.count_nonzero(y_pred == 1)
plt.figure(figsize=(8, 8))
df1 = df
df1['outlier'] = y_pred.tolist()
    
# sales - inlier feature 1,  profit - inlier feature 2
inliers_sales = np.array(df1['Sales'][df1['outlier'] == 0]).reshape(-1,1)
inliers_profit = np.array(df1['Profit'][df1['outlier'] == 0]).reshape(-1,1)
    
# sales - outlier feature 1, profit - outlier feature 2
outliers_sales = df1['Sales'][df1['outlier'] == 1].values.reshape(-1,1)
outliers_profit = df1['Profit'][df1['outlier'] == 1].values.reshape(-1,1)
         
print('OUTLIERS:',n_outliers,'INLIERS:',n_inliers)
threshold = percentile(scores_pred, 100 * outliers_fraction)=
Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) * -1
Z = Z.reshape(xx.shape)

plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), threshold, 7),cmap=plt.cm.Blues_r)
a = plt.contour(xx, yy, Z, levels=[threshold],linewidths=2, colors='red')
plt.contourf(xx, yy, Z, levels=[threshold, Z.max()],colors='orange')
b = plt.scatter(inliers_sales, inliers_profit, c='white',s=20, edgecolor='k')
    
c = plt.scatter(outliers_sales, outliers_profit, c='black',s=20, edgecolor='k')
       
plt.axis('tight')      
plt.legend([a.collections[0], b,c], ['learned decision function', 'inliers','outliers'],
           prop=matplotlib.font_manager.FontProperties(size=20),loc='lower right')      
plt.xlim((0, 1))
plt.ylim((0, 1))
plt.title('Histogram-base Outlier Detection (HBOS)')
plt.show();

نتیجه اجرای کد، نموداری است که در تصویر زیر مشاهده می‌کنید.

histo method outlier detection
تصویر ۱۴: شناسایی نقاط دور افتاده بوسیله روش نمودار فراوانی (HBOS)

همانطور که در تصویر ۱۴ مشاهده می‌کنید، این بار ۹۰ مشاهده نامتعارف یا ناهنجار شناسایی شده و ۹۹۰۴ مشاهده نیز داده‌های هنجار شناخته شده‌اند.

جنگل ایزوله و شناخت مشاهدات ناهنجار چند متغیره

روشی که برای شناسایی مشاهدان ناهنجار در جنگل ایزوله (Isolation Forest) به کار می‌رود، شبیه به جنگل تصادفی (Random Forest) است. هر دو این روش‌ها برمبنای درخت تصمیم عمل می‌کنند.

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

با اسفاده از کتابخانه PyOD که نسخه بهینه شده دستور درخت ایزوله در کتابخانه Scikit-learn است می‌توان محاسبات مربوطه را انجام داد. در ادامه کدی را که به این منظور برای داده‌های فروش و سود فروشگاه‌های زنجیره‌ای SuperStore تهیه شده، مشاهده می‌کنید.

outliers_fraction = 0.01
xx , yy = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))
clf = IForest(contamination=outliers_fraction,random_state=0)
clf.fit(X)
scores_pred = clf.decision_function(X) * -1

y_pred = clf.predict(X)
n_inliers = len(y_pred) - np.count_nonzero(y_pred)
n_outliers = np.count_nonzero(y_pred == 1)
plt.figure(figsize=(8, 8))

df1 = df
df1['outlier'] = y_pred.tolist()
    
# sales - inlier feature 1,  profit - inlier feature 2
inliers_sales = np.array(df1['Sales'][df1['outlier'] == 0]).reshape(-1,1)
inliers_profit = np.array(df1['Profit'][df1['outlier'] == 0]).reshape(-1,1)
    
# sales - outlier feature 1, profit - outlier feature 2
outliers_sales = df1['Sales'][df1['outlier'] == 1].values.reshape(-1,1)
outliers_profit = df1['Profit'][df1['outlier'] == 1].values.reshape(-1,1)
         
print('OUTLIERS: ',n_outliers,'INLIERS: ',n_inliers)

threshold = percentile(scores_pred, 100 * outliers_fraction)
Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) * -1
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), threshold, 7),cmap=plt.cm.Blues_r)
a = plt.contour(xx, yy, Z, levels=[threshold],linewidths=2, colors='red')
plt.contourf(xx, yy, Z, levels=[threshold, Z.max()],colors='orange')
b = plt.scatter(inliers_sales, inliers_profit, c='white',s=20, edgecolor='k')
    
c = plt.scatter(outliers_sales, outliers_profit, c='black',s=20, edgecolor='k')
       
plt.axis('tight')
plt.legend([a.collections[0], b,c], ['learned decision function', 'inliers','outliers'],
           prop=matplotlib.font_manager.FontProperties(size=20),loc='lower right')      
plt.xlim((0, 1))
plt.ylim((0, 1))
plt.title('Isolation Forest')
plt.show();

نتیجه اجرای این دستورات، نموداری است که در ادامه مشاهده می‌کنید.

isolation forest
تصویر ۱۵: شناسایی نقاط دورافتاده بوسیله روش جنگل ایزوله (Isolation Forest)

شناسایی مشاهدات ناهنجار بوسیله الگوریتم دسته‌بندی k-نزدیکترین همسایه

الگوریتم یا شیوه دسته‌بندی k-نزدیکترین همسایه (k- Nearest Neigbors) که به اختصار KNN نامیده می‌شود، یکی از ساده‌ترین و عمومی‌ترین تکنیک‌های دسته‌بندی است.

در این تکنیک برای هر نقطه فاصله با k-امین همسایه که نسبت به دیگر همسایه‌ها نزدیکتر است، محاسبه می‌شود. این مقدار به عنوان نمره یا امتیاز دورافتادگی در نظر گرفته می‌شود.

کدی که در زیر مشاهده می‌کنید به منظور پیاده‌سازی این الگوریتم روی داده‌های SuperStore اجرا شده است.

outliers_fraction = 0.01
xx , yy = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0, 1, 100))
clf = KNN(contamination=outliers_fraction)
clf.fit(X)
scores_pred = clf.decision_function(X) * -1
y_pred = clf.predict(X)
n_inliers = len(y_pred) - np.count_nonzero(y_pred)
n_outliers = np.count_nonzero(y_pred == 1)
plt.figure(figsize=(8, 8))

df1 = df
df1['outlier'] = y_pred.tolist()
    
# sales - inlier feature 1,  profit - inlier feature 2
inliers_sales = np.array(df1['Sales'][df1['outlier'] == 0]).reshape(-1,1)
inliers_profit = np.array(df1['Profit'][df1['outlier'] == 0]).reshape(-1,1)
    
# sales - outlier feature 1, profit - outlier feature 2
outliers_sales = df1['Sales'][df1['outlier'] == 1].values.reshape(-1,1)
outliers_profit = df1['Profit'][df1['outlier'] == 1].values.reshape(-1,1)
         
print('OUTLIERS: ',n_outliers,'INLIERS: ',n_inliers)

threshold = percentile(scores_pred, 100 * outliers_fraction)

Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) * -1
Z = Z.reshape(xx.shape)

plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), threshold, 7),cmap=plt.cm.Blues_r)
a = plt.contour(xx, yy, Z, levels=[threshold],linewidths=2, colors='red')
plt.contourf(xx, yy, Z, levels=[threshold, Z.max()],colors='orange')
b = plt.scatter(inliers_sales, inliers_profit, c='white',s=20, edgecolor='k')    
c = plt.scatter(outliers_sales, outliers_profit, c='black',s=20, edgecolor='k')       
plt.axis('tight')  
plt.legend([a.collections[0], b,c], ['learned decision function', 'inliers','outliers'],
           prop=matplotlib.font_manager.FontProperties(size=20),loc='lower right')      
plt.xlim((0, 1))
plt.ylim((0, 1))
plt.title('K Nearest Neighbors (KNN)')
plt.show();

نتیجه به صورت نموداری است که در تصویر ۱۶ مشاهده می‌کنید.

نکته: به نظر می‌رسد که نقاط ناهنجار در این چهار الگوریتم (در حالت دو متغیره) با یکدیگر تفاوتی ندارند و همگی به مشاهدات مشابه، به عنوان مشاهدات نامتعارف یا ناهنجار رسیده‌اند.

knn detection
تصویر ۱۶: شناسایی نقاط دورافتاده بوسیله روش KNN

خلاصه و جمع بندی

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

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

اگر این مطلب برای شما مفید بوده است، آموزش‌ها و مطالبی که در ادامه آمده‌اند نیز به شما پیشنهاد می‌شوند:

^^

بر اساس رای ۳ نفر
آیا این مطلب برای شما مفید بود؟
شما قبلا رای داده‌اید!
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.

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