عالم البيانات Data Scientist – الجزء الثاني

الجزء الثاني

1٬123

في هذا المقال، سوف نستكمل ما بداناه في ايضاح ما تحتاجه لتكون عالم بيانات لا أكثر ولا أقل. pokemon.csv هي البيانات التي نعمل عليها يمكنك تحميلها من هنا.

المحتوى:

  1. تنظيف البيانات
    1. تشخيص بيانات للتنظيف
    2. تحليل البيانات الاستكشافي
    3. تحليل البيانات الاستطلاعي
    4. ترتيب البيانات
    5. تجميع البيانات
    6. بيانات متسلسلة
    7. أنواع البيانات
    8. البيانات المفقودة والاختبار مع التأكيد

1- تنظيف البيانات

i- تشخيص بيانات للتنظيف

  • نحن بحاجة إلى تشخيص وتنظيف البيانات قبل الاستكشافها.

    البيانات الغير نظيفة قد تكون:

    • تناقض اسم العمود مثل حالة الحرف في الحيز العلوي او السفلي والمسافات بين الكلمات
    • بيانات مفقودة.
    • لغة مختلفة.

    سنستخدم وظائف الرأس()head والذيل()tail والأعمدةcolumns والشكلshape والمعلوماتinfo لتشخيص البيانات.

    الرأس: اول خمس صفوف.

    data = pd.read_csv('../input/pokemon.csv')
    data.head() # head shows first 5 rows

    النتيجة.

    # Name Type 1 Type 2 HP Attack Defense Sp. Atk Sp. Def Speed Generation Legendary
    0 1 Bulbasaur Grass Poison 45 49 49 65 65 45 1 False
    1 2 Ivysaur Grass Poison 60 62 63 80 80 60 1 False
    2 3 Venusaur Grass Poison 80 82 83 100 100 80 1 False
    3 4 Mega Venusaur Grass Poison 80 100 123 122 120 80 1 False
    4 5 Charmander Fire NaN 39 52 43 60 50 65 1 False

    الذيل: اخر خمس صفوف.

    # tail shows last 5 rows
    data.tail()

    النتيجة.

    # Name Type 1 Type 2 HP Attack Defense Sp. Atk Sp. Def Speed Generation Legendary
    795 796 Diancie Rock Fairy 50 100 150 100 150 50 6 True
    796 797 Mega Diancie Rock Fairy 50 160 110 160 110 110 6 True
    797 798 Hoopa Confined Psychic Ghost 80 110 60 150 130 70 6 True
    798 799 Hoopa Unbound Psychic Dark 80 160 60 170 130 80 6 True
    799 800 Volcanion Fire Water 80 110 120 130 90 70 6 True

    العمود: أسماء الصفات.

    data.columns

    النتيجة.

    Index(['#', 'Name', 'Type 1', 'Type 2', 'HP', 'Attack', 'Defense', 'Sp. Atk',
    'Sp. Def', 'Speed', 'Generation', 'Legendary'],
    dtype='object')

    الشكل: ابعاد البيانات.

    data.shape

    النتيجة.

    (800, 12)

    المعلومات: عدد الصفوف والاعمدة وحجمهم في الذاكرة.

    data.info()

    النتيجة.

    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 800 entries, 0 to 799
    Data columns (total 12 columns):
    # 800 non-null int64
    Name 799 non-null object
    Type 1 800 non-null object
    Type 2 414 non-null object
    HP 800 non-null int64
    Attack 800 non-null int64
    Defense 800 non-null int64
    Sp. Atk 800 non-null int64
    Sp. Def 800 non-null int64
    Speed 800 non-null int64
    Generation 800 non-null int64
    Legendary 800 non-null bool
    dtypes: bool(1), int64(8), object(3)
    memory usage: 69.6+ KB

ii- تحليل البيانات الاستكشافي

عدد التكرارات: ()value_counts.

القيم المتطرفة: القيمة التي تكون أعلى أو أقل بكثير من بقية البيانات.

بافتراض ان x3 هو %75، x1 هو %25.

القيم المتطرفة ستكون أكبر من x3 او أصغر من x1.

سوف نستخدم وظائف متعددة لوصف هذه الإحصاءات:

عدد الوحدات count.

القيمة المتوسطة للوحدات mean.

الانحراف المعياري std.

اقل قيمة للوحدات min.

الربع الأول للبيانات من حيث القيمة %25.

الربع الثاني للبيانات من حيث القيمة %50.

الربع الثالث للبيانات من حيث القيمة %75.

اعلي قيمة للوحدات max.

باعتبار هذه السلسلة من الأرقام.

1,4,5,6,8,9,11,12,13,14,15,16,17

فالمتوسط سيكون 11.

الربع الأول ينتهي عند المتوسط بين البداية ومتوسط السلسلة كلها أي ما بين 1 و11 أي 6.

الربع الثالث ينتهي عند المتوسط بين النهاية ومتوسط السلسلة كلها أي ما بين 11 و17 أي 14.

مثال:dropna هي صفة تسمح لنا ان نعد القيم التي لا تحمل قيمة داخلها او لا.

# For example lets look frequency of pokemom types
print(data['Type 1'].value_counts(dropna =False)) # if there are nan values that also be counted
# As it can be seen below there are 112 water pokemon or 70 grass pokemon

النتيجة.”سيظهر لنا الان جميع انواع الPokemon و عدد الوحدات حسب كل نوع”

Water 112
Normal 98
Grass 70
Bug 69
Psychic 57
Fire 52
Electric 44
Rock 44
Ground 32
Dragon 32
Ghost 32
Dark 31
Poison 28
Fighting 27
Steel 27
Ice 24
Fairy 17
Flying 4
Name: Type 1, dtype: int64

لنعرض بعض الإحصاءات المهمة حول هذه البيانات.

data.describe() #ignore null entries

النتيجة. “ستلاخظ انه تجاهل الوحدات ذات قيمة “NAN” اي الوحدات الفارغة”

# HP Attack Defense Sp. Atk Sp. Def Speed Generation
count 800.0000 800.000000 800.000000 800.000000 800.000000 800.000000 800.000000 800.00000
mean 400.5000 69.258750 79.001250 73.842500 72.820000 71.902500 68.277500 3.32375
std 231.0844 25.534669 32.457366 31.183501 32.722294 27.828916 29.060474 1.66129
min 1.0000 1.000000 5.000000 5.000000 10.000000 20.000000 5.000000 1.00000
25% 200.7500 50.000000 55.000000 50.000000 49.750000 50.000000 45.000000 2.00000
50% 400.5000 65.000000 75.000000 70.000000 65.000000 70.000000 65.000000 3.00000
75% 600.2500 80.000000 100.000000 90.000000 95.000000 90.000000 90.000000 5.00000
max 800.0000 255.000000 190.000000 230.000000 194.000000 230.000000 180.000000 6.00000

 

 

 

 

 

 

 

 

 

 

iii- تحليل البيانات الاستطلاعي

مخطط الصندوق: تصور للإحصاءات الأساسية مثل القيم المتطرفة أو الحد الأدنى / الحد الأقصى أو الكميّات.

على سبيل المثال: قارن هجوم Pokemon الأسطورية أم لا.

خط أسود في الأعلى هو الحد الأقصى.

الخط الأزرق في الأعلى %75.

الخط الأحمر متوسط %50.

الخط الأزرق في الجزء السفلي هو %25.

خط أسود في الأسفل هو الحد الأدنى.

لا توجد القيم المتطرفة.

data.boxplot(column='Attack',by = 'Legendary')

النتيجة.

<matplotlib.axes._subplots.AxesSubplot at 0x7fd37c662eb8>

iv- ترتيب البيانات

سوف نرتب البيانات عن طريق وظيفة ()melt “ذوبان”، يبدو وصفها غريبا بعض الشيء لكن ستتضح أكثر مع مثال.

data_new = data.head() # I only take 5 rows into new data
data_new

النتيجة.

# Name Type 1 Type 2 HP Attack Defense Sp. Atk Sp. Def Speed Generation Legendary
0 1 Bulbasaur Grass Poison 45 49 49 65 65 45 1 False
1 2 Ivysaur Grass Poison 60 62 63 80 80 60 1 False
2 3 Venusaur Grass Poison 80 82 83 100 100 80 1 False
3 4 Mega Venusaur Grass Poison 80 100 123 122 120 80 1 False
4 5 Charmander Fire NaN 39 52 43 60 50 65 1 False

id_vars = ما لا نريد أن نذوبه.

value_vars = ما نريد أن نذوب.

melted = pd.melt(frame=data_new,id_vars = 'Name', value_vars= ['Attack','Defense'])
melted

النتيجة.

Name variable value
0 Bulbasaur Attack 49
1 Ivysaur Attack 62
2 Venusaur Attack 82
3 Mega Venusaur Attack 100
4 Charmander Attack 52
5 Bulbasaur Defense 49
6 Ivysaur Defense 63
7 Venusaur Defense 83
8 Mega Venusaur Defense 123
9 Charmander Defense 43

 

 

 

 

 

 

 

 

 

 

 

 

v- تجميع البيانات

عكس ذوبان.

الفهرس هو الاسم.

أريد جعل الأعمدة متغيرة.

القيم النهائية في الأعمدة هي القيمة.

melted.pivot(index = 'Name', columns = 'variable',values='value')

النتيجة.

variable Attack Defense
Name
Bulbasaur 49 49
Charmander 52 43
Ivysaur 62 63
Mega Venusaur 100 123
Venusaur 82 83

 

 

 

 

 

 

 

 

vi- بيانات متسلسلة

يمكننا وصل اثنين من إطارات البيانات.

من ثمًِ يضيف مخططات البيانات في الصفوف axis=0.

data1 = data.head()
data2= data.tail()
conc_data_row = pd.concat([data1,data2],axis =0,ignore_index =True) # axis = 0 : adds dataframes in row
conc_data_row

النتيجة.

# Name Type 1 Type 2 HP Attack Defense Sp. Atk Sp. Def Speed Generation Legendary
0 1 Bulbasaur Grass Poison 45 49 49 65 65 45 1 False
1 2 Ivysaur Grass Poison 60 62 63 80 80 60 1 False
2 3 Venusaur Grass Poison 80 82 83 100 100 80 1 False
3 4 Mega Venusaur Grass Poison 80 100 123 122 120 80 1 False
4 5 Charmander Fire NaN 39 52 43 60 50 65 1 False
5 796 Diancie Rock Fairy 50 100 150 100 150 50 6 True
6 797 Mega Diancie Rock Fairy 50 160 110 160 110 110 6 True
7 798 Hoopa Confined Psychic Ghost 80 110 60 150 130 70 6 True
8 799 Hoopa Unbound Psychic Dark 80 160 60 170 130 80 6 True
9 800 Volcanion Fire Water 80 110 120 130 90 70 6 True

يضيف مخططات البيانات في الاعمدة axis=1.

data1 = data['Attack'].head()
data2= data['Defense'].head()
conc_data_col = pd.concat([data1,data2],axis =1) # axis = 1 : adds dataframes in row
conc_data_col

النتيجة.

Attack Defense
0 49 49
1 62 63
2 82 83
3 100 123
4 52 43

 

 

 

 

 

 

 

vii- أنواع البيانات

هناك 5 أنواع البيانات الأساسية: object(string)، boolen, integer, float, categorical.

يمكننا التحويل بين أنواع البيانات مثل من str إلى categorical أو من int الي float.

ذلك لجعل إطارات البيانات أصغر في الذاكرة.

data.dtypes

النتيجة.

# int64
Name object
Type 1 object
Type 2 object
HP int64
Attack int64
Defense int64
Sp. Atk int64
Sp. Def int64
Speed int64
Generation int64
Legendary bool
dtype: object

لنقم بتحويل (object(str الي categorical و int الي float.

data['Type 1'] = data['Type 1'].astype('category')
data['Speed'] = data['Speed'].astype('float')
data.dtypes

النتيجة.

# int64
Name object
Type 1 category
Type 2 object
HP int64
Attack int64
Defense int64
Sp. Atk int64
Sp. Def int64
Speed float64
Generation int64
Legendary bool
dtype: object

طبعا لاحظت الفرق.

viii- البيانات المفقودة والاختبار مع التأكيد

إذا واجهتنا البيانات المفقودة، فما يمكننا فعله:

اتركه كما هو.

إسقاطهم مع ()dropna .

ملء القيمة المفقودة مع ()fillna .

ملء القيم المفقودة مع إحصائيات الاختبار مثل المتوسط.

بيان التأكيد: هو التحقق من أنه يمكنك تشغيل أو إيقاف عندما تنتهي من اختبارك للبرنامج.

دعنا نتفحص البيانات ان كلن هناك وحدات خالية من القيمة.

data.info()

النتيجة.

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 800 entries, 0 to 799
Data columns (total 12 columns):
# 800 non-null int64
Name 799 non-null object
Type 1 800 non-null category
Type 2 414 non-null object
HP 800 non-null int64
Attack 800 non-null int64
Defense 800 non-null int64
Sp. Atk 800 non-null int64
Sp. Def 800 non-null int64
Speed 800 non-null float64
Generation 800 non-null int64
Legendary 800 non-null bool
dtypes: bool(1), category(1), float64(1), int64(7), object(2)
memory usage: 64.9+ KB

كما ترون هناك 800 وحدة. على الرغم من أن type 2 يحتوي على 414 كائن غير خالٍ لذلك يحتوي على 386 قيمة خالية.

لنتفحص type 2.

data["Type 2"].value_counts(dropna =False)

النتيجة.

NaN 386
Flying 97
Ground 35
Poison 34
Psychic 33
Fighting 26
Grass 25
Fairy 23
Steel 22
Dark 20
Dragon 18
Rock 14
Ghost 14
Water 14
Ice 14
Fire 12
Electric 6
Normal 4
Bug 3
Name: Type 2, dtype: int64

سنجد 386 وحدة خالية هيا لنتخلص منهم.

data1=data # also we will use data to fill missing value so I assign it to data1 variable
data1["Type 2"].dropna(inplace = True) # inplace = True means we do not assign it to new variable. Changes automatically assigned to data

صفة inplace=true تسمح لنا التغير دون ان نحجز متغير جديد في الذاكرة.

ولان لنقم بكتابة جمل التحقق للتأكد من ان البرنامج يعمل جيدا.

assert data['Type 2'].notnull().all() # returns nothing because we drop nan values

سيعمل البرنامج جيدا لأننا قد اعطينا بالفعل كل القيم الفارغة قيم مسبقا.

وأخيرا مع جملة التحقق نستطيع التأكد من أشياء كثيرة في المستقبل مثل:

assert data.columns[1] == 'Name'
assert data.Speed.dtypes == np.int

و بذلك ننتهي من الجزء الثاني الخاص بتجهيز البيانات, و نسوف نتحدث في الجزء الثالث عن المكتبات البرمجية الباندا Panda.

 

المصدر: DATA SCIENTIST, Kaggle

تعليقات