أساسيات قواعد بناء جمل Python شائعة الاستخدام في علم البيانات
خلال اليومين الماضيين، كنت أقرأ هذا الكتاب Data Science from Scrach (رابط PDF)، وهو كتاب ممتاز وسهل الفهم للمبتدئين في علم البيانات. خصص أحد فصوله لتقديم قواعد بناء جمل Python الأساسية وقواعد بناء الجمل المتقدمة الشائعة الاستخدام في علم البيانات. وجدت الشرح رائعًا وموجزًا وواضحًا، لذا قمت بترجمته ووضعه هنا ليكون مرجعًا لي. أساسيات قواعد بناء جمل Python شائعة الاستخدام في علم البيانات قواعد بناء جمل Python متقدمة شائعة الاستخدام في علم البيانات
تركز هذه المقالة على تقديم قواعد بناء جمل Python الأساسية والميزات المفيدة جدًا في معالجة البيانات (بناءً على Python 2.7).
تنسيق المسافات البيضاء
تستخدم العديد من اللغات الأقواس للتحكم في كتل الكود، لكن Python تعتمد على المسافات البادئة (indentation):
for i in [1, 2, 3, 4, 5]:
print i # السطر الأول من حلقة "for i"
for j in [1, 2, 3, 4, 5]:
print j # السطر الأول من حلقة "for j"
print i + j # السطر الأخير من حلقة "for j"
print i # السطر الأخير من حلقة "for i"
print "done looping"
هذا يجعل كود Python سهل القراءة للغاية، ولكنه يعني أيضًا أنه يتعين عليك الانتباه دائمًا إلى التنسيق. يتم تجاهل المسافات البيضاء داخل الأقواس، وهذا مفيد عند كتابة تعبيرات طويلة:
long_winded_computation = (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20)
ويجعل الكود أسهل في القراءة:
list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
easier_to_read_list_of_lists = [ [1, 2, 3],
[4 ,5 ,6 ],
[7 ,8 ,9 ] ]
العبارات متعددة الأسطر
يمكن استخدام شرطة مائلة عكسية (\) للإشارة إلى أن سطرين متصلان (نادراً ما تُستخدم هذه الممارسة):
two_plus_three = 2 + \
3
الوحدات النمطية Modules
سواء كانت وحدات Python المدمجة أو الوحدات الخارجية التي قمت بتنزيلها، فإنها تتطلب الاستيراد يدويًا قبل الاستخدام.
-
استيراد الوحدة بأكملها مباشرةً وببساطة:
import re my_regex = re.compile("[0-9]+", re.I)هنا، الوحدة
reالمستوردة تُستخدم للتعبيرات النمطية (regular expressions). بعد استيراد الوحدة، يمكن استدعاء وظائفها مباشرةً باستخدام اسم الوحدة كبادئة (re.). -
إذا كان اسم الوحدة التي يتم استيرادها مستخدمًا بالفعل في الكود، يمكنك إعادة تسمية الوحدة عند استيرادها:
import re as regex my_regex = regex.compile("[0-9]+", regex.I) -
إذا أردت أن تتصرف بتهور، يمكنك استيراد الوحدة بأكملها إلى مساحة الأسماء الحالية، مما قد يؤدي عن غير قصد إلى الكتابة فوق المتغيرات التي قمت بتعريفها مسبقاً:
match = 10 from re import * # تحتوي الوحدة re على دالة اسمها match print match # يطبع الدالة matchبما أنك شخص جيد، فأنا على ثقة بأنك لن تفعل ذلك.
العمليات الحسابية Arithmetic
تستخدم Python 2.7 القسمة الصحيحة افتراضيًا، لذا فإن $ 5 / 2 = 2 $. ولكن في كثير من الأحيان، لا نرغب في القسمة الصحيحة، لذلك يمكننا استيراد هذه الوحدة:
from __future__ import division
بعد الاستيراد، يصبح $ 5 / 2 = 2.5 $. للقسمة الصحيحة: $ 5 // 2 = 2 $.
الدوال Functions
تعريف الدوال
الدالة هي قاعدة تستقبل صفرًا أو أكثر من المدخلات، وتُرجع ناتجًا معينًا. في Python، نعرّف الدالة باستخدام def اسم_الدالة(معاملات):
def double(x):
"""يمكنك كتابة شرح لوظيفة الدالة هنا.
على سبيل المثال، هذه الدالة تضرب المدخل في 2."""
# يمكن كتابة جسم الدالة هنا، مع الانتباه للمسافة البادئة.
return x * 2
استخدام الدوال
في Python، الدوال هي كيانات من الدرجة الأولى (first-class citizens)، مما يعني أنه يمكننا إسناد دالة إلى متغير، أو تمريرها كمعامل لدوال أخرى:
def apply_to_one(f):
"""تستدعي الدالة f وتمرر 1 كمعامل لها."""
return f(1)
my_double = double # يشير double إلى الدالة المعرفة في القسم السابق
x = apply_to_one(my_double) # x يساوي 2
الدوال المجهولة
يمكن أيضًا إنشاء دوال مجهولة (anonymous functions) باستخدام lambda:
y = apply_to_one(lambda x: x + 4) # يساوي 5
يمكنك إسناد lambda إلى متغيرات أخرى، لكن معظم المبرمجين ينصحون بالالتزام باستخدام def قدر الإمكان:
another_double = lambda x: 2 * x # غير مستحسن
def another_double(x): return 2 * x # الممارسة الموصى بها
ملاحظات إضافية:
lambdaهي مجرد تعبير، وهيكل الدالة فيها أبسط بكثير منdef.- جسم
lambdaهو تعبير وليس كتلة كود. يمكن فقط تضمين منطق محدود داخل تعبيرlambda.
تمرير معاملات الدوال
يمكن تعريف قيم افتراضية لمعاملات الدوال. إذا لم يتم تمرير معاملات للدالة، فستستخدم القيم الافتراضية، أما إذا تم تمرير معاملات، فستُستخدم هذه القيم المحددة:
def my_print(message="my default message"):
print message
my_print("hello") # يطبع "hello"
my_print() # يطبع "my default message"
من المفيد أحيانًا تحديد المعاملات مباشرةً باستخدام أسمائها:
def subtract(a=0, b=0):
return a - b
subtract(10, 5) # يعيد 5
subtract(0, 5) # يعيد -5
subtract(b=5) # نفس السابق، يعيد -5
السلاسل النصية Strings
يمكنك إنشاء سلاسل نصية باستخدام علامات اقتباس مفردة أو مزدوجة (يجب أن تكون علامات الاقتباس متطابقة):
single_quoted_string = 'data science'
double_quoted_string = "data science"
تُستخدم الشرطة المائلة العكسية (\) لتمثيل أحرف الهروب (escape characters)، مثل:
tab_string = "\t" # يمثل حرف الجدولة tab
len(tab_string) # يساوي 1
عندما ترغب في استخدام الشرطة المائلة العكسية بحد ذاتها (مثلاً في مسارات مجلدات Windows أو التعبيرات النمطية)، يمكنك تعريفها باستخدام السلاسل النصية الخام r"":
not_tab_string = r"\t" # يمثل الحرفين '\' و 't'
len(not_tab_string) # يساوي 2
يمكن إنشاء سلاسل نصية متعددة الأسطر باستخدام ثلاث علامات اقتباس مزدوجة:
multi_line_string = """هذا هو السطر الأول
هذا هو السطر الثاني
هذا هو السطر الثالث"""
معالجة الاستثناءات Exception
عند حدوث خطأ في البرنامج، تطلق Python استثناءً (exception). إذا لم نتعامل معه، فسيتوقف البرنامج عن التنفيذ. يمكن التقاط الاستثناءات باستخدام عبارتي try و except:
try:
print 0 / 0
except ZeroDivisionError:
print "لا يمكن القسمة على صفر"
على الرغم من أن الاستثناءات تعتبر ظاهرة غير مرغوبة في لغات البرمجة الأخرى، إلا أن التعامل معها بكثرة في Python يجعل الكود أكثر أناقة ونظافة.
القوائم Lists
إنشاء القوائم
القوائم هي مجموعات بسيطة ومرتبة، وهي أحد هياكل البيانات الأساسية في Python (تشبه المصفوفات في لغات أخرى، لكن القوائم تتميز ببعض الخصائص الإضافية). لإنشاء قائمة:
integer_list = [1, 2, 3]
heterogeneous_list = ["string", 0.1, True]
list_of_lists = [ integer_list, heterogeneous_list, [] ]
list_length = len(integer_list) # يساوي 3
list_sum = sum(integer_list) # يساوي 6
الوصول إلى قيم القائمة
يمكنك الوصول إلى قيم القائمة باستخدام الأقواس المربعة والمؤشرات:
x = range(10) # يحصل على القائمة x = [0, 1, ..., 9]
zero = x[0] # يساوي 0، تبدأ مؤشرات القوائم من 0
one = x[1] # يساوي 1
nine = x[-1] # يساوي 9، العنصر الأخير في القائمة
eight = x[-2] # يساوي 8، العنصر قبل الأخير في القائمة
x[0] = -1 # القائمة الحالية x = [-1, 1, 2, 3, ..., 9]
تقطيع القوائم
يمكن تقطيع القوائم باستخدام الأقواس المربعة:
first_three = x[:3] # [-1, 1, 2]
three_to_end = x[3:] # [3, 4, ..., 9]
one_to_four = x[1:5] # [1, 2, 3, 4]
last_three = x[-3:] # [7, 8, 9]
without_first_and_last = x[1:-1] # [1, 2, ..., 8]
copy_of_x = x[:] # [-1, 1, 2, ..., 9]
يمكن استخدام in للتحقق مما إذا كان عنصر معين موجودًا في القائمة:
1 in [1, 2, 3] # صحيح
0 in [1, 2, 3] # خطأ
طريقة البحث عن العناصر هذه غير فعالة للغاية. استخدمها فقط إذا كانت القائمة صغيرة أو إذا لم تكن تهتم بوقت البحث.
ضم القوائم
من السهل جدًا ضم قائمتين في Python:
x = [1, 2, 3]
x.extend([4, 5, 6]) # الآن x = [1,2,3,4,5,6]
إذا كنت لا ترغب في تعديل القائمة الأصلية x، يمكنك استخدام عامل التشغيل + لإنشاء قائمة جديدة:
x = [1, 2, 3]
y = x + [4, 5, 6] # الآن y = [1, 2, 3, 4, 5, 6]؛ x لم تتغير
يتم استخدام هذه الطريقة غالبًا لإضافة عنصر واحد إلى القائمة في كل مرة:
x = [1, 2, 3]
x.append(0) # الآن x = [1, 2, 3, 0]
y = x[-1] # يساوي 0
z = len(x) # يساوي 4
تفكيك القوائم
إذا كنت تعرف عدد العناصر في القائمة، فمن السهل تفكيكها:
x, y = [1, 2] # الآن x = 1, y = 2
إذا كان عدد العناصر على جانبي المعادلة غير متطابق، فستحصل على ValueError. لذلك، غالبًا ما نستخدم الشرطة السفلية (_) لتخزين الجزء المتبقي من القائمة:
_, y = [1, 2] # الآن y == 2، بغض النظر عن العنصر الأول
الـ Tuples
الـ lists والـ tuples متشابهتان جدًا، والفرق الوحيد هو أن العناصر في الـ tuple لا يمكن تعديلها.
إنشاء الـ Tuples
يمكن إنشاء الـ tuples باستخدام الأقواس الدائرية أو بدون أي أقواس:
my_tuple = (1, 2)
other_tuple = 3, 4
my_list[1] = 3 # الآن my_list هي [1, 3]
try:
my_tuple[1] = 3
except TypeError:
print "لا يمكن تعديل العناصر في tuple"
تُسهل الـ tuples الحصول على قيم متعددة من الدالة:
def sum_and_product(x, y):
return (x + y),(x * y)
sp = sum_and_product(2, 3) # يساوي (5, 6)
s, p = sum_and_product(5, 10) # s = 15, p = 50
تدعم الـ tuples (والـ lists) إسناد قيم متعددة في وقت واحد:
x, y = 1, 2 # الآن x = 1, y = 2
x, y = y, x # تبديل قيم متغيرين في Python؛ الآن x = 2, y = 1
القواميس Dictionaries
إنشاء القواميس
أحد هياكل البيانات الأساسية الأخرى في Python هو القاموس (dictionary)، الذي يتيح لك الحصول بسرعة على القيمة المقابلة باستخدام مفتاح (key):
empty_dict = {} # تعريف قاموس فارغ بطريقة Pythonية بحتة
empty_dict2 = dict() # تعريف قاموس فارغ بطريقة أقل Pythonية
grades = { "Joel" : 80, "Tim" : 95 } # تخزين قاموسي
البحث عن عناصر القاموس
يمكنك البحث عن القيمة المقابلة باستخدام الأقواس المربعة والمفتاح:
joels_grade = grades["Joel"] # يساوي 80
إذا لم يكن المفتاح الذي تبحث عنه موجودًا في القاموس، فستُرجع الدالة KeyError:
try:
kates_grade = grades["Kate"]
except KeyError:
print "لا يوجد تقدير لـ Kate!"
يمكن التحقق مما إذا كان المفتاح موجودًا في القاموس باستخدام in:
joel_has_grade = "Joel" in grades # صحيح
kate_has_grade = "Kate" in grades # خطأ
يحتوي القاموس على دالة يمكنها إعادة قيمة افتراضية؛ فإذا لم يكن المفتاح الذي تبحث عنه موجودًا في القاموس، فستُرجع القيمة الافتراضية المحددة (بدلاً من إطلاق استثناء):
joels_grade = grades.get("Joel", 0) # يساوي 80
kates_grade = grades.get("Kate", 0) # يساوي 0
no_ones_grade = grades.get("No One") # يعيد القيمة الافتراضية None
تعديل القواميس
يمكن استخدام الأقواس المربعة لإنشاء وتعديل أزواج المفتاح-القيمة في القاموس:
grades["Tim"] = 99 # يستبدل القيمة القديمة
grades["Kate"] = 100 # يضيف زوجًا من المفتاح-القيمة
num_students = len(grades) # يساوي 3
غالبًا ما سنستخدم القواميس بهذه الطريقة لتمثيل هياكل البيانات:
tweet = {
"user" : "joelgrus",
"text" : "Data Science is Awesome",
"retweet_count" : 100,
"hashtags" : ["#data", "#science", "#datascience", "#awesome", "#yolo"]
}
بالإضافة إلى البحث عن مفاتيح محددة، يمكننا أيضًا التعامل مع جميع المفاتيح بهذه الطريقة:
tweet_keys = tweet.keys() # يحصل على قائمة بالمفاتيح
tweet_values = tweet.values() # يحصل على قائمة بالقيم
tweet_items = tweet.items() # يحصل على tuple من (مفتاح, قيمة)
"user" in tweet_keys # يعيد True، ويستخدم بحث 'in' الأقل كفاءة في القوائم
"user" in tweet # طريقة أكثر Pythonية، تستخدم بحث 'in' الفعال في القواميس
"joelgrus" in tweet_values # صحيح
المفاتيح في القواميس فريدة، ولا يمكن استخدام القوائم كمفاتيح في القواميس. إذا كنت بحاجة إلى مفتاح متعدد الأجزاء، يمكنك استخدام tuple، أو تحويل المفتاح إلى سلسلة نصية بطريقة ما.
القواميس الافتراضية (Default Dictionaries)
إذا كنت تحاول إحصاء تكرار كل كلمة في مستند، فإن إحدى الطرق الواضحة هي إنشاء قاموس تكون الكلمة فيه مفتاحًا، ويكون التكرار هو القيمة المقابلة. ثم يتم المرور على المستند، وعند مصادفة كلمة موجودة بالفعل، يُزاد عدد تكرارها بمقدار 1، وإذا كانت الكلمة غير موجودة، يُضاف زوج مفتاح-قيمة جديد إلى القاموس:
word_counts = {}
for word in document:
if word in word_counts:
word_counts[word] += 1
else:
word_counts[word] = 1
بالطبع، يمكنك أيضًا معالجة المفتاح المفقود مسبقًا بهذه الطريقة التي تعتمد على محاولة الوصول ثم معالجة الاستثناء:
word_counts = {}
for word in document:
try:
word_counts[word] += 1
except KeyError:
word_counts[word] = 1
الطريقة الثالثة هي استخدام get، وهذه الدالة تؤدي أداءً ممتازًا في التعامل مع المفاتيح المفقودة:
word_counts = {}
for word in document:
previous_count = word_counts.get(word, 0)
word_counts[word] = previous_count + 1
القواميس الافتراضية (Default Dictionaries) هي مثل القواميس العادية، والفرق الوحيد هو أنه عندما تحاول البحث عن مفتاح غير موجود في القاموس الافتراضي، سيقوم القاموس تلقائيًا بإنشاء زوج مفتاح-قيمة باستخدام المفتاح الذي قدمته. لاستخدام القواميس الافتراضية، تحتاج إلى استيراد مكتبة collections:
from collections import defaultdict
word_counts = defaultdict(int) # int() تنشئ 0
for word in document:
word_counts[word] += 1
تُعد القواميس الافتراضية مفيدة أيضًا مع القوائم، والقواميس العادية، وحتى الدوال المخصصة:
dd_list = defaultdict(list) # list() تنشئ قائمة فارغة
dd_list[2].append(1) # الآن dd_list هي {2: [1]}
dd_dict = defaultdict(dict) # dict() تنشئ قاموسًا فارغًا
dd_dict["Joel"]["City"] = "Seattle" # الآن محتوى dd_dict هو { "Joel" : { "City" : Seattle"}}
dd_pair = defaultdict(lambda: [0, 0]) # ينشئ قاموسًا تكون فيه قيم المفاتيح عبارة عن قوائم
dd_pair[2][1] = 1 # الآن محتوى dd_pair هو {2: [0,1]}
تُعد هذه الطريقة مفيدة جدًا، حيث لن نحتاج في المستقبل إلى التحقق مما إذا كان المفتاح موجودًا عند محاولة الحصول على قيم معينة من القاموس.
العدادات Counter
يمكن للـ Counter تحويل مجموعة من القيم مباشرةً إلى كائن شبيه بالقاموس، حيث يكون المفتاح هو أحد عناصر المجموعة، والقيمة المقابلة هي عدد مرات ظهور هذا العنصر. يُستخدم هذا غالبًا عند إنشاء المخططات التكرارية (histograms):
from collections import Counter
c = Counter([0, 1, 2, 0]) # c (تقريبًا) هي { 0 : 2, 1 : 1, 2 : 1 }
وبهذا، يصبح لدينا طريقة سهلة جدًا لإحصاء تكرار الكلمات:
word_counts = Counter(document)
تحتوي الـ Counter أيضًا على دالة most_common مفيدة جدًا، يمكنها الحصول مباشرةً على الكلمات الأكثر تكرارًا وعدد مرات ظهورها:
# يطبع أكثر 10 كلمات تكرارًا مع عدد مرات ظهورها
for word, count in word_counts.most_common(10):
print word, count
المجموعات Sets
الـ Set (المجموعة) هي هيكل بيانات آخر في Python يمثل مجموعة من العناصر الفريدة. يمكن إنشاء مجموعة وإضافة عناصر إليها بهذه الطريقة:
s = set()
s.add(1) # s هي { 1 }
s.add(2) # s هي { 1, 2 }
s.add(2) # s هي { 1, 2 }
x = len(s) # يساوي 2
y = 2 in s # يساوي True
z = 3 in s # يساوي False
يوجد سببان رئيسيان لاستخدام المجموعات:
أولاً، عملية in في المجموعات فعالة للغاية. عندما يكون عدد العناصر في مجموعة البيانات كبيرًا جدًا، فإن البحث عن العناصر في set يكون أكثر ملاءمة وفعالية بشكل واضح من البحث في قائمة:
stopwords_list = ["a","an","at"] + hundreds_of_other_words + ["yet", "you"]
"zip" in stopwords_list # فشل، يتطلب فحص كل عنصر
stopwords_set = set(stopwords_list)
"zip" in stopwords_set # نجح البحث، وبسرعة كبيرة
ثانيًا، من الملائم جدًا استخدام الـ sets للحصول على العناصر الفريدة من مجموعة بيانات:
item_list = [1, 2, 3, 1, 2, 3]
num_items = len(item_list) # 6
item_set = set(item_list) # {1, 2, 3}
num_distinct_items = len(item_set) # 3
distinct_item_list = list(item_set) # [1, 2, 3]
ولكن في الواقع، لا يزال استخدام الـ sets أقل شيوعًا من استخدام القواميس والقوائم.
العبارات الشرطية
في معظم لغات البرمجة، يمكنك استخدام if بهذه الطريقة لتمثيل التفرعات الشرطية:
if 1 > 2:
message = "لو كان 1 أكبر من 2 فقط…"
elif 1 > 3:
message = "elif تعني 'else if'"
else:
message = "عندما تفشل جميع الخيارات، استخدم else (إذا أردت)"
يمكنك أيضًا كتابة عبارات التفرع الشرطي في سطر واحد بهذه الطريقة، لكن هذا نادر الاستخدام:
parity = "even" if x % 2 == 0 else "odd"
عبارات التكرار (الحلقات)
حلقات while
حلقة while في Python:
x = 0
while x < 10:
print x, "أقل من 10"
x += 1
حلقات for
الأكثر شيوعًا هو استخدام حلقة for-in:
for x in range(10):
print x, "أقل من 10"
للتعبيرات المنطقية الأكثر تعقيدًا، يمكن استخدام عبارتي continue و break:
for x in range(10):
if x == 3:
continue # ينتقل مباشرة إلى الدورة التالية من الحلقة
if x == 5:
break # ينهي الحلقة بالكامل
print x
ستكون النتيجة طباعة 0، 1، 2، و 4.
القيم المنطقية Truthiness
تُستخدم المتغيرات المنطقية (Booleans) في Python بشكل مشابه للغات الأخرى، والفرق الوحيد هو أن الحرف الأول يجب أن يكون كبيرًا (Capitalized):
one_is_less_than_two = 1 < 2 # صحيح
true_equals_false = True == False # خطأ
تستخدم Python القيمة None للدلالة على عدم وجود قيمة، على غرار null في لغات البرمجة الأخرى:
x = None
print x == None # يطبع True، لكنها ليست الطريقة الأفضل
print x is None # يطبع True، وهي الطريقة الأفضل
تسمح Python باستخدام قيم أخرى بدلاً من القيم المنطقية. الآتي كله يعادل False:
- False
- None
- [] (قائمة فارغة)
- {} (قاموس فارغ)
- “” (سلسلة نصية فارغة)
- set() (مجموعة فارغة)
- 0 (عدد صحيح)
- 0.0 (عدد عشري)
وبالمثل، هناك العديد من القيم التي تعادل True. هذا يسهل عليك كثيرًا التحقق من القوائم الفارغة، والسلاسل النصية الفارغة، والقواميس الفارغة، وما إلى ذلك.
بالطبع، إذا لم تتمكن من توقع النتيجة، فقد تحدث أخطاء أثناء الاستخدام:
s = some_function_that_returns_a_string()
if s:
first_char = s[0]
else:
first_char = ""
طريقة أبسط تحقق نفس النتيجة:
first_char = s and s[0]
إذا كانت القيمة الأولى صحيحة، فسيتم إرجاع القيمة الثانية، وإلا فسيتم إرجاع القيمة الأولى.
وبالمثل، إذا كان x قد يكون رقمًا أو فارغًا، فهذه الطريقة تضمن الحصول على x كقيمة رقمية مؤكدة:
safe_x = x or 0
توجد في Python دالة all التي تُرجع True إذا كانت جميع العناصر صحيحة. ودالة any التي تُرجع True إذا كان هناك عنصر واحد على الأقل صحيح. على سبيل المثال، بالنسبة لقائمة يكون فيها كل عنصر “صحيحًا” (True)، ستُرجع دالة all القيمة True، وإلا فستُرجع False:
all([True, 1, { 3 }]) # صحيح
all([True, 1, {}]) # خطأ، {} يعادل "False"
any([True, 1, {}]) # صحيح
all([]) # صحيح، لا يوجد عنصر يعادل "False"
any([]) # خطأ، لا يوجد عنصر يعادل "True"
قراءة متقدمة: قواعد بناء جمل Python متقدمة شائعة الاستخدام في علم البيانات