# کار با داده‌ها: آماده‌سازی داده‌ها |![ اسکیچ‌نوت توسط [(@sketchthedocs)](https://sketchthedocs.dev) ](../../sketchnotes/08-DataPreparation.png)| |:---:| |آماده‌سازی داده‌ها - _اسکیچ‌نوت توسط [@nitya](https://twitter.com/nitya)_ | ## [پیش‌کوئیز](https://purple-hill-04aebfb03.1.azurestaticapps.net/quiz/14) بسته به منبع آن، داده‌های خام ممکن است شامل ناسازگاری‌هایی باشند که در تحلیل و مدل‌سازی مشکلاتی ایجاد کنند. به عبارت دیگر، این داده‌ها می‌توانند به عنوان "کثیف" دسته‌بندی شوند و نیاز به پاک‌سازی دارند. این درس بر تکنیک‌های پاک‌سازی و تبدیل داده‌ها برای مدیریت چالش‌های داده‌های گم‌شده، نادرست یا ناقص تمرکز دارد. موضوعات پوشش داده‌شده در این درس از پایتون و کتابخانه Pandas استفاده می‌کنند و در [دفترچه یادداشت](../../../../2-Working-With-Data/08-data-preparation/notebook.ipynb) موجود در این پوشه نمایش داده می‌شوند. ## اهمیت پاک‌سازی داده‌ها - **سهولت استفاده و استفاده مجدد**: وقتی داده‌ها به درستی سازماندهی و نرمال‌سازی شوند، جستجو، استفاده و به اشتراک‌گذاری آن‌ها با دیگران آسان‌تر می‌شود. - **یکپارچگی**: علم داده اغلب نیاز به کار با بیش از یک مجموعه داده دارد، جایی که مجموعه داده‌ها از منابع مختلف باید با هم ترکیب شوند. اطمینان از اینکه هر مجموعه داده به صورت جداگانه استانداردسازی شده است، تضمین می‌کند که داده‌ها هنگام ادغام در یک مجموعه داده همچنان مفید باقی می‌مانند. - **دقت مدل**: داده‌هایی که پاک‌سازی شده‌اند، دقت مدل‌هایی که به آن‌ها وابسته هستند را بهبود می‌بخشند. ## اهداف و استراتژی‌های رایج در پاک‌سازی - **کاوش در یک مجموعه داده**: کاوش داده‌ها، که در [درس بعدی](https://github.com/microsoft/Data-Science-For-Beginners/tree/main/4-Data-Science-Lifecycle/15-analyzing) پوشش داده می‌شود، می‌تواند به شما کمک کند داده‌هایی که نیاز به پاک‌سازی دارند را کشف کنید. مشاهده بصری مقادیر درون یک مجموعه داده می‌تواند انتظاراتی از ظاهر بقیه داده‌ها ایجاد کند یا ایده‌ای از مشکلاتی که می‌توانند حل شوند ارائه دهد. کاوش می‌تواند شامل پرس‌وجوهای ساده، مصورسازی‌ها و نمونه‌گیری باشد. - **فرمت‌بندی**: بسته به منبع، داده‌ها ممکن است ناسازگاری‌هایی در نحوه ارائه داشته باشند. این می‌تواند در جستجو و نمایش مقدار مشکلاتی ایجاد کند، جایی که مقدار در مجموعه داده دیده می‌شود اما به درستی در مصورسازی‌ها یا نتایج پرس‌وجو نمایش داده نمی‌شود. مشکلات رایج فرمت‌بندی شامل حل فاصله‌های خالی، تاریخ‌ها و انواع داده‌ها است. حل مشکلات فرمت‌بندی معمولاً به عهده افرادی است که از داده‌ها استفاده می‌کنند. به عنوان مثال، استانداردهای نحوه نمایش تاریخ‌ها و اعداد می‌تواند در کشورهای مختلف متفاوت باشد. - **تکرارها**: داده‌هایی که بیش از یک بار ظاهر می‌شوند می‌توانند نتایج نادرستی تولید کنند و معمولاً باید حذف شوند. این می‌تواند یک اتفاق رایج هنگام ترکیب دو یا چند مجموعه داده باشد. با این حال، مواردی وجود دارد که تکرار در مجموعه داده‌های ترکیبی شامل بخش‌هایی است که می‌توانند اطلاعات اضافی ارائه دهند و ممکن است نیاز به حفظ داشته باشند. - **داده‌های گم‌شده**: داده‌های گم‌شده می‌توانند باعث نادرستی و همچنین نتایج ضعیف یا جانبدارانه شوند. گاهی اوقات این مشکلات می‌توانند با "بارگذاری مجدد" داده‌ها، پر کردن مقادیر گم‌شده با محاسبات و کدهایی مانند پایتون، یا به سادگی حذف مقدار و داده‌های مربوطه حل شوند. دلایل متعددی برای گم‌شدن داده‌ها وجود دارد و اقداماتی که برای حل این مقادیر گم‌شده انجام می‌شود می‌تواند به نحوه و دلیل گم‌شدن آن‌ها بستگی داشته باشد. ## کاوش اطلاعات DataFrame > **هدف یادگیری:** تا پایان این بخش، باید بتوانید اطلاعات کلی درباره داده‌های ذخیره‌شده در DataFrameهای pandas را پیدا کنید. پس از بارگذاری داده‌ها در pandas، احتمالاً داده‌ها در یک DataFrame خواهند بود (برای مرور دقیق‌تر به [درس قبلی](https://github.com/microsoft/Data-Science-For-Beginners/tree/main/2-Working-With-Data/07-python#dataframe) مراجعه کنید). با این حال، اگر مجموعه داده در DataFrame شما شامل ۶۰,۰۰۰ سطر و ۴۰۰ ستون باشد، چگونه می‌توانید حتی شروع به درک آنچه با آن کار می‌کنید کنید؟ خوشبختانه، [pandas](https://pandas.pydata.org/) ابزارهای مناسبی برای مشاهده سریع اطلاعات کلی درباره یک DataFrame و همچنین چند سطر اول و آخر آن ارائه می‌دهد. برای کاوش این قابلیت‌ها، ما کتابخانه scikit-learn پایتون را وارد کرده و از یک مجموعه داده معروف: **مجموعه داده Iris** استفاده خواهیم کرد. ```python import pandas as pd from sklearn.datasets import load_iris iris = load_iris() iris_df = pd.DataFrame(data=iris['data'], columns=iris['feature_names']) ``` | |طول کاسبرگ (cm)|عرض کاسبرگ (cm)|طول گلبرگ (cm)|عرض گلبرگ (cm)| |----------------------------------------|----------------|---------------|--------------|--------------| |0 |5.1 |3.5 |1.4 |0.2 | |1 |4.9 |3.0 |1.4 |0.2 | |2 |4.7 |3.2 |1.3 |0.2 | |3 |4.6 |3.1 |1.5 |0.2 | |4 |5.0 |3.6 |1.4 |0.2 | - **DataFrame.info**: برای شروع، متد `info()` برای چاپ خلاصه‌ای از محتوای موجود در یک `DataFrame` استفاده می‌شود. بیایید نگاهی به این مجموعه داده بیندازیم تا ببینیم چه داریم: ```python iris_df.info() ``` ``` RangeIndex: 150 entries, 0 to 149 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 sepal length (cm) 150 non-null float64 1 sepal width (cm) 150 non-null float64 2 petal length (cm) 150 non-null float64 3 petal width (cm) 150 non-null float64 dtypes: float64(4) memory usage: 4.8 KB ``` از اینجا می‌دانیم که مجموعه داده *Iris* شامل ۱۵۰ ورودی در چهار ستون است و هیچ ورودی خالی ندارد. تمام داده‌ها به صورت اعداد اعشاری ۶۴ بیتی ذخیره شده‌اند. - **DataFrame.head()**: سپس، برای بررسی محتوای واقعی `DataFrame`، از متد `head()` استفاده می‌کنیم. بیایید ببینیم چند سطر اول `iris_df` ما چگونه به نظر می‌رسند: ```python iris_df.head() ``` ``` sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) 0 5.1 3.5 1.4 0.2 1 4.9 3.0 1.4 0.2 2 4.7 3.2 1.3 0.2 3 4.6 3.1 1.5 0.2 4 5.0 3.6 1.4 0.2 ``` - **DataFrame.tail()**: به طور مشابه، برای بررسی چند سطر آخر `DataFrame`، از متد `tail()` استفاده می‌کنیم: ```python iris_df.tail() ``` ``` sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) 145 6.7 3.0 5.2 2.3 146 6.3 2.5 5.0 1.9 147 6.5 3.0 5.2 2.0 148 6.2 3.4 5.4 2.3 149 5.9 3.0 5.1 1.8 ``` > **نکته کلیدی:** حتی با نگاه کردن به متادیتا درباره اطلاعات موجود در یک DataFrame یا چند مقدار اول و آخر آن، می‌توانید فوراً ایده‌ای درباره اندازه، شکل و محتوای داده‌هایی که با آن‌ها کار می‌کنید به دست آورید. ## مدیریت داده‌های گم‌شده > **هدف یادگیری:** تا پایان این بخش، باید بدانید چگونه مقادیر خالی را در DataFrameها جایگزین یا حذف کنید. اغلب مجموعه داده‌هایی که می‌خواهید استفاده کنید (یا مجبور به استفاده از آن‌ها هستید) دارای مقادیر گم‌شده هستند. نحوه مدیریت داده‌های گم‌شده دارای ملاحظات ظریفی است که می‌تواند بر تحلیل نهایی و نتایج دنیای واقعی تأثیر بگذارد. pandas داده‌های گم‌شده را به دو روش مدیریت می‌کند. اولین روش که قبلاً در بخش‌های قبلی دیده‌اید: `NaN` یا Not a Number است. این در واقع یک مقدار خاص است که بخشی از مشخصات IEEE برای اعداد اعشاری است و فقط برای نشان دادن مقادیر گم‌شده اعشاری استفاده می‌شود. برای مقادیر گم‌شده غیر از اعداد اعشاری، pandas از شیء `None` پایتون استفاده می‌کند. در حالی که ممکن است گیج‌کننده به نظر برسد که با دو نوع مقدار مختلف مواجه شوید که اساساً یک چیز را می‌گویند، دلایل برنامه‌نویسی معتبری برای این انتخاب طراحی وجود دارد و در عمل، این رویکرد به pandas امکان می‌دهد تا برای اکثر موارد یک سازش خوب ارائه دهد. با این حال، هم `None` و هم `NaN` محدودیت‌هایی دارند که باید در مورد نحوه استفاده از آن‌ها به آن‌ها توجه کنید. اطلاعات بیشتری درباره `NaN` و `None` را از [دفترچه یادداشت](https://github.com/microsoft/Data-Science-For-Beginners/blob/main/4-Data-Science-Lifecycle/15-analyzing/notebook.ipynb) بررسی کنید! - **تشخیص مقادیر خالی**: در `pandas`، متدهای `isnull()` و `notnull()` روش‌های اصلی شما برای تشخیص داده‌های خالی هستند. هر دو ماسک‌های بولی روی داده‌های شما برمی‌گردانند. ما از `numpy` برای مقادیر `NaN` استفاده خواهیم کرد: ```python import numpy as np example1 = pd.Series([0, np.nan, '', None]) example1.isnull() ``` ``` 0 False 1 True 2 False 3 True dtype: bool ``` به خروجی با دقت نگاه کنید. آیا چیزی شما را شگفت‌زده می‌کند؟ در حالی که `0` یک مقدار خالی حسابی است، با این حال یک عدد صحیح کاملاً معتبر است و pandas آن را به همین صورت در نظر می‌گیرد. `''` کمی ظریف‌تر است. در حالی که ما از آن در بخش ۱ برای نمایش یک مقدار رشته‌ای خالی استفاده کردیم، با این حال یک شیء رشته‌ای است و به عنوان یک مقدار خالی از نظر pandas در نظر گرفته نمی‌شود. اکنون، بیایید این موضوع را برعکس کنیم و این متدها را به روشی که بیشتر در عمل استفاده خواهید کرد، به کار ببریم. شما می‌توانید از ماسک‌های بولی به طور مستقیم به عنوان یک ``Series`` یا ``DataFrame`` ایندکس استفاده کنید، که می‌تواند هنگام کار با مقادیر گم‌شده (یا موجود) مفید باشد. > **نکته کلیدی**: هر دو متد `isnull()` و `notnull()` نتایج مشابهی را هنگام استفاده در `DataFrame`ها تولید می‌کنند: آن‌ها نتایج و ایندکس آن نتایج را نشان می‌دهند، که به شما کمک زیادی خواهد کرد زیرا با داده‌های خود سر و کله می‌زنید. - **حذف مقادیر خالی**: فراتر از شناسایی مقادیر گم‌شده، pandas یک روش مناسب برای حذف مقادیر خالی از `Series` و `DataFrame`ها ارائه می‌دهد. (به خصوص در مجموعه داده‌های بزرگ، اغلب توصیه می‌شود که به سادگی مقادیر گم‌شده [NA] را از تحلیل خود حذف کنید تا اینکه به روش‌های دیگر با آن‌ها برخورد کنید.) برای دیدن این موضوع در عمل، بیایید به `example1` بازگردیم: ```python example1 = example1.dropna() example1 ``` ``` 0 0 2 dtype: object ``` توجه داشته باشید که این باید شبیه خروجی شما از `example3[example3.notnull()]` باشد. تفاوت اینجا این است که، به جای فقط ایندکس کردن مقادیر ماسک‌شده، `dropna` آن مقادیر گم‌شده را از `Series` `example1` حذف کرده است. از آنجا که `DataFrame`ها دو بعد دارند، گزینه‌های بیشتری برای حذف داده‌ها ارائه می‌دهند. ```python example2 = pd.DataFrame([[1, np.nan, 7], [2, 5, 8], [np.nan, 6, 9]]) example2 ``` | | 0 | 1 | 2 | |------|---|---|---| |0 |1.0|NaN|7 | |1 |2.0|5.0|8 | |2 |NaN|6.0|9 | (آیا متوجه شدید که pandas دو ستون را به اعداد اعشاری تبدیل کرد تا `NaN`ها را جا دهد؟) شما نمی‌توانید یک مقدار واحد را از یک `DataFrame` حذف کنید، بنابراین باید سطرها یا ستون‌های کامل را حذف کنید. بسته به کاری که انجام می‌دهید، ممکن است بخواهید یکی یا دیگری را انجام دهید، و بنابراین pandas گزینه‌هایی برای هر دو به شما می‌دهد. زیرا در علم داده، ستون‌ها معمولاً متغیرها و سطرها مشاهدات را نشان می‌دهند، احتمالاً بیشتر سطرهای داده را حذف خواهید کرد؛ تنظیم پیش‌فرض برای `dropna()` این است که تمام سطرهایی که حاوی هر مقدار خالی هستند را حذف کند: ```python example2.dropna() ``` ``` 0 1 2 1 2.0 5.0 8 ``` در صورت لزوم، می‌توانید مقادیر NA را از ستون‌ها حذف کنید. از `axis=1` برای این کار استفاده کنید: ```python example2.dropna(axis='columns') ``` ``` 2 0 7 1 8 2 9 ``` توجه داشته باشید که این می‌تواند مقدار زیادی از داده‌هایی را که ممکن است بخواهید نگه دارید حذف کند، به خصوص در مجموعه داده‌های کوچک‌تر. اگر فقط بخواهید سطرها یا ستون‌هایی را که شامل چندین مقدار خالی یا حتی تمام مقادیر خالی هستند حذف کنید چه؟ شما می‌توانید این تنظیمات را در `dropna` با پارامترهای `how` و `thresh` مشخص کنید. به طور پیش‌فرض، `how='any'` است (اگر می‌خواهید خودتان بررسی کنید یا ببینید این متد چه پارامترهای دیگری دارد، `example4.dropna?` را در یک سلول کد اجرا کنید). شما می‌توانید به طور متناوب `how='all'` را مشخص کنید تا فقط سطرها یا ستون‌هایی که تمام مقادیر آن‌ها خالی است حذف شوند. بیایید مثال `DataFrame` خود را گسترش دهیم تا این موضوع را در عمل ببینیم. ```python example2[3] = np.nan example2 ``` | |0 |1 |2 |3 | |------|---|---|---|---| |0 |1.0|NaN|7 |NaN| |1 |2.0|5.0|8 |NaN| |2 |NaN|6.0|9 |NaN| پارامتر `thresh` کنترل دقیق‌تری به شما می‌دهد: شما تعداد مقادیر *غیرخالی* را که یک سطر یا ستون باید داشته باشد تا نگه داشته شود تنظیم می‌کنید: ```python example2.dropna(axis='rows', thresh=3) ``` ``` 0 1 2 3 1 2.0 5.0 8 NaN ``` اینجا، سطر اول و آخر حذف شده‌اند، زیرا فقط شامل دو مقدار غیرخالی هستند. - **پر کردن مقادیر خالی**: بسته به مجموعه داده شما، گاهی اوقات پر کردن مقادیر خالی با مقادیر معتبر به جای حذف آن‌ها منطقی‌تر است. شما می‌توانید از `isnull` برای این کار به صورت جایگزین استفاده کنید، اما این می‌تواند خسته‌کننده باشد، به خصوص اگر مقادیر زیادی برای پر کردن داشته باشید. زیرا این یک کار رایج در علم داده است، pandas متد `fillna` را ارائه می‌دهد که یک کپی از `Series` یا `DataFrame` را با مقادیر گم‌شده جایگزین‌شده با مقداری که شما انتخاب می‌کنید برمی‌گرداند. بیایید یک مثال دیگر از `Series` ایجاد کنیم تا ببینیم این در عمل چگونه کار می‌کند. ```python example3 = pd.Series([1, np.nan, 2, None, 3], index=list('abcde')) example3 ``` ``` a 1.0 b NaN c 2.0 d NaN e 3.0 dtype: float64 ``` شما می‌توانید تمام ورودی‌های خالی را با یک مقدار واحد، مانند `0` پر کنید: ```python example3.fillna(0) ``` ``` a 1.0 b 0.0 c 2.0 d 0.0 e 3.0 dtype: float64 ``` شما می‌توانید مقادیر خالی را **به جلو پر کنید**، یعنی از آخرین مقدار معتبر برای پر کردن یک مقدار خالی استفاده کنید: ```python example3.fillna(method='ffill') ``` ``` a 1.0 b 1.0 c 2.0 d 2.0 e 3.0 dtype: float64 ``` شما همچنین می‌توانید **به عقب پر کنید** تا مقدار معتبر بعدی را به عقب منتقل کنید و یک مقدار خالی را پر کنید: ```python example3.fillna(method='bfill') ``` ``` a 1.0 b 2.0 c 2.0 d 3.0 e 3.0 dtype: float64 ``` همان‌طور که ممکن است حدس بزنید، این با `DataFrame`ها نیز به همین صورت کار می‌کند، اما شما همچنین می‌توانید یک `axis` را مشخص کنید که در طول آن مقادیر خالی پر شوند. با استفاده مجدد از `example2`: ```python example2.fillna(method='ffill', axis=1) ``` ``` 0 1 2 3 0 1.0 1.0 7.0 7.0 1 2.0 5.0 8.0 8.0 2 NaN 6.0 9.0 9.0 ``` توجه داشته باشید که وقتی مقدار قبلی برای پر کردن به جلو در دسترس نیست، مقدار خالی باقی می‌ماند. > **نکته کلیدی:** روش‌های مختلفی برای برخورد با مقادیر گمشده در داده‌های شما وجود دارد. استراتژی خاصی که استفاده می‌کنید (حذف، جایگزینی یا حتی نحوه جایگزینی) باید بر اساس ویژگی‌های خاص آن داده‌ها تعیین شود. هرچه بیشتر با مجموعه داده‌ها کار کنید و تعامل داشته باشید، درک بهتری از نحوه برخورد با مقادیر گمشده پیدا خواهید کرد. ## حذف داده‌های تکراری > **هدف یادگیری:** تا پایان این بخش، باید بتوانید مقادیر تکراری را در DataFrame‌ها شناسایی و حذف کنید. علاوه بر داده‌های گمشده، اغلب در مجموعه داده‌های دنیای واقعی با داده‌های تکراری مواجه می‌شوید. خوشبختانه، `pandas` روشی ساده برای شناسایی و حذف ورودی‌های تکراری ارائه می‌دهد. - **شناسایی تکراری‌ها: `duplicated`**: می‌توانید به راحتی مقادیر تکراری را با استفاده از متد `duplicated` در pandas شناسایی کنید. این متد یک ماسک بولی برمی‌گرداند که نشان می‌دهد آیا یک ورودی در `DataFrame` تکراری از ورودی قبلی است یا خیر. بیایید یک مثال دیگر از `DataFrame` ایجاد کنیم تا این موضوع را در عمل ببینیم. ```python example4 = pd.DataFrame({'letters': ['A','B'] * 2 + ['B'], 'numbers': [1, 2, 1, 3, 3]}) example4 ``` | |letters|numbers| |------|-------|-------| |0 |A |1 | |1 |B |2 | |2 |A |1 | |3 |B |3 | |4 |B |3 | ```python example4.duplicated() ``` ``` 0 False 1 False 2 True 3 False 4 True dtype: bool ``` - **حذف تکراری‌ها: `drop_duplicates`:** به سادگی یک کپی از داده‌ها را برمی‌گرداند که در آن تمام مقادیر `duplicated` برابر با `False` هستند: ```python example4.drop_duplicates() ``` ``` letters numbers 0 A 1 1 B 2 3 B 3 ``` هم `duplicated` و هم `drop_duplicates` به طور پیش‌فرض تمام ستون‌ها را در نظر می‌گیرند، اما می‌توانید مشخص کنید که فقط یک زیرمجموعه از ستون‌ها در `DataFrame` بررسی شوند: ```python example4.drop_duplicates(['letters']) ``` ``` letters numbers 0 A 1 1 B 2 ``` > **نکته کلیدی:** حذف داده‌های تکراری بخش ضروری تقریباً هر پروژه داده‌محور است. داده‌های تکراری می‌توانند نتایج تحلیل‌های شما را تغییر دهند و نتایج نادرستی به شما بدهند! ## 🚀 چالش تمام مطالب مطرح‌شده به صورت یک [دفترچه Jupyter](https://github.com/microsoft/Data-Science-For-Beginners/blob/main/2-Working-With-Data/08-data-preparation/notebook.ipynb) ارائه شده‌اند. علاوه بر این، تمرین‌هایی بعد از هر بخش وجود دارد، حتماً آن‌ها را امتحان کنید! ## [آزمون پس از درس](https://purple-hill-04aebfb03.1.azurestaticapps.net/quiz/15) ## مرور و مطالعه شخصی روش‌های زیادی برای کشف و آماده‌سازی داده‌ها برای تحلیل و مدل‌سازی وجود دارد و پاک‌سازی داده‌ها یک مرحله مهم و عملی است. این چالش‌ها از Kaggle را امتحان کنید تا تکنیک‌هایی که در این درس پوشش داده نشده‌اند را بررسی کنید. - [چالش پاک‌سازی داده‌ها: تجزیه تاریخ‌ها](https://www.kaggle.com/rtatman/data-cleaning-challenge-parsing-dates/) - [چالش پاک‌سازی داده‌ها: مقیاس‌بندی و نرمال‌سازی داده‌ها](https://www.kaggle.com/rtatman/data-cleaning-challenge-scale-and-normalize-data) ## تکلیف [ارزیابی داده‌ها از یک فرم](assignment.md) **سلب مسئولیت**: این سند با استفاده از سرویس ترجمه هوش مصنوعی [Co-op Translator](https://github.com/Azure/co-op-translator) ترجمه شده است. در حالی که ما تلاش می‌کنیم دقت را حفظ کنیم، لطفاً توجه داشته باشید که ترجمه‌های خودکار ممکن است شامل خطاها یا نادرستی‌ها باشند. سند اصلی به زبان اصلی آن باید به عنوان منبع معتبر در نظر گرفته شود. برای اطلاعات حساس، توصیه می‌شود از ترجمه انسانی حرفه‌ای استفاده کنید. ما مسئولیتی در قبال سوء تفاهم‌ها یا تفسیرهای نادرست ناشی از استفاده از این ترجمه نداریم.