تحضير البيانات باستخدام مكتبة Pandas بلغة بايثون

ترجمة: فاضل حمود.

تدقيق: أثير الحارثي.

في هذا الدرس، سنتعرف على أهمية المعالجة المسبقة للبيانات وكيفية القيام بذلك باستخدام مكتبة Pandas بلغة بايثون.

سيرشدك هذا الدرس إلى بعض المفاهيم والخطوات الأساسية لتحضير البيانات باعتبارها الخطوة الأولى بعد الحصول على أي نوع من أنواع البيانات. يتركز الهدف من هذه المرحلة بمعالجة البيانات الخام وجعلها في شكل يمكن تحليله بسهولة ودقة. يؤدي التحضير الجيد للبيانات إلى التحليل الفعال حيث يمكن إزالة الأخطاء أو نقص البيانات والتي يمكن أن تحدث أثناء عملية جمع البيانات وبالتالي يمكن أن تساعد مرحلة تحضير البيانات في إزالة بعض التحيز الناتج عن رداءة جودة البيانات. لذلك يقضي محلل البيانات الكثير من وقته في هذه الخطوة المهمة.

تتضمن مرحلة تحضير البيانات عدة خطوات منها: تحميل البيانات، تنظيف البيانات (إزالة البيانات غير الضرورية أو البيانات الخاطئة)، تحويل تنسيقات البيانات، إعادة ترتيب البيانات وغير ذلك. 

وفي هذا الدرس، سنعمل على بعض هذه الخطوات باستخدام مكتبة Pandas الخاصة بلغة البرمجة بايثون (Python) لتحضير البيانات.

تأكد من إلمامك بمفاهيم مثل DataFrame و Series، وما إلى ذلك، حيث ستتكرر هذه المصطلحات كثيرا ً في هذا الدرس. 

في هذا الدرس:

  • سنبدأ بمقدمة قصيرة عن Pandas (المكتبة المستخدمة).
  • ثم سنقوم بتحميل البيانات.
  • بعد ذلك، سنستكشف البيانات المفقودة وكيفية التعامل معها. سنتطرق إلى التعامل مع البيانات المفقودة وطرق استبدالها.
  • سنتعلم بعد ذلك بعض حيل تحويل البيانات منها: استبدال القيم، ودمج سلاسل البيانات، وإضافة المعرفة إلى مجموعة البيانات باستخدام دالة map، وتقسيم البيانات المستمرة (Continuous data)، وأخيراً حول المتغيرات الوهمية (Dummy Variables) والترميز الأحادي (One-hot encoding)

لنبدأ بمقدمة قصيرة عن Pandas

مكتبة Pandas

Pandas هي مكتبة برمجية مكتوبة للغة بايثون وتعد من أشهر المكتبات في مجتمع علوم البيانات حيث توفر هياكل بيانات قوية ومعبرة ومرنة تجعل معالجة البيانات وتحليلها أمرا ً سهلاً. علاوة على ذلك، إذا كان لديك حالة استخدام محددة وجديدة، فيمكنك مشاركتها على موقع pandas GitHub – في الواقع، هذه هي الطريقة التي ظهرت منها معظم الدوال في Pandas، من خلال حالات استخدام واقعية.

ولاستخدام مكتبة Pandas، علينا استيرادها من خلال الأمر التالي:

import pandas as pd 

 

تحميل البيانات

الخطوة الأولى لتحضير البيانات هي استيرادها من خلال الأمر التالي. إذا كان لديك ملف بصيغة .csv، فيمكنك تحميله بسهولة باستخدام الأمر التالي:

('data = pd.read_csv('path_to_file.csv

في هذا الدرس، سننشئ بيانات صغيرة سهلة الفهم لغرض التطبيق.

البيانات المفقودة (missing data)

معالجة البيانات المفقودة

وتعتبر هذه الخطوة من أشهر المشاكل التي قد تواجه عالم البيانات والتي عليه التعامل معها بحذر. ويمكن أن تظهر البيانات المفقودة في البيانات لأسباب متعددة منها: عدم إضافة البيانات الخاصة بالحقل المحدد بواسطة المستخدم أو تطبيق جمع البيانات، أو فقد البيانات أثناء النقل يدويًا، أو خطأ برمجي وغير ذلك. ومن الضروري أحيانا ً فهم سبب فقدان بعض البيانات لمعرفة كيفية التعامل معها وهذا سيأتي لاحقا ً في هذا الدرس. في الوقت الحالي، سنركز على ما يجب فعله عندما نواجه بعض البيانات المفقودة.

بالنسبة للبيانات الرقمية، يستخدم Pandas قيمة NaN (ليست برقم) لتمثيل البيانات المفقودة. إنها قيمة فريدة تم تحديدها ضمن مكتبة Numpy لذلك سنحتاج إلى استيراد هذه المكتبة. NaN هي علامة تشير للقيمة الافتراضية المفقودة لأسباب تتعلق بالسرعة والسهولة الحاسوبية. هي قيمة تنبيهية، بمعنى أنها بيانات وهمية يمكن اكتشافها بسهولة والعمل معها باستخدام دوال Pandas.

لنقم باستكشاف بعض البيانات:

import numpy as np 
([data = pd.Series([0, 1, 2, 3, 4, 5, np.nan, 6, 7, 8
# لنرى إذا ما كان هناك قيم مفقودة وتحديد مكانها
()data.isnull
0    False
1    False
2    False
3    False
4    False
5    False
6     True
7    False
8    False
9    False
dtype: bool

 

كما يتضح لدينا أعلاه، استخدمنا الدالة ()isnull والتي تُرجع قيمة منطقية صحيحة أو خاطئة (True or False). صحيحة (True): عندما تكون البيانات الموجودة في هذا المكان مفقودة أو (NaN). ولاكتشاف العكس نستخدم دالة ()notnull.

()data.notnull
0     True  1     True  2     True  3     True  4     True  5     True  6    False  7     True  8     True  9     True  dtype: bool

بالإضافة إلى ذلك، يمكننا استخدام دالة ()dropna لتصفية البيانات المفقودة وإزالة القيمة الفارغة (المفقودة) ومشاهدة القيم غير المفقودة فقط. ومع ذلك، لا يتم حذف قيمة NaN بالفعل ولا يزال من الممكن العثور عليها في البيانات الأصلية عن طريق الأمر التالي.

()data.dropna
0    0.0  1    1.0  2    2.0  3    3.0  4    4.0  5    5.0  7    6.0  8    7.0  9    8.0  dtype: float64

 

وهناك خياران “لحذف” قيمة مفقودة (NaN): إما تخزين البيانات الجديدة (بدون NaN) باسم جديد بحيث لا يتم التعديل على البيانات الأصلية أو تغيير القيمة الافتراضية لخيار inplace. القيمة الافتراضية ل inplace هي false.

()not_null_data = data.dropna
not_null_data
0    0.0  1    1.0  2    2.0  3    3.0  4    4.0  5    5.0  7    6.0  8    7.0  9    8.0  dtype: float64

ويمكن أن تكون البيانات أكثر تعقيدا ً وتكون ذات بعدين، مما يعني أنها تحتوي على صفوف وأعمدة. Pandas تدعم معالجة هذا النوع من البيانات أيضا

,[data_dim = pd.DataFrame([[1,2,3,np.nan],[4,5,np.nan,np.nan
([[7,np.nan,np.nan,np.nan],[np.nan,np.nan,np.nan,np.nan] 
data_dim

لنفترض الآن أننا نريد فقط حذف الصفوف أو الأعمدة التي تكون كلها فارغة أو فقط تلك التي تحتوي على قدر معين من القيم المفقودة. تحقق من السيناريوهات المختلفة، تذكر أن الحذف لا يحدث في البيانات الأصلية، وبالتالي لا يتم العبث بمجموعة البيانات الحقيقية. انتبه إلى الخيارات التي يتم تحديدها داخل دالة ()dropna لتحديد كيفية حذف البيانات المفقودة.

()data_dim.dropna

 

نلاحظ من النتيجة بأنه تم حذف جميع الصفوف ولاأعمدة لأنها تحتوي على قيمة مفقودة واحدة على الأقل.

('data_dim.dropna(how = 'all

 

# القيمة الافتراضية هي 0 – والتي تشير إلى الصفوف
('data_dim.dropna(axis = 1, how = 'all
(data_dim.dropna(axis = 1, thresh = 2

 

data_dim.dropna(thresh = 2) 

الآن تعرفنا على كيفية تحديد القيم المفقودة وحذفها – سواء إذا أردنا رؤية البيانات الناتجة أو القيام بحذف فعلي. في كثير من الحالات، لا يعد حذف القيم الفارغة خيارا ً منطقيا ً، وقد نرغب في ملء البيانات المفقودة ببعض القيم الأخرى. لنرى كيف يمكننا القيام بذلك.

استبدال البيانات المفقودة

لاستبدال أو “ملء” البيانات الفارغة، يمكننا استخدام دالة ()fillna. على سبيل المثال، دعونا نحاول استخدام نفس البيانات السابقة ونحاول ملء قيم NaN بـ 0.

data_dim

 

(data_dim_fill = data_dim.fillna(0
data_dim_fill

 

ومثلما هو الحال مع ()Dropna، يمكنك أيضاً القيام بالعديد من الأشياء الأخرى اعتمادا ً على نوع الخيارات التي تحددها داخل الدالة. تذكر أيضًا بأن خيار inplace = True سيؤدي إلى إجراء التغيير على البيانات الأصلية.

data_dim_fill = data_dim.fillna({0: 0, 1: 8, 2: 9, 3: 10}) data_dim_fill

يمكنك تحديد قيمة لخيار ‘method’ في ()fillna التي ستقوم تلقائيا ً بتعبئة القيم المفقودة بالقيمة السابقة (ffill أو pad) أو التالية (bfill أو backfill).

('data_dim_fill = data_dim.fillna(method='ffill 
data_dim_fill

يمكنك أيضًا تحديد عدد عمليات التعبئة. على سبيل المثال، املأ مكانين فقط في الأعمدة. إذا قمت بتحديد خيار axis = 1، فسيتم ملء قيمة بحسب الصفوف بدل الأعمدة.

here data_dim_fill = data_dim.fillna(method='ffill', limit = 2) data_dim_fill
data_dim_fill = data_dim.fillna(axis = 1, method='ffill') data_dim_fill

بحسب معرفتك بالبيانات، يمكنك استخدام دالة ()fillna بعدة طرق أخرى غير ملئها بأرقام محددة. يمكنك تعبئتها باستخدام المتوسط باستخدام ()mean أو الوسيط باستخدام ()median.

data_dim
(()data_dim_fill = data_dim.fillna(data_dim.mean
data_dim_fill

 

تحويل البيانات واستبدالها

حتى الآن، تعاملنا مع البيانات المفقودة (NaN) فقط، ولكن في بعض الحالات نرغب باستبدال قيمة معينة (غير مفقودة) بقيمة مختلفة. وفي حالات أخرى يتم استبدال القيمة المفقودة برقم عشوائي محدد، وبالتالي يجب معاملتها على أنها مفقودة NaN بدلاً من التعامل معها كرقم. في هذه الحالة يمكننا استخدام دالة ()replace.

([data = pd.Series([1,2,-99,4,5,-99,7,8,-99 
data
0     1  1     2  2   -99  3     4  4     5  5   -99  6     7  7     8  8   -99  dtype: int64
(data.replace(-99, np.nan
0    0.0  1    1.0  2    2.0  3    3.0  4    4.0  5    5.0  7    6.0  8    7.0  9    8.0  dtype: float64

كما نشاهد، لن نرى قيمة -99 بعد الآن بسبب استبدالها بـ NaN. وبالمثل، يمكنك تحديد عدة قيم واستبدالها بقيم أخرى. وللقيام بذلك، سننشئ سلسلة بيانات أخرى ثم ندمج سلسلة البيانات الأصلية بالسلسلة الجديدة ثم نطبق أمر استبدال القيم المتعددة.

دمج سلاسل البيانات في Pandas

لدمج عدة سلاسل بيانات يمكننا استخدام الدالة ()concat في Pandas. ولمتابعة التسلسل الرقمي (Indexing) بعد الدمج، يمكنك تحديد خيار ignore_index = True.

([new_data = pd.Series([-100, 11, 12, 13 
combined_series = pd.concat([data, new_data], ignore_index = True (
combined_series
0       1  1       2  2     -99  3       4  4       5  5     -99  6       7  7       8  8     -99  9    -100  10     11  11     12  12     13  dtype: int64
data_replaced = combined_series.replace([-99, -100], np.nan) data_replaced
0      1.0  1      2.0  2      NaN  3      4.0  4      5.0  5      NaN  6      7.0  7      8.0  8      NaN  9      NaN  10    11.0  11    12.0  12    13.0  dtype: float64
data_replaced = combined_series.replace({-99: np.nan, -100: 0}) Same as: new_data.replace([-99, -100], [np.nan,0 #
data_replaced
0      1.0  1      2.0  2      NaN  3      4.0  4      5.0  5      NaN  6      7.0  7      8.0  8      NaN  9      0.0  10    11.0  11    12.0  12    13.0  dtype: float64

 

إضافة المعرفة – دالة المطابقة (Map)

في بعض الحالات، قد ترغب في إضافة المزيد من الأفكار بناءً على منطق معين. ولعمل ذلك يمكننا استخدام دالة المطابقة (map) ومجموعة من الدوال الأخرى. يمكن أن تصبح بعض العمليات المنطقية أكثر تعقيدا ً ولكن دعونا نوضح هذه الفكرة عن طريق المثال التالي.

لنفترض أن لدينا بيانات من الأرقام يقابلها الرقم كتابة ً باللغة الإنجليزية.

data_number = pd.DataFrame({'english': ['zero','one','two','three','four','five'], 'digits': [0,1,2,3,4,5
({[[
data_number

يمكنك استخدام دالة ()map لإضافة عمود يحتوي على قيمة معينة في حال تحقق الشرط المطلوب في هذا المثال الشرط أن يكون الرقم باللغة الإنجليزية من مضاعفات الرقم 2. ولكن ما هي القيمة التي سيتم إضافتها إذا لم يتحقق الشرط (أي الرقم ليس من مضاعفات 2)؟ لنكتشف ذلك.

{'english_to_multiple = {'two': 'yes',  'four': 'yes
data_number['multiple'] = data_number['english'].map(english_to_multiple)e
data_number

 

يتضح لدينا من خلال النتائج أنه عندما لا يتحقق الشرط نجد القيمة NaN، وقد ذكرنا مسبقا كيفية التعامل مع القيم المفقودة. كان هذا مثالا بسيطا لكيفية استخدام دالة ()map ولكن هناك عدد لا محدود من الأفكار لتطبيق هذه الدالة والاستفادة من امكانياتها. 

التصنيف أو التقدير (Discretization) – دالة ()cut

في بعض الأحيان قد نرغب في التصنيف بناءً على منطق معين (شرط معين) ووضع جميع البيانات في فئات منفصلة لغرض التحليل. يمكنك استخدام دالة ()cut لهذا الغرض. على سبيل المثال، لنقم أولاً بإنشاء بيانات تحتوي على 30 رقما ً عشوائيا ً بين 1 و100.

import random 
(data = random.sample(range(1, 101), 30
data
[45,   39,   25,   83,   27,   6,   73,   43,   36,   93,   97,   17,   15,   99,   37,   5,   31,   22,   65,   30,   3,   26,   91,   12,   52,   76,   63,   84,   59,   53]

لنفترض أننا نريد تصنيف هذه الأرقام إلى مجموعات نحددها بأنفسنا: فمثلا ً الأرقام بين 1-25 ، 25-35 ، 40-60 ثم 60-80 ثم الباقي. في البداية دعونا نعرف المجموعات.

[bucket = [1, 25, 35, 60, 80, 100
(cut_data = pd.cut(data, bucket
cut_data
[(35, 60], (35, 60], (1, 25], (80, 100], (25, 35], ..., (60, 80], (60, 80], (80, 100], (35, 60], (35, 60]]  Length: 30  Categories (5, interval[int64]): [(1, 25] < (25, 35] < (35, 60] < (60, 80] < (80, 100]]

دالة ()cut مفيدة للغاية، وهناك الكثير من الأمور التي يمكننا القيام بها باستخدام هذه الدالة.

المتغيرات الوهمية (Dummy variables) و الترميز الأحادي (One-Hot Encodings)

هذا الموضوع مفيد جدا ً عند الرغبة بتحويل بعض البيانات الفئوية (Categorical) إلى قيم عددية بحيث يمكنك استخدامها في نماذج تحليل البيانات، خاصة عند القيام ببناء نماذج تعلم الآلة، حيث يكون مفهوم الترميز الأحادي (One-Hot Encodings) شائعا. باستخدام كلمات تقنية أكثر: الترميز الأحادي هو عملية تحويل القيم الفئوية إلى متجه (Vector) رقمي أحادي البعد.

يمكن القيام بذلك باستخدام دالة ()get_dummies. إذا كان أحد الأعمدة في البيانات لديك يحتوي على عدد معين من القيم المميزة، نشير لهذا العدد ب n، فستشتق الدالة مصفوفة تحتوي على نفس العدد من الأعمدة وجميع القيم عبارة عن 1 أو 0. دعونا نوضح هذه الفكرة بمثال لتتضح الصورة بشكل أفضل.

(('data = pd.Series(list('abcdababcdabcd
data
0     a  1     b  2     c  3     d  4     a  5     b  6     a  7     b  8     c  9     d  10    a  11    b  12    c  13    d  dtype: object

 

لنفترض الآن أننا نريد أن يكون لدينا متجهات فردية تشير إلى ظهور كل حرف لإدخاله إلى دالة أخرى. كمثال:  [1,0,0,0,1,0,1,0,0,0,1,0,0,0]=  ‘a’، ويعني: إذا كانت القيمة الأصلية  ‘a’  نضع 1 و 0 في عكس ذلك. استخدام دالة ()get_dummies سيجعل المهمة أسهل.

pd.get_dummies(data) 

 

هذا مثال بسيط لمساعدتك على البدء في استخدام هذه الدالة. في تطبيقات العالم الحقيقي، يمكن أن ينتمي الحرف إلى فئات مختلفة في نفس الوقت، وبالتالي سيتعين عليك تعلم طرق أكثر تعقيدا ً. أيضا ً بالنسبة للبيانات الأكبر حجما ً، قد لا تكون هذه الدالة فعالة جدًا من حيث السرعة ولكنها فعالة جدا ً لتطبيق الكثير من الأفكار. 

ختاما ً، لقد وصلنا إلى نهاية هذا الدرس، وتعلمنا سويا بعض الأدوات الأساسية لبدء الخطوة الأولى في مسار تحضير البيانات. بالتأكيد هناك العديد من المهارات الأخرى التي يجب تعلمها من أجل مرحلة إعداد البيانات منها معرفة كيفية اكتشاف القيم المتطرفة وتصفيتها في مجموعة البيانات. غالبًا ما تكون مرحلة تحضير البيانات في البيانات الحقيقية أكثر تعقيدا ً وقد تحتاج إلى حل مختلف حسب البيانات التي تعمل عليها ، ولكن الفكرة هي أن تبدأ التطبيق على بعض البيانات الصغيرة نسبيا ً لبناء المعرفة والمهارات اللازمة ومن ثم التدرج في التدرب على أنواع مختلفة من البيانات ،تجربة أدوات جديدة.

 

###

Source: DataCamp tutorials/Sejal Jaiswal