پاکسازی داده (Data Cleaning) در پایتون با استفاده از NumPy و Pandas — راهنمای جامع
«دانشمندان داده» (Data Scientists) حجم زیادی از زمان خود در پروژههای «تحلیل داده» (Data Analysis) را به «پاکسازی دادهها» (Data Cleansing | Data Cleaning) و تبدیل آنها به شکل قابل پردازش اختصاص میدهند. در حقیقت، دانشمندان داده بسیاری بر این باور هستند که گامهای ابتدایی گردآوری و پاکسازی دادهها در فرآیند تحلیل داده و دادهکاوی، ۸۰ درصد از کل کار آنها را تشکیل میدهد.
بنابراین، برای افرادی که تمایل دارند وارد حوزه دادهکاوی و تحلیل داده شوند تسلط بر روشهای مواجهه با دادههای کثیف (Dirty data | Messy Data) الزامی و حائز اهمیت محسوب میشود. منظور از دادههای کثیف دادههایی است که شامل مقادیر ناموجود، فرمتهای ناسازگار، موارد ثبت شده ناقص یا دورافتادگیهای فاقد معنی هستند. در این راهنما، از کتابخانههای Pandas و NumPy زبان برنامهنویسی پایتون به منظور پاکسازی دادهها استفاده خواهد شد. مطالعه مطلب «یادگیری علم داده با پایتون — از صفر تا صد» نیز به افرادی که در آغاز راه یادگیری علم داده با پایتون قرار دارند توصیه میشود.
در مقاله پیش رو سرفصلهای زیر مورد بررسی قرار خواهد گرفت.
- حذف ستونهای غیر لازم از دیتافریم
- تغییر اندیس یک دیتافریم
- استفاده از متدهای ()str. برای پاک کردن ستونها
- استفاده از تابع ()DataFrame.applymap برای پاکسازی کل مجموعه داده به صورت مولفه به مولفه
- نامگذاری مجدد ستونها به منظور ایجاد مجموعه برچسبهای قابل تشخیصتر
- گذر کردن از سطرهای غیر لازم در فایل CSV
مجموعه دادههایی که در مثالهای این نوشتار مورد استفاده قرار گرفتهاند عبارتند از:
- BL-Flickr-Images-Book.csv: یک فایل CSV که شامل اطلاعاتی پیرامون کتابهای موجود در کتابخانه بریتانیا «British Library» است.
- university_towns.txt: یک فایل متنی که در برگیرنده اسامی شهرهای دارای کالج در هر یک از ایالتهای آمریکا است.
- olympics.csv: یک فایل CSV که حاوی اطلاعات خلاصهای پیرامون مشارکت کلیه کشورها در المپیک تابستانی و زمستانی است.
مجموعه دادههای بالا را میتوان از مخزن گیتهاب (GitHub repository) پایتون دانلود کرد. انجام این کار به مخاطبان این مطلب اکیدا توصیه میشود، زیرا میتوانند گام به گام و همراه با مطلب، آنچه آموزش داده شده را تمرین کرده و بیاموزند. در ادامه این مطلب فرض بر این است که مطالعهکنندگان دارای آشنایی مقدماتی با کتابخانههای Pandas و NumPy و به ویژه «سریها» (Series) و «دیتافریمها» (DataFrame)، متدهای متداول قابل اعمال بر آنها و همچنین مقدار NaN در NumPy هستند. افرادی که با مباحث یاد شده آشنایی ندارند میتوانند پیش از ادامه مقاله پیش رو، این مطلب را مطالعه کنند.
پیش از ادامه این مبحث لازم است یادآور شویم که میتوانید آمادهسازی و پاکسازی داده را با استفاده از مجموعه آموزش آماده سازی و پاکسازی داده، مقدماتی تا پیشرفته فرادرس یاد بگیرید.
اکنون به اصل مطلب، یعنی پاکسازی دادههای کثیف با استفاده از NumPy و Pandas پرداخته میشود. در ابتدا، کار با ایمپورت کردن ماژولهای مورد نیاز (یعنی دو کتابخانه یاد شده) آغاز میشود.
1>>> import pandas as pd
2>>> import numpy as np
حذف ستونها در یک DataFrame
در اغلب پروژههای تحلیل داده و دادهکاوی، پژوهشگران با این مساله مواجه هستند که همه دستههای دادههای موجود مفید نیستند. برای مثال، ممکن است یک مجموعه داده از اطلاعات دانشآموزان شامل نام، مقطع تحصیلی، نام والدین و آدرس آنها وجود داشته باشد، اما هدف تحلیل مقطع تحصیلی دانشآموزان باشد. در این مثال، آدرس منزل یا نام والدین در تحلیلها فاقد اهمیت محسوب میشوند.
نگهداری این اطلاعات بلااستفاده صرفا فضا اشغال کرده و سرعت تحلیل را در زمان اجرا کاهش میدهد. Pandas یک راهکار کاربردی برای حذف ستونها یا سطرهای ناخواسته از دیتافریم (DataFrame) با تابع ()drop فراهم میکند. در ادامه مثال کوچکی از چگونگی حذف تعدادی از ستونها از یک دیتافریم نشان داده شده است. ابتدا، یک دیتافریم از فایل CSV مربوط به مجموعه داده BL-Flickr-Images-Book.csv ساخته میشود. در این مثال، مسیر مربوطه به pd.read_csv پاس داده میشود، بدین معنا که مجموعه دادهها در پوشهای با نام Datasets در دایرکتوری در حال کار فعلی قرار دارند.
1>>> df = pd.read_csv('Datasets/BL-Flickr-Images-Book.csv')
2>>> df.head()
3
4 Identifier Edition Statement Place of Publication \
50 206 NaN London
61 216 NaN London; Virtue & Yorston
72 218 NaN London
83 472 NaN London
94 480 A new edition, revised, etc. London
10
11 Date of Publication Publisher \
120 1879 [1878] S. Tinsley & Co.
131 1868 Virtue & Co.
142 1869 Bradbury, Evans & Co.
153 1851 James Darling
164 1857 Wertheim & Macintosh
17
18 Title Author \
190 Walter Forbes. [A novel.] By A. A A. A.
201 All for Greed. [A novel. The dedication signed... A., A. A.
212 Love the Avenger. By the author of “All for Gr... A., A. A.
223 Welsh Sketches, chiefly ecclesiastical, to the... A., E. S.
234 [The World in which I live, and my place in it... A., E. S.
24
25 Contributors Corporate Author \
260 FORBES, Walter. NaN
271 BLAZE DE BURY, Marie Pauline Rose - Baroness NaN
282 BLAZE DE BURY, Marie Pauline Rose - Baroness NaN
293 Appleyard, Ernest Silvanus. NaN
304 BROOME, John Henry. NaN
31
32 Corporate Contributors Former owner Engraver Issuance type \
330 NaN NaN NaN monographic
341 NaN NaN NaN monographic
352 NaN NaN NaN monographic
363 NaN NaN NaN monographic
374 NaN NaN NaN monographic
38
39 Flickr URL \
400 http://www.flickr.com/photos/britishlibrary/ta...
411 http://www.flickr.com/photos/britishlibrary/ta...
422 http://www.flickr.com/photos/britishlibrary/ta...
433 http://www.flickr.com/photos/britishlibrary/ta...
444 http://www.flickr.com/photos/britishlibrary/ta...
45
46 Shelfmarks
470 British Library HMNTS 12641.b.30.
481 British Library HMNTS 12626.cc.2.
492 British Library HMNTS 12625.dd.1.
503 British Library HMNTS 10369.bbb.15.
514 British Library HMNTS 9007.d.28.
سپس با استفاده از متد ()head پنج ورودی اول پرینت میشوند، و بر همین اساس قابل مشاهده است که برخی ستونها اطلاعات مازادی فراهم میکنند که امکان دارد به وصف کتابخانه کمک کنند، اما نقشی در توصیف خود کتابها ندارند که از این جمله میتوان به Edition Statement، Corporate Author، Corporate Contributors، Former owner، Engraver، Issuance type و Shelfmarks اشاره کرد. میتوان این ستونها را به شکل زیر حذف کرد.
1>>> to_drop = ['Edition Statement',
2... 'Corporate Author',
3... 'Corporate Contributors',
4... 'Former owner',
5... 'Engraver',
6... 'Contributors',
7... 'Issuance type',
8... 'Shelfmarks']
9
10>>> df.drop(to_drop, inplace=True, axis=1)
در بالا، لیستی تعریف شد که اسامی همه ستونهایی که هدف حذف آنها است تعیین شدهاند. سپس، از تابع ()drop روی اشیا تعیین شده استفاده میشود، و برای پارامتر inplace مقدار True و پارامتر axis برابر با ۱ قرار داده میشوند. این به Pandas میگوید که پژوهشگر تمایل دارد تغییراتی را به طور مستقیم روی اشیا انجام داده و در این راستا باید مقادیری از ستونهای شی حذف شوند. با بازرسی مجدد دیتافریم (DataFrame)، به وضوح مشهود است که ستونها حذف شدهاند.
1>>> df.head()
2 Identifier Place of Publication Date of Publication \
30 206 London 1879 [1878]
41 216 London; Virtue & Yorston 1868
52 218 London 1869
63 472 London 1851
74 480 London 1857
8
9 Publisher Title \
100 S. Tinsley & Co. Walter Forbes. [A novel.] By A. A
111 Virtue & Co. All for Greed. [A novel. The dedication signed...
122 Bradbury, Evans & Co. Love the Avenger. By the author of “All for Gr...
133 James Darling Welsh Sketches, chiefly ecclesiastical, to the...
144 Wertheim & Macintosh [The World in which I live, and my place in it...
15
16 Author Flickr URL
170 A. A. http://www.flickr.com/photos/britishlibrary/ta...
181 A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
192 A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
203 A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
214 A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
از سوی دیگر، میتوان ستونها را با پاس دادن آنها به پارامتر columns به طور مستقیم حذف کرد، به جای آنکه برچسبها را برای حذف و axis که Pandas باید در آن به دنبال برچسبها باشد را به طور جداگانه تعیین کرد.
1>>> df.drop(columns=to_drop, inplace=True)
این «نحو» (syntax) بصریتر و دارای خوانایی بالاتر است. آنچه در اینجا انجام میشود کاملا واضح است.
تغییر Index دیتافریم
در کتابخانه Pandas پایتون، Index عملکرد آرایه NumPy را گسترش میدهد تا امکان برچسبزنی و بخشبندی متنوعتر دادهها فراهم شود. در بسیاری از موارد، استفاده از یک فیلد شناسایی دارای مقدار یکتا برای داده به عنوان اندیس آن میتواند مفید باشد. برای مثال، در مجموعه داده استفاده شده در بخش بعدی، میتوان انتظار داشت هنگامی که یک کتابدار به دنبال یک رکورد میگردد، یک شناساگر یکتا را برای کتاب وارد کند (مقادیر در ستون Identifier):
1>>> df['Identifier'].is_unique
2True
اکنون اندیس موجود در این ستون با استفاده از set_index جایگزین میشود:
1>>> df = df.set_index('Identifier')
2>>> df.head()
3 Place of Publication Date of Publication \
4206 London 1879 [1878]
5216 London; Virtue & Yorston 1868
6218 London 1869
7472 London 1851
8480 London 1857
9
10 Publisher \
11206 S. Tinsley & Co.
12216 Virtue & Co.
13218 Bradbury, Evans & Co.
14472 James Darling
15480 Wertheim & Macintosh
16
17 Title Author \
18206 Walter Forbes. [A novel.] By A. A A. A.
19216 All for Greed. [A novel. The dedication signed... A., A. A.
20218 Love the Avenger. By the author of “All for Gr... A., A. A.
21472 Welsh Sketches, chiefly ecclesiastical, to the... A., E. S.
22480 [The World in which I live, and my place in it... A., E. S.
23
24 Flickr URL
25206 http://www.flickr.com/photos/britishlibrary/ta...
26216 http://www.flickr.com/photos/britishlibrary/ta...
27218 http://www.flickr.com/photos/britishlibrary/ta...
28472 http://www.flickr.com/photos/britishlibrary/ta...
29480 http://www.flickr.com/photos/britishlibrary/ta...
نکته: بر خلاف کلید اصلی در SQL، یک Index در Pandas هیچ تضمینی بر یکتا بودن ندارد، اگرچه انجام اندیسگذاریها و ادغامهای زیاد منجر به افزایش سرعت در زمان اجرا میشوند.
میتوان به هر رکورد به شیوهای ساده و با بهرهگیری از [ ]loc دسترسی داشت. با وجود آنکه [ ]loc ممکن است به معنای واقعی کلمه بصری نباشد، اما امکان اندیسگذاری مبتنی بر برچسب (label-based indexing) را فراهم میکند که در واقع برچسبگذاری سطرها یا رکوردها بدون در نظر گرفتن موقعیت آنها است.
1>>> df.loc[206]
2Place of Publication London
3Date of Publication 1879 [1878]
4Publisher S. Tinsley & Co.
5Title Walter Forbes. [A novel.] By A. A
6Author A. A.
7Flickr URL http://www.flickr.com/photos/britishlibrary/ta...
8Name: 206, dtype: object
به عبارت دیگر، 206 اولین برچسب اندیس است. برای «دسترسی» (access) به این مقدار با استفاده از موقعیت (position) آن میتوان از [df.iloc[0 استفاده کرد که اندیسگذاری مبتنی بر موقعیت را امکانپذیر میکند. پیش از این، اندیس یک «RangeIndex» (اعداد صحیح از صفر آغاز میشدند، مشابه با range توکار در پایتون) بود. با پاس دادن یک نام به set_index، اندیس به مقدار موجود در Identifier تغییر پیدا میکند. نکته شایان توجه آن است که متغیر مجدد به شی بازگردانده شده توسط متد (...)df = df.set_index تخصیص داده شده است. این کار به این دلیل صورت میگیرد که به طور پیشفرض، متد یک کپی ویرایش شده از شی را باز گردانده و تغییرات را به طور مستقیم روی شی انجام نمیدهد. میتوان با تنظیم پارامتر inplace از این کار اجتناب کرد.
1df.set_index('Identifier', inplace=True)
مرتبسازی فیلدهای داده
تاکنون، ستونهای غیر لازم حذف شدند و اندیس دیتافریم (DataFrame) به چیز معقولتری تغییر کرد. در این بخش، ستونهای مشخصی حذف میشوند و سایر موارد به یک فرمت واحد در میآیند تا ضمن ارائه درک بهتری از مجموعه داده از انسجام نیز پیروی کند. مشخصا، Date of Publication و Place of Publication به دلیل آنکه مورد نیاز نیستند حذف خواهند شد. پس از انجام بازرسی، مشخص شد که همه انواع دادهها از نوع داده object dtype هستند که تقریبا مشابه با str در پایتون محلی است. این مورد هر فیلدی که نمیتواند به خوبی به عنوان عددی یا داده دستهای فیت شود را انباشته میکند. این کار هنگامی معنادار است که پژوهشگر با دادههایی کار کند که در اصل دستهای از رشتههای کثیف هستند.
1>>> df.get_dtype_counts()
2object 6
یکی از فیلدهایی که تبدیل آن به مقدار عددی دارای معنا خواهد بود، تاریخ انتشار است. بنابراین، میتوان محاسبات زیر را روی آنها انجام داد.
1>>> df.loc[1905:, 'Date of Publication'].head(10)
2Identifier
31905 1888
41929 1839, 38-54
52836 [1897?]
62854 1865
72956 1860-63
82957 1873
93017 1866
103131 1899
114598 1814
124884 1820
13Name: Date of Publication, dtype: object
یک کتاب خاص ممکن است تنها یک تاریخ انتشار داشته باشد. بنابراین انجام کارهای زیر مورد نیاز است.
- حذف دادههای اضافی در آکولادها در هر جایی که [1879] 1878 وجود داشت.
- تبدیل رنج دادهها به تاریخ شروع آنها، هرجا که 63-1860، 1839 و 54-38 وجود دارد.
- تاریخهایی که کاربر درباره آنها مطمئن نیست به طور کامل حذف و با مقدار NaN در کتابخانه NumPy جایگزین میشوند، برای مثال [?1897]
- تبدیل رشته NaN به مقدار NaN در NumPy
با سنتز کرده این الگوها، میتوان از مزایای عبارت با قاعده مجرد برای استخراج سال انتشار استفاده کرد.
1regex = r'^(\d{4})'
عبارت با قاعده بالا به معنای یافتن هر چهار رقم در آغاز رشته است که برای پیدا کردن سال انتشار کافی محسوب میشود. آنچه در بالا ارائه شد «رشته خام» (raw string) است (بدین معنا که بَکاِسلَش (backslash) دیگر یک کاراکتر فرار محسوب نمیشود)، که در واقع یک اقدام استاندارد با عبارات باقاعده است. «d\» هر رقمی را نشان میدهد، و {4} این قانون را چهار بار تکرار میکند. کاراکتر ^ شروع رشتهها را مطابقت میدهد و پرانتزها مشخص کننده یک گروه طبقهبندی شده هستند که به Pandas سیگنال میدهد که کاربر قصد دارد آن بخش از regex را استخراج کند (هدف آن است که ^ از مواردی که در آن جمله با ] آغاز میشود اجتناب کند). اکنون میتوان دید که با اجرای این regex در مجموعه داده چه اتفاقی میافتد.
1>>> extr = df['Date of Publication'].str.extract(r'^(\d{4})', expand=False)
2>>> extr.head()
3Identifier
4206 1879
5216 1868
6218 1869
7472 1851
8480 1857
9Name: Date of Publication, dtype: object
نکته: افرادی که با regex آشنایی ندارند میتوانند در رابطه با نتیجه خروجی عبارت بالا در این وبسایت به بررسی بپردازند.
به لحاظ فنی این ستون همچنان دارای object dtype است، اما میتوان به سادگی نسخه عددی آن را با pd.to_numeric دریافت کرد.
1>>> df['Date of Publication'] = pd.to_numeric(extr)
2>>> df['Date of Publication'].dtype
3dtype('float64')
این نتیجه تقریبا یکی در هر ده مورد مقدار ناموجود است که هزینه کوچکی محسوب میشود که اکنون باید پرداخته شود تا امکان انجام محاسبات در دیگر مقادیر باقیمانده وجود داشته باشد.
1>>> df['Date of Publication'].isnull().sum() / len(df)
20.11717147339205986
بسیار عالی، کار مورد نظر انجام شد.
ترکیب متدهای str با NumPy برای پاکسازی ستونها
مسالهای که در بالا ممکن است توجه افراد زیادی را به خود جلب کرده باشد استفاده از df['Date of Publication'].str است. این مشخصه راهی برای دسترسی سریع به عملیات رشتهها در Pandas محسوب میشود که به طور گسترده عملیات موجود برای رشتهها یا عبارات باقاعده کامپایل شده در پایتون محلی مانند ()split()، .replace. و ()capitalize. را تقلید میکند.
برای پاکسازی فیلد Place of Publication، میتوان متد str در کتابخانه Pandas را با np.where در NumPy ترکیب کرد که در اصل شکل بُرداری شده ماکرو ()IF در نرمافزار «اکسل» (Excel) به شمار میآید. نحو این دستور در پایتون به صورت زیر است.
1>>> np.where(condition, then, else)
در اینجا، condition نیز یک شی آرایه مانند یا «ماسک بولین» (boolean mask) محسوب میشود. then مقداری که اگر condition برابر با True ارزیابی شود و else مقداری است که در غیر این صورت مورد استفاده قرار میگیرد.
اساسا، ()where. هر مولفه در شی که برای condition مورد استفاده قرار میگیرد را دریافت کرده، بررسی میکند که حاصل ارزیابی آن مولفه برابر با True در زمینه شرط است یا خیر، و یک ndarray باز میگرداند که بسته به آنکه کدام شرط اعمال شود دربرگیرنده then یا else است. این دستور میتواند در یک عبارت ترکیبی if-then تودرتو نوشته شود که امکان محاسبه مقادیر بر پایه شرایط گوناگون را فراهم میکند.
1>>> np.where(condition1, x1,
2 np.where(condition2, x2,
3 np.where(condition3, x3, ...)))
از این دو تابع برای پاکسازی Place of Publication استفاده میشود، زیرا این ستون دارای اشیای رشتهای است که محتوای آن به صورت زیر نمایش داده میشود.
1>>> df['Place of Publication'].head(10)
2Identifier
3206 London
4216 London; Virtue & Yorston
5218 London
6472 London
7480 London
8481 London
9519 London
10667 pp. 40. G. Bryan & Co: Oxford, 1898
11874 London]
121143 London
13Name: Place of Publication, dtype: object
قابل مشاهده است که در برخی از سطرها محل انتشار به وسیله دیگر اطلاعات غیرلازم احاطه شده. اگر به مقادیر موجود با دقت بیشتر نگاه شود، میتوان به وضوح مشاهده کرد که این شرایط تنها برای برخی سطرهایی که موقعیت انتشار آنها «London» یا «Oxford» است وجود دارد. در ادامه دو ورودی مشخص بررسی میشوند.
1>>> df.loc[4157862]
2Place of Publication Newcastle-upon-Tyne
3Date of Publication 1867
4Publisher T. Fordyce
5Title Local Records; or, Historical Register of rema...
6Author T. Fordyce
7Flickr URL http://www.flickr.com/photos/britishlibrary/ta...
8Name: 4157862, dtype: object
9
10>>> df.loc[4159587]
11Place of Publication Newcastle upon Tyne
12Date of Publication 1834
13Publisher Mackenzie & Dent
14Title An historical, topographical and descriptive v...
15Author E. (Eneas) Mackenzie
16Flickr URL http://www.flickr.com/photos/britishlibrary/ta...
17Name: 4159587, dtype: object
این دو کتاب در یک محل منتشر شدهاند، اما در نام محل یکی از آنها خط فاصله وجود دارد و در دیگری این خط وجود ندارد. برای پاکسازی این ستون در یک حرکت، میتوان از دستور ()str.contains برای دریافت ماسکهای بولین استفاده کرد. ستون را میتوان به صورت زیر پاک کرد.
1>>> pub = df['Place of Publication']
2>>> london = pub.str.contains('London')
3>>> london[:5]
4Identifier
5206 True
6216 True
7218 True
8472 True
9480 True
10Name: Place of Publication, dtype: bool
11
12>>> oxford = pub.str.contains('Oxford')
دستور بالا با np.where به صورت زیر ترکیب میشود.
1df['Place of Publication'] = np.where(london, 'London',
2 np.where(oxford, 'Oxford',
3 pub.str.replace('-', ' ')))
4
5>>> df['Place of Publication'].head()
6Identifier
7206 London
8216 London
9218 London
10472 London
11480 London
12Name: Place of Publication, dtype: object
در اینجا تابع np.where در یک ساختار تو در تو فراخوانی میشود، با condition که سریهایی از بولینهای مشاهده شده با ()str.contains است. متد ()contains به طرز مشابهی با in keyword توکار استفاده شده برای کشف وقوع یک موجودیت در تکرارپذیری (یا زیررشته در رشته) به کار میرود.
جایگزینی که استفاده میشود در واقع رشتهای است که محل مورد انتظار انتشار را نشان میدهد. همچنین، با استفاده از دستور ()str.replace خط تیرهها با فاصله (space) جایگزین و مجددا به ستون دیتافریم (DataFrame) تخصیص داده میشوند. اگرچه دادههای کثیف زیادی در این مجموعه داده وجود دارند ولی در حال حاضر تنها دو ستون مورد بررسی قرار میگیرند. ابتدا پنج ورودی اول بررسی میشوند و مشخص است که مجموعه داده در حال حاضر نسبت به زمانی که هنوز کار پاکسازی آغاز نشده بود بسیار واضحتر و شفافتر هستند.
1>>> df.head()
2 Place of Publication Date of Publication Publisher \
3206 London 1879 S. Tinsley & Co.
4216 London 1868 Virtue & Co.
5218 London 1869 Bradbury, Evans & Co.
6472 London 1851 James Darling
7480 London 1857 Wertheim & Macintosh
8
9 Title Author \
10206 Walter Forbes. [A novel.] By A. A AA
11216 All for Greed. [A novel. The dedication signed... A. A A.
12218 Love the Avenger. By the author of “All for Gr... A. A A.
13472 Welsh Sketches, chiefly ecclesiastical, to the... E. S A.
14480 [The World in which I live, and my place in it... E. S A.
15
16 Flickr URL
17206 http://www.flickr.com/photos/britishlibrary/ta...
18216 http://www.flickr.com/photos/britishlibrary/ta...
19218 http://www.flickr.com/photos/britishlibrary/ta...
20472 http://www.flickr.com/photos/britishlibrary/ta...
21480 http://www.flickr.com/photos/britishlibrary/ta...
نکته: در این لحظه، Place of Publication میتواند کاندیدای خوبی برای تبدیل به Categorical dtype باشد، زیرا میتوان مجموعه یکتا و به اندازه کافی کوچکی از شهرها را با اعداد صحیح رمزنگاری کرد. (میزان حافظه مورد استفاده برای Categorical متناسب با تعداد دستهها به علاوه طول داده است. یک شی dtype ثابتی است که طول داده را چند برابر میکند.)
پاکسازی کل مجموعه داده با استفاده از تابع applymap
در یک موقعیت خاص، میتوان مشاهده کرد که «dirt» تنها در یک ستون محلی نشده، بلکه بیشتر گسترش یافته است. نمونههایی نیز وجود دارد که در آنها اعمال یک تابع سفارشیسازی شده به هر سلول یا مولفه در دیتافریم میتواند مفید واقع شود. متد ()applymap. در کتابخانه Pandas مشابه تابع محلی ()map است و به سادگی یک تابع را بر همه دیگر عناصر موجود در دیتافریم اعمال میکند. مثال زیر در همین راستا بسیار قابل توجه است و در آن یک DataFrame از فایل مجموعه داده «university_towns.txt» ساخته شده.
1$ head Datasets/univerisity_towns.txt
2Alabama[edit]
3Auburn (Auburn University)[1]
4Florence (University of North Alabama)
5Jacksonville (Jacksonville State University)[2]
6Livingston (University of West Alabama)[2]
7Montevallo (University of Montevallo)[2]
8Troy (Troy University)[2]
9Tuscaloosa (University of Alabama, Stillman College, Shelton State)[3][4]
10Tuskegee (Tuskegee University)[5]
11Alaska[edit]
قابل ملاحظه است که اسامی ایالتها به صورت دورهای با شهر دانشگاه در آن ایالت دنبال شدهاند: StateA TownA1 TownA2 StateB TownB1 TownB2 و .... اگر به شیوهای که نام ایالتها در فایل نوشته شده توجه شود، قابل مشاهده است که همه آنها دارای زیر رشته «[edit]» هستند. میتوان از مزایای این الگو با ساخت لیستی از تاپلهای (state, city) و پوششدهی آن لیست در یک دیتافریم بهرهمند شد.
1>>> university_towns = []
2>>> with open('Datasets/university_towns.txt') as file:
3... for line in file:
4... if '[edit]' in line:
5... # Remember this `state` until the next is found
6... state = line
7... else:
8... # Otherwise, we have a city; keep `state` as last-seen
9... university_towns.append((state, line))
10
11>>> university_towns[:5]
12[('Alabama[edit]\n', 'Auburn (Auburn University)[1]\n'),
13 ('Alabama[edit]\n', 'Florence (University of North Alabama)\n'),
14 ('Alabama[edit]\n', 'Jacksonville (Jacksonville State University)[2]\n'),
15 ('Alabama[edit]\n', 'Livingston (University of West Alabama)[2]\n'),
16 ('Alabama[edit]\n', 'Montevallo (University of Montevallo)[2]\n')]
میتوان این لیست را در دیتافریم پوشش داد و ستونها را با عنوان «State» و «RegionName» تنظیم کرد. Pandas هر عنصر در لیست را دریافت کرده و State را روی مقدار سمت چپ و RegionName را روی مقدار سمت راست تنظیم میکند. مجموعه داده حاصل مشابه زیر خواهد بود:
1>>> towns_df = pd.DataFrame(university_towns,
2... columns=['State', 'RegionName'])
3
4>>> towns_df.head()
5 State RegionName
60 Alabama[edit]\n Auburn (Auburn University)[1]\n
71 Alabama[edit]\n Florence (University of North Alabama)\n
82 Alabama[edit]\n Jacksonville (Jacksonville State University)[2]\n
93 Alabama[edit]\n Livingston (University of West Alabama)[2]\n
104 Alabama[edit]\n Montevallo (University of Montevallo)[2]
میتوان این رشتهها را در حلقه بالا پاکسازی کرد، و Pandas انجام این کار را ساده میکند. در این راستا، تنها نام ایالتها و شهرها مورد نیاز است و میتوان هر چیز دیگری را پاک کرد. در حالیکه امکان استفاده از متد ()str. در Pandas وجود دارد، میتوان از ()applymap برای نگاشت یک چیز قابل فراخوانی در پایتون (Python callable) برای هر مولفه در دیتافریم بهره برد. در کد بالا از عبارت element استفاده شد، اما واقعا معنی این کار چیست؟؟ مجموعه داده «الکی» زیر مفروض است.
1 0 1
20 Mock Dataset
31 Python Pandas
42 Real Python
53 NumPy Clean
در این مثال، هر سلول (Mock، Dataset، Python، Pandas و دیگر موارد) یک مولفه است. بنابراین، ()applymap یک تابع را بر هر یک از این موارد به طور مستقل اعمال میکند. اکنون تابع بیان شده تعریف میشود.
1>>> def get_citystate(item):
2... if ' (' in item:
3... return item[:item.find(' (')]
4... elif '[' in item:
5... return item[:item.find('[')]
6... else:
7... return item
()applymap. کتابخانه Pandas تنها یک پارامتر را دریافت میکند که تابعی است (قابل فراخوانی) که باید بر هر عنصر اعمال شود.
1>>> towns_df = towns_df.applymap(get_citystate)
ابتدا، یک تابع پایتون که یک مولفه را از دیتافریم به عنوان پارامتر خود دریافت میکند تعریف میشود. درون تابع، بررسیهایی به منظور تعیین اینکه ) یا ] وجود دارد یا خیر انجام و بسته به بررسیها، مقادیر توسط تابع بازگردانده میشوند. در نهایت، تابع ()applymap در شی فراخونی میشود. اکنون دیتافریم تمیزتر است.
1>>> towns_df.head()
2 State RegionName
30 Alabama Auburn
41 Alabama Florence
52 Alabama Jacksonville
63 Alabama Livingston
74 Alabama Montevallo
متد ()applymap هر مولفه را از دیتافریم گرفته و آن را به تابع پاس میدهد، این در حالیست که مقادیر قبلی با مقدار بازگردانده شده جایگزین میشوند. به همین سادگی!
نکته: در حالیکه متد applymap. ساده و دارای کاربرد گستردهای است، میتواند زمان اجرای قابل توجهی برای مجموعه دادههای بزرگ داشته باشد، زیرا یک چیز قابل فراخوانی توسط پایتون را برای هر مولفه نگاشت میکند. در برخی موارد، این راهکار که Cython یا NumPY را به کار گیرد (که به نوبه خود تماسها را در C انجام میدهد) میتواند برای انجام عملیات برداریسازی موثرتر باشد.
تغییر نام ستونها و گذر از سطرها
اغلب، مجموعه دادههایی که افراد با آن کار میکنند دارای اسامی برای ستونها هستند که معنای آنها به سادگی قابل درک نیست، و یا حاوی اطلاعات غیر مهمی مانند تعریف عبارات موجود در مجموعه داده و پانویسها در چند سطر اول و آخر هستند. در این شرایط، دادهکاو معمولا تمایل به تغییر نام ستونها و گذر از سطرهای خاص و رسیدن به اطلاعات لازم با برچسبهای صحیح و معنادار دارد. برای آنکه چگونگی انجام این کار شفاف شود، پنج سطر اول مجموعه داده «olympics.csv» با دستور head نمایش داده میشوند.
1$ head -n 5 Datasets/olympics.csv
20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
3,? Summer,01 !,02 !,03 !,Total,? Winter,01 !,02 !,03 !,Total,? Games,01 !,02 !,03 !,Combined total
4Afghanistan (AFG),13,0,0,2,2,0,0,0,0,0,13,0,0,2,2
5Algeria (ALG),12,5,2,8,15,3,0,0,0,0,15,5,2,8,15
6Argentina (ARG),23,18,24,28,70,18,0,0,0,0,41,18,24,28,70
اکنون مجموعه داده در دیتافریم Pandas خوانده میشود.
1>>> olympics_df = pd.read_csv('Datasets/olympics.csv')
2>>> olympics_df.head()
3 0 1 2 3 4 5 6 7 8 \
40 NaN ? Summer 01 ! 02 ! 03 ! Total ? Winter 01 ! 02 !
51 Afghanistan (AFG) 13 0 0 2 2 0 0 0
62 Algeria (ALG) 12 5 2 8 15 3 0 0
73 Argentina (ARG) 23 18 24 28 70 18 0 0
84 Armenia (ARM) 5 1 2 9 12 6 0 0
9
10 9 10 11 12 13 14 15
110 03 ! Total ? Games 01 ! 02 ! 03 ! Combined total
121 0 0 13 0 0 2 2
132 0 0 15 5 2 8 15
143 0 0 41 18 24 28 70
154 0 0 11 1 2 9 12
این مجموعه داده حقیقتا کثیف است! ستونها فرم رشتهای از اعداد صحیح اندیسگذاری شده از صفر هستند. سطری که باید «سرآیند» (Header) باشد (سطری که برای نامگذاری ستونها مورد استفاده قرار میگیرد) در [olympics_df.iloc[0 قرار دارد. این مساله به این دلیل به وقوع میپیوندد که فایل CSV موجود با ۰، ۱، ۲، ...، ۱۵ آغاز شده است. همچنین، اگر منبع مجموعه داده مذکور بررسی شود، میتوان ملاحظه کرد که NaN بالا باید چیزی شبیه «Country» باشد و ? Summer باید نمایانگر «Summer Games» و !01 باید «Gold» باشد و به همین صورت. بنابراین، نیاز به انجام دو کار است:
- حذف یک سطر و تنظیم هِدِر به عنوان اولین سطر (دارای اندیس ۰)
- تغییر نام ستونها
میتوان در حال خواندن فایل CSV با پاس دادن برخی پارامترها به تابع ()read_csv از سطرها گذر و هدِر را تنظیم کرد. این تابع پارامترهای دلخواه زیادی را دریافت میکند، اما در این مورد تنها به یک مورد (header) برای حذف سطر ۰ نیاز است.
1>>> olympics_df = pd.read_csv('Datasets/olympics.csv', header=1)
2>>> olympics_df.head()
3 Unnamed: 0 ? Summer 01 ! 02 ! 03 ! Total ? Winter \
40 Afghanistan (AFG) 13 0 0 2 2 0
51 Algeria (ALG) 12 5 2 8 15 3
62 Argentina (ARG) 23 18 24 28 70 18
73 Armenia (ARM) 5 1 2 9 12 6
84 Australasia (ANZ) [ANZ] 2 3 4 5 12 0
9
10 01 !.1 02 !.1 03 !.1 Total.1 ? Games 01 !.2 02 !.2 03 !.2 \
110 0 0 0 0 13 0 0 2
121 0 0 0 0 15 5 2 8
132 0 0 0 0 41 18 24 28
143 0 0 0 0 11 1 2 9
154 0 0 0 0 2 3 4 5
16
17 Combined total
180 2
191 15
202 70
213 12
224 12
اکنون سطر صحیحی به عنوان هِدِر قرار گرفت و همه سطرهای غیر لازم حذف شدند. این نکته که چگونه Pandas نام ستونهای حاوی اسامی کشورها را از NaN به Unnamed: 0 تغییر داد قابل توجه است. برای تغییر نام ستونها، از متد ()rename دیتافریم استفاده میشود که امکان برچسب زدن یک محور برپایه نگاشت را فراهم میکند (در این مورد یک dict). اکنون کار با تعریف یک دیکشنری که اسامی ستونهای کنونی را (به عنوان کلیدی) به موارد قابل استفادهتر تبدیل میکند آغاز خواهد شد (مقادیر دیکشنری).
1>>> new_names = {'Unnamed: 0': 'Country',
2... '? Summer': 'Summer Olympics',
3... '01 !': 'Gold',
4... '02 !': 'Silver',
5... '03 !': 'Bronze',
6... '? Winter': 'Winter Olympics',
7... '01 !.1': 'Gold.1',
8... '02 !.1': 'Silver.1',
9... '03 !.1': 'Bronze.1',
10... '? Games': '# Games',
11... '01 !.2': 'Gold.2',
12... '02 !.2': 'Silver.2',
13... '03 !.2': 'Bronze.2'}
تابع ()rename در شی فراخوانی میشود.
1>>> olympics_df.rename(columns=new_names, inplace=True)
تنظیم inplace به مقدار True مشخص میکند که تغییرات به طور مستقیم روی شی انجام شوند. اکنون این مساله با قطعه کد زیر بررسی میشود.
1>>> olympics_df.head()
2 Country Summer Olympics Gold Silver Bronze Total \
30 Afghanistan (AFG) 13 0 0 2 2
41 Algeria (ALG) 12 5 2 8 15
52 Argentina (ARG) 23 18 24 28 70
63 Armenia (ARM) 5 1 2 9 12
74 Australasia (ANZ) [ANZ] 2 3 4 5 12
8
9 Winter Olympics Gold.1 Silver.1 Bronze.1 Total.1 # Games Gold.2 \
100 0 0 0 0 0 13 0
111 3 0 0 0 0 15 5
122 18 0 0 0 0 41 18
133 6 0 0 0 0 11 1
144 0 0 0 0 0 2 3
15
16 Silver.2 Bronze.2 Combined total
170 0 2 2
181 2 8 15
192 24 28 70
203 2 9 12
214 4 5 12
خلاصه مطلب
در این راهنما، چگونگی حذف اطلاعات غیر لازم از یک مجموعه داده با استفاده از تابع ()drop و چگونگی تنظیم اندیس برای مجموعه داده به نوعی که آیتمهای آن به سادگی قابل ارجاع باشند بیان شد. علاوه بر این، چگونگی پاک کردن فیلدهای object با اکسسور ()str. و نحوه پاکسازی کل مجموعه داده با استفاده از متد ()applymap مورد بررسی قرار گرفت. در پایان، چگونگی گذر از سطرها در فایل CSV و تغییر نام ستونها با استفاده از متد rename() تشریح شد. تسلط بر چگونگی پاکسازی دادهها بسیار مهم است، زیرا این کار بخش مهم و بزرگی از پروژههای علم داده محسوب میشود.
اگر مطلب بالا برای شما مفید بوده، آموزشهای زیر نیز به شما پیشنهاد میشود:
- گنجینه آموزشهای برنامه نویسی پایتون (Python)
- مجموعه آموزشهای آمار، احتمالات و دادهکاوی
- مجموعه آموزشهای یادگیری ماشین و بازشناسی الگو
- مجموعه آموزشهای شبکههای عصبی مصنوعی
- مجموعه آموزشهای هوش محاسباتی
- آموزش برنامهنویسی R و نرمافزار R Studio
- مجموعه آموزشهای برنامه نویسی متلب (MATLAB)
^^
سلام وقت بخیر
اگر دیتایی داشته باشیم و بخوایم مثلا فقط دیتا شهر تهران به نمایش نمایش بده یا برعکس همه دیتا نمایش بده به جز تهران از چه کدی میشه استفاده کرد؟