سپس با استفاده از متد ()head پنج ورودی اول پرینت میشوند، و بر همین اساس قابل مشاهده است که برخی ستونها اطلاعات مازادی فراهم میکنند که امکان دارد به وصف کتابخانه کمک کنند، اما نقشی در توصیف خود کتابها ندارند که از این جمله میتوان به Edition Statement، Corporate Author، Corporate Contributors، Former owner، Engraver، Issuance type و Shelfmarks اشاره کرد. میتوان این ستونها را به شکل زیر حذف کرد.
در بالا، لیستی تعریف شد که اسامی همه ستونهایی که هدف حذف آنها است تعیین شدهاند. سپس، از تابع ()drop روی اشیا تعیین شده استفاده میشود، و برای پارامتر inplace مقدار True و پارامتر axis برابر با ۱ قرار داده میشوند. این به Pandas میگوید که پژوهشگر تمایل دارد تغییراتی را به طور مستقیم روی اشیا انجام داده و در این راستا باید مقادیری از ستونهای شی حذف شوند. با بازرسی مجدد دیتافریم (DataFrame)، به وضوح مشهود است که ستونها حذف شدهاند.
از سوی دیگر، میتوان ستونها را با پاس دادن آنها به پارامتر columns به طور مستقیم حذف کرد، به جای آنکه برچسبها را برای حذف و axis که Pandas باید در آن به دنبال برچسبها باشد را به طور جداگانه تعیین کرد.
این «نحو» (syntax) بصریتر و دارای خوانایی بالاتر است. آنچه در اینجا انجام میشود کاملا واضح است.
تغییر Index دیتافریم
در کتابخانه Pandas پایتون، Index عملکرد آرایه NumPy را گسترش میدهد تا امکان برچسبزنی و بخشبندی متنوعتر دادهها فراهم شود. در بسیاری از موارد، استفاده از یک فیلد شناسایی دارای مقدار یکتا برای داده به عنوان اندیس آن میتواند مفید باشد. برای مثال، در مجموعه داده استفاده شده در بخش بعدی، میتوان انتظار داشت هنگامی که یک کتابدار به دنبال یک رکورد میگردد، یک شناساگر یکتا را برای کتاب وارد کند (مقادیر در ستون Identifier):
اکنون اندیس موجود در این ستون با استفاده از set_index جایگزین میشود:
نکته: بر خلاف کلید اصلی در SQL، یک Index در Pandas هیچ تضمینی بر یکتا بودن ندارد، اگرچه انجام اندیسگذاریها و ادغامهای زیاد منجر به افزایش سرعت در زمان اجرا میشوند.
میتوان به هر رکورد به شیوهای ساده و با بهرهگیری از [ ]loc دسترسی داشت. با وجود آنکه [ ]loc ممکن است به معنای واقعی کلمه بصری نباشد، اما امکان اندیسگذاری مبتنی بر برچسب (label-based indexing) را فراهم میکند که در واقع برچسبگذاری سطرها یا رکوردها بدون در نظر گرفتن موقعیت آنها است.
به عبارت دیگر، 206 اولین برچسب اندیس است. برای «دسترسی» (access) به این مقدار با استفاده از موقعیت (position) آن میتوان از [df.iloc[0 استفاده کرد که اندیسگذاری مبتنی بر موقعیت را امکانپذیر میکند. پیش از این، اندیس یک «RangeIndex» (اعداد صحیح از صفر آغاز میشدند، مشابه با range توکار در پایتون) بود. با پاس دادن یک نام به set_index، اندیس به مقدار موجود در Identifier تغییر پیدا میکند. نکته شایان توجه آن است که متغیر مجدد به شی بازگردانده شده توسط متد (...)df = df.set_index تخصیص داده شده است. این کار به این دلیل صورت میگیرد که به طور پیشفرض، متد یک کپی ویرایش شده از شی را باز گردانده و تغییرات را به طور مستقیم روی شی انجام نمیدهد. میتوان با تنظیم پارامتر inplace از این کار اجتناب کرد.
df.set_index('Identifier', inplace=True)
مرتبسازی فیلدهای داده
تاکنون، ستونهای غیر لازم حذف شدند و اندیس دیتافریم (DataFrame) به چیز معقولتری تغییر کرد. در این بخش، ستونهای مشخصی حذف میشوند و سایر موارد به یک فرمت واحد در میآیند تا ضمن ارائه درک بهتری از مجموعه داده از انسجام نیز پیروی کند. مشخصا، Date of Publication و Place of Publication به دلیل آنکه مورد نیاز نیستند حذف خواهند شد. پس از انجام بازرسی، مشخص شد که همه انواع دادهها از نوع داده object dtype هستند که تقریبا مشابه با str در پایتون محلی است. این مورد هر فیلدی که نمیتواند به خوبی به عنوان عددی یا داده دستهای فیت شود را انباشته میکند. این کار هنگامی معنادار است که پژوهشگر با دادههایی کار کند که در اصل دستهای از رشتههای کثیف هستند.
یکی از فیلدهایی که تبدیل آن به مقدار عددی دارای معنا خواهد بود، تاریخ انتشار است. بنابراین، میتوان محاسبات زیر را روی آنها انجام داد.
یک کتاب خاص ممکن است تنها یک تاریخ انتشار داشته باشد. بنابراین انجام کارهای زیر مورد نیاز است.
- حذف دادههای اضافی در آکولادها در هر جایی که [1879] 1878 وجود داشت.
- تبدیل رنج دادهها به تاریخ شروع آنها، هرجا که 63-1860، 1839 و 54-38 وجود دارد.
- تاریخهایی که کاربر درباره آنها مطمئن نیست به طور کامل حذف و با مقدار NaN در کتابخانه NumPy جایگزین میشوند، برای مثال [?1897]
- تبدیل رشته NaN به مقدار NaN در NumPy
با سنتز کرده این الگوها، میتوان از مزایای عبارت با قاعده مجرد برای استخراج سال انتشار استفاده کرد.
عبارت با قاعده بالا به معنای یافتن هر چهار رقم در آغاز رشته است که برای پیدا کردن سال انتشار کافی محسوب میشود. آنچه در بالا ارائه شد «رشته خام» (raw string) است (بدین معنا که بَکاِسلَش (backslash) دیگر یک کاراکتر فرار محسوب نمیشود)، که در واقع یک اقدام استاندارد با عبارات باقاعده است. «d\» هر رقمی را نشان میدهد، و {4} این قانون را چهار بار تکرار میکند. کاراکتر ^ شروع رشتهها را مطابقت میدهد و پرانتزها مشخص کننده یک گروه طبقهبندی شده هستند که به Pandas سیگنال میدهد که کاربر قصد دارد آن بخش از regex را استخراج کند (هدف آن است که ^ از مواردی که در آن جمله با ] آغاز میشود اجتناب کند). اکنون میتوان دید که با اجرای این regex در مجموعه داده چه اتفاقی میافتد.
نکته: افرادی که با regex آشنایی ندارند میتوانند در رابطه با نتیجه خروجی عبارت بالا در این وبسایت به بررسی بپردازند.
به لحاظ فنی این ستون همچنان دارای object dtype است، اما میتوان به سادگی نسخه عددی آن را با pd.to_numeric دریافت کرد.
این نتیجه تقریبا یکی در هر ده مورد مقدار ناموجود است که هزینه کوچکی محسوب میشود که اکنون باید پرداخته شود تا امکان انجام محاسبات در دیگر مقادیر باقیمانده وجود داشته باشد.
بسیار عالی، کار مورد نظر انجام شد.
ترکیب متدهای str با NumPy برای پاکسازی ستونها
مسالهای که در بالا ممکن است توجه افراد زیادی را به خود جلب کرده باشد استفاده از df['Date of Publication'].str است. این مشخصه راهی برای دسترسی سریع به عملیات رشتهها در Pandas محسوب میشود که به طور گسترده عملیات موجود برای رشتهها یا عبارات باقاعده کامپایل شده در پایتون محلی مانند ()split()، .replace. و ()capitalize. را تقلید میکند.
برای پاکسازی فیلد Place of Publication، میتوان متد str در کتابخانه Pandas را با np.where در NumPy ترکیب کرد که در اصل شکل بُرداری شده ماکرو ()IF در نرمافزار «اکسل» (Excel) به شمار میآید. نحو این دستور در پایتون به صورت زیر است.
در اینجا، condition نیز یک شی آرایه مانند یا «ماسک بولین» (boolean mask) محسوب میشود. then مقداری که اگر condition برابر با True ارزیابی شود و else مقداری است که در غیر این صورت مورد استفاده قرار میگیرد.
اساسا، ()where. هر مولفه در شی که برای condition مورد استفاده قرار میگیرد را دریافت کرده، بررسی میکند که حاصل ارزیابی آن مولفه برابر با True در زمینه شرط است یا خیر، و یک ndarray باز میگرداند که بسته به آنکه کدام شرط اعمال شود دربرگیرنده then یا else است. این دستور میتواند در یک عبارت ترکیبی if-then تودرتو نوشته شود که امکان محاسبه مقادیر بر پایه شرایط گوناگون را فراهم میکند.
از این دو تابع برای پاکسازی Place of Publication استفاده میشود، زیرا این ستون دارای اشیای رشتهای است که محتوای آن به صورت زیر نمایش داده میشود.
قابل مشاهده است که در برخی از سطرها محل انتشار به وسیله دیگر اطلاعات غیرلازم احاطه شده. اگر به مقادیر موجود با دقت بیشتر نگاه شود، میتوان به وضوح مشاهده کرد که این شرایط تنها برای برخی سطرهایی که موقعیت انتشار آنها «London» یا «Oxford» است وجود دارد. در ادامه دو ورودی مشخص بررسی میشوند.
این دو کتاب در یک محل منتشر شدهاند، اما در نام محل یکی از آنها خط فاصله وجود دارد و در دیگری این خط وجود ندارد. برای پاکسازی این ستون در یک حرکت، میتوان از دستور ()str.contains برای دریافت ماسکهای بولین استفاده کرد. ستون را میتوان به صورت زیر پاک کرد.
دستور بالا با np.where به صورت زیر ترکیب میشود.
در اینجا تابع np.where در یک ساختار تو در تو فراخوانی میشود، با condition که سریهایی از بولینهای مشاهده شده با ()str.contains است. متد ()contains به طرز مشابهی با in keyword توکار استفاده شده برای کشف وقوع یک موجودیت در تکرارپذیری (یا زیررشته در رشته) به کار میرود.
جایگزینی که استفاده میشود در واقع رشتهای است که محل مورد انتظار انتشار را نشان میدهد. همچنین، با استفاده از دستور ()str.replace خط تیرهها با فاصله (space) جایگزین و مجددا به ستون دیتافریم (DataFrame) تخصیص داده میشوند. اگرچه دادههای کثیف زیادی در این مجموعه داده وجود دارند ولی در حال حاضر تنها دو ستون مورد بررسی قرار میگیرند. ابتدا پنج ورودی اول بررسی میشوند و مشخص است که مجموعه داده در حال حاضر نسبت به زمانی که هنوز کار پاکسازی آغاز نشده بود بسیار واضحتر و شفافتر هستند.
نکته: در این لحظه، Place of Publication میتواند کاندیدای خوبی برای تبدیل به Categorical dtype باشد، زیرا میتوان مجموعه یکتا و به اندازه کافی کوچکی از شهرها را با اعداد صحیح رمزنگاری کرد. (میزان حافظه مورد استفاده برای Categorical متناسب با تعداد دستهها به علاوه طول داده است. یک شی dtype ثابتی است که طول داده را چند برابر میکند.)
پاکسازی کل مجموعه داده با استفاده از تابع applymap
در یک موقعیت خاص، میتوان مشاهده کرد که «dirt» تنها در یک ستون محلی نشده، بلکه بیشتر گسترش یافته است. نمونههایی نیز وجود دارد که در آنها اعمال یک تابع سفارشیسازی شده به هر سلول یا مولفه در دیتافریم میتواند مفید واقع شود. متد ()applymap. در کتابخانه Pandas مشابه تابع محلی ()map است و به سادگی یک تابع را بر همه دیگر عناصر موجود در دیتافریم اعمال میکند. مثال زیر در همین راستا بسیار قابل توجه است و در آن یک DataFrame از فایل مجموعه داده «university_towns.txt» ساخته شده.
قابل ملاحظه است که اسامی ایالتها به صورت دورهای با شهر دانشگاه در آن ایالت دنبال شدهاند: StateA TownA1 TownA2 StateB TownB1 TownB2 و .... اگر به شیوهای که نام ایالتها در فایل نوشته شده توجه شود، قابل مشاهده است که همه آنها دارای زیر رشته «[edit]» هستند. میتوان از مزایای این الگو با ساخت لیستی از تاپلهای (state, city) و پوششدهی آن لیست در یک دیتافریم بهرهمند شد.
میتوان این لیست را در دیتافریم پوشش داد و ستونها را با عنوان «State» و «RegionName» تنظیم کرد. Pandas هر عنصر در لیست را دریافت کرده و State را روی مقدار سمت چپ و RegionName را روی مقدار سمت راست تنظیم میکند. مجموعه داده حاصل مشابه زیر خواهد بود:
میتوان این رشتهها را در حلقه بالا پاکسازی کرد، و Pandas انجام این کار را ساده میکند. در این راستا، تنها نام ایالتها و شهرها مورد نیاز است و میتوان هر چیز دیگری را پاک کرد. در حالیکه امکان استفاده از متد ()str. در Pandas وجود دارد، میتوان از ()applymap برای نگاشت یک چیز قابل فراخوانی در پایتون (Python callable) برای هر مولفه در دیتافریم بهره برد. در کد بالا از عبارت element استفاده شد، اما واقعا معنی این کار چیست؟؟ مجموعه داده «الکی» زیر مفروض است.
در این مثال، هر سلول (Mock، Dataset، Python، Pandas و دیگر موارد) یک مولفه است. بنابراین، ()applymap یک تابع را بر هر یک از این موارد به طور مستقل اعمال میکند. اکنون تابع بیان شده تعریف میشود.
()applymap. کتابخانه Pandas تنها یک پارامتر را دریافت میکند که تابعی است (قابل فراخوانی) که باید بر هر عنصر اعمال شود.
ابتدا، یک تابع پایتون که یک مولفه را از دیتافریم به عنوان پارامتر خود دریافت میکند تعریف میشود. درون تابع، بررسیهایی به منظور تعیین اینکه ) یا ] وجود دارد یا خیر انجام و بسته به بررسیها، مقادیر توسط تابع بازگردانده میشوند. در نهایت، تابع ()applymap در شی فراخونی میشود. اکنون دیتافریم تمیزتر است.
متد ()applymap هر مولفه را از دیتافریم گرفته و آن را به تابع پاس میدهد، این در حالیست که مقادیر قبلی با مقدار بازگردانده شده جایگزین میشوند. به همین سادگی!
نکته: در حالیکه متد applymap. ساده و دارای کاربرد گستردهای است، میتواند زمان اجرای قابل توجهی برای مجموعه دادههای بزرگ داشته باشد، زیرا یک چیز قابل فراخوانی توسط پایتون را برای هر مولفه نگاشت میکند. در برخی موارد، این راهکار که Cython یا NumPY را به کار گیرد (که به نوبه خود تماسها را در C انجام میدهد) میتواند برای انجام عملیات برداریسازی موثرتر باشد.
تغییر نام ستونها و گذر از سطرها
اغلب، مجموعه دادههایی که افراد با آن کار میکنند دارای اسامی برای ستونها هستند که معنای آنها به سادگی قابل درک نیست، و یا حاوی اطلاعات غیر مهمی مانند تعریف عبارات موجود در مجموعه داده و پانویسها در چند سطر اول و آخر هستند. در این شرایط، دادهکاو معمولا تمایل به تغییر نام ستونها و گذر از سطرهای خاص و رسیدن به اطلاعات لازم با برچسبهای صحیح و معنادار دارد. برای آنکه چگونگی انجام این کار شفاف شود، پنج سطر اول مجموعه داده «olympics.csv» با دستور head نمایش داده میشوند.
$ head -n 5 Datasets/olympics.csv
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
,? Summer,01 !,02 !,03 !,Total,? Winter,01 !,02 !,03 !,Total,? Games,01 !,02 !,03 !,Combined total
Afghanistan (AFG),13,0,0,2,2,0,0,0,0,0,13,0,0,2,2
Algeria (ALG),12,5,2,8,15,3,0,0,0,0,15,5,2,8,15
Argentina (ARG),23,18,24,28,70,18,0,0,0,0,41,18,24,28,70
اکنون مجموعه داده در دیتافریم Pandas خوانده میشود.
این مجموعه داده حقیقتا کثیف است! ستونها فرم رشتهای از اعداد صحیح اندیسگذاری شده از صفر هستند. سطری که باید «سرآیند» (Header) باشد (سطری که برای نامگذاری ستونها مورد استفاده قرار میگیرد) در [olympics_df.iloc[0 قرار دارد. این مساله به این دلیل به وقوع میپیوندد که فایل CSV موجود با ۰، ۱، ۲، ...، ۱۵ آغاز شده است. همچنین، اگر منبع مجموعه داده مذکور بررسی شود، میتوان ملاحظه کرد که NaN بالا باید چیزی شبیه «Country» باشد و ? Summer باید نمایانگر «Summer Games» و !01 باید «Gold» باشد و به همین صورت. بنابراین، نیاز به انجام دو کار است:
- حذف یک سطر و تنظیم هِدِر به عنوان اولین سطر (دارای اندیس ۰)
- تغییر نام ستونها
میتوان در حال خواندن فایل CSV با پاس دادن برخی پارامترها به تابع ()read_csv از سطرها گذر و هدِر را تنظیم کرد. این تابع پارامترهای دلخواه زیادی را دریافت میکند، اما در این مورد تنها به یک مورد (header) برای حذف سطر ۰ نیاز است.
اکنون سطر صحیحی به عنوان هِدِر قرار گرفت و همه سطرهای غیر لازم حذف شدند. این نکته که چگونه Pandas نام ستونهای حاوی اسامی کشورها را از NaN به Unnamed: 0 تغییر داد قابل توجه است. برای تغییر نام ستونها، از متد ()rename دیتافریم استفاده میشود که امکان برچسب زدن یک محور برپایه نگاشت را فراهم میکند (در این مورد یک dict). اکنون کار با تعریف یک دیکشنری که اسامی ستونهای کنونی را (به عنوان کلیدی) به موارد قابل استفادهتر تبدیل میکند آغاز خواهد شد (مقادیر دیکشنری).
تابع ()rename در شی فراخوانی میشود.
تنظیم inplace به مقدار True مشخص میکند که تغییرات به طور مستقیم روی شی انجام شوند. اکنون این مساله با قطعه کد زیر بررسی میشود.
خلاصه مطلب
در این راهنما، چگونگی حذف اطلاعات غیر لازم از یک مجموعه داده با استفاده از تابع ()drop و چگونگی تنظیم اندیس برای مجموعه داده به نوعی که آیتمهای آن به سادگی قابل ارجاع باشند بیان شد. علاوه بر این، چگونگی پاک کردن فیلدهای object با اکسسور ()str. و نحوه پاکسازی کل مجموعه داده با استفاده از متد ()applymap مورد بررسی قرار گرفت. در پایان، چگونگی گذر از سطرها در فایل CSV و تغییر نام ستونها با استفاده از متد rename() تشریح شد. تسلط بر چگونگی پاکسازی دادهها بسیار مهم است، زیرا این کار بخش مهم و بزرگی از پروژههای علم داده محسوب میشود.