دليل Numpy – دليلك الأول لبناء أساسيات برمجة بايثون باستخدام Numpy

هذا هو الجزء الأول من برنامج Numpy التعليمي، الذي يغطي جميع الجوانب الأساسية لمعالجة البيانات وتحليلها باستخدام مصفوفات Ndarrays من Numpy. Numpy هي الحزمة الأساسية والفعّالة للحوسبة العلمية ومعالجة البيانات باستخدام بايثون.

Numpy هي الحزمة الأساسية والقوية للعمل مع البيانات في بايثون.

إذا كنت ستعمل على مشاريع تحليل البيانات أو التعلم الآلي، فمن الضروري أن يكون لديك فهم قوي لـ numpy.

نظرًا لأن الحزم الأخرى لتحليل البيانات (مثل pandas) مبنية على numpy وحزمة scikit-learn التي تُستخدم لبناء تطبيقات التعلم الآلي تعمل بشكل كبير مع numpy أيضًا.

إذن ما الذي يوفره numpy؟

في الأساس، يوفر numpy كائنات ndarray الممتازة، وهي اختصار لمصفوفات ذات أبعاد n.

في كائن “ndarray”، المعروف أيضًا باسم “array”، يمكنك تخزين عناصر متعددة من نفس نوع البيانات. الإمكانيات المتوفرة في كائن المصفوفة هي ما يجعل numpy سهل الاستخدام لإجراء العمليات الحسابية ومعالجة البيانات.

قد تتساءل، “يمكنني تخزين الأرقام والكائنات الأخرى في قائمة بايثون نفسها وإجراء جميع أنواع الحسابات والتلاعبات من خلال فهم القائمة وحلقات for وما إلى ذلك. ما الذي أحتاج إليه في مصفوفة numpy؟”

حسنًا، هناك مزايا كبيرة جدًا لاستخدام مصفوفات numpy بدلاً من القوائم.

لفهم هذا، دعونا أولاً نرى كيفية إنشاء مجموعة numpy.

كيفية إنشاء مجموعة numpy؟

هناك طرق متعددة لإنشاء مصفوفة numpy، وسيتم شرح معظمها أثناء قراءتك لهذا. مع ذلك، إحدى أكثر الطرق شيوعًا هي إنشاء مصفوفة من قائمة، أو قائمة تشبه كائنًا، عن طريق تمريرها إلى الدالة np.array.

# Create an 1d array from a list
import numpy as np
list1 = [0,1,2,3,4]
arr1d = np.array(list1)

# Print the array and its type
print(type(arr1d))
arr1d

#> class 'numpy.ndarray'
#> array([0, 1, 2, 3, 4])

الفرق الرئيسي بين المصفوفة والقائمة هو أن المصفوفات مصممة للتعامل مع العمليات المتجهة بينما قائمة بايثون ليست كذلك.

وهذا يعني أنه إذا قمت بتطبيق دالة، فسيتم تنفيذها على كل عنصر في المصفوفة، وليس على كائن المصفوفة بأكمله.

لنفترض أنك تريد إضافة الرقم ٢ إلى كل عنصر في القائمة. الطريقة البديهية للقيام بذلك هي كالتالي:

list1 + 2  # error

لم يكن ذلك ممكنًا باستخدام قائمة. ولكن يمكنك القيام بذلك باستخدام مصفوفة ndarray.

# Add 2 to each element of arr1d
arr1d + 2

#> array([2, 3, 4, 5, 6])

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

ومع ذلك، هناك مزايا أخرى كثيرة. لنكتشفها.

هذا يتعلق بمصفوفة أحادية البعد. يمكنك أيضًا تمرير قائمة من القوائم لإنشاء مصفوفة مثل مصفوفة ثنائية الأبعاد.

# Create a 2d array from a list of lists
list2 = [[0,1,2], [3,4,5], [6,7,8]]
arr2d = np.array(list2)
arr2d

#> array([[0, 1, 2],
#>        [3, 4, 5],
#>        [6, 7, 8]])

يمكنك أيضًا تحديد نوع البيانات بتعيين وسيطة نوع البيانات. من أكثر أنواع البيانات استخدامًا: 'float'، 'int'، 'bool'، 'str'، و'object'.

للتحكم في تخصيصات الذاكرة، يمكنك اختيار استخدام أحد الخيارات التالية: “float32″، أو “float64″، أو “int8″، أو “int16″، أو “int32”.

# Create a float 2d array
arr2d_f = np.array(list2, dtype='float')
arr2d_f

#> array([[ 0.,  1.,  2.],
#>        [ 3.,  4.,  5.],
#>        [ 6.,  7.,  8.]])

تشير العلامة العشرية بعد كل رقم إلى نوع البيانات العائم. يمكنك أيضًا تحويله إلى نوع بيانات مختلف باستخدام التابع astype.

# Convert to 'int' datatype
arr2d_f.astype('int')

#> array([[0, 1, 2],
#>        [3, 4, 5],
#>        [6, 7, 8]])
# Convert to int then to str datatype
arr2d_f.astype('int').astype('str')

#> array([['0', '1', '2'],
#>        ['3', '4', '5'],
#>        ['6', '7', '8']],
#>       dtype='U21')

يجب أن تكون جميع عناصر مصفوفة numpy من نفس نوع البيانات، على عكس القوائم. وهذا فرق جوهري آخر.

ومع ذلك، إذا كنت غير متأكد من نوع البيانات الذي سيحتفظ به المصفوف الخاص بك أو إذا كنت تريد الاحتفاظ بالأحرف والأرقام في نفس المصفوفة، فيمكنك تعيين dtype كـ “object”.

# Create a boolean array
arr2d_b = np.array([1, 0, 10], dtype='bool')
arr2d_b

#> array([ True, False,  True], dtype=bool)
# Create an object array to hold numbers as well as strings
arr1d_obj = np.array([1, 'a'], dtype='object')
arr1d_obj

#> array([1, 'a'], dtype=object)

أخيرًا، يمكنك دائمًا تحويل المصفوفة إلى قائمة بايثون باستخدام tolist().

# Convert an array back to a list
arr1d_obj.tolist()

#> [1, 'a']

باختصار، الاختلافات الرئيسية مع قوائم بايثون هي:

  • تدعم المصفوفات العمليات المتجهة، بينما لا تدعمها القوائم.
  • بمجرد إنشاء مصفوفة، لا يمكنك تغيير حجمها. سيتعين عليك إنشاء مصفوفة جديدة أو استبدال المصفوفة الحالية.
  • لكل مصفوفة نوع بيانات واحد فقط. جميع العناصر فيها يجب أن تكون من هذا النوع.
  • تشغل مجموعة numpy المكافئة مساحة أقل بكثير من قائمة قوائم Python.

كيفية فحص حجم وشكل مجموعة numpy؟

تحتوي كل مصفوفة على بعض الخصائص التي أريد أن أفهمها حتى أتمكن من معرفة المزيد عن المصفوفة.

لننظر إلى المصفوفة arr2d. بما أنها مُنشأة من قائمة قوائم، فهي ذات بُعدين، يمكن تمثيلهما كصفوف وأعمدة، كما في المصفوفة.

لو أنشأتُ واحدًا من قائمة قوائم، لكان له ثلاثة أبعاد، كما في المكعب. وهكذا.

لنفترض أنك تلقيت متجهًا من نوع numpy لم تنشئه بنفسك. ما هي الأمور التي ترغب في استكشافها لمعرفة المزيد عن هذا المصفوف؟

حسنًا، أريد أن أعرف:

  • إذا كانت مصفوفة أحادية الأبعاد أو ثنائية الأبعاد أو أكثر. (استخدم ndim)
  • كم عدد العناصر الموجودة في كل بُعد (استخدم shape)
  • ما هو نوع البيانات الخاص به (استخدم dtype)
  • ما هو العدد الإجمالي للعناصر الموجودة فيه (size)
  • عينات من العناصر القليلة الأولى في المصفوفة (من خلال الفهرسة)
# Create a 2d array with 3 rows and 4 columns
list2 = [[1, 2, 3, 4],[3, 4, 5, 6], [5, 6, 7, 8]]
arr2 = np.array(list2, dtype='float')
arr2

#> array([[ 1.,  2.,  3.,  4.],
#>        [ 3.,  4.,  5.,  6.],
#>        [ 5.,  6.,  7.,  8.]])
# shape
print('Shape: ', arr2.shape)

# dtype
print('Datatype: ', arr2.dtype)

# size
print('Size: ', arr2.size)

# ndim
print('Num Dimensions: ', arr2.ndim)

#> Shape:  (3, 4)
#> Datatype:  float64
#> Size:  12
#> Num Dimensions:  2

كيفية استخراج عناصر محددة من المصفوفة؟

arr2

#> array([[ 1.,  2.,  3.,  4.],
#>          [ 3.,  4.,  5.,  6.],
#>          [ 5.,  6.,  7.,  8.]])

يمكنك استخراج أجزاء محددة من مصفوفة باستخدام الفهرسة التي تبدأ بالرقم 0، وهو أمر مشابه لما تفعله مع قوائم بايثون.

لكن على عكس القوائم، يمكن لمصفوفات numpy قبول عدد اختياري من المعلمات بين قوسين مربعين يساوي عدد الأبعاد.

# Extract the first 2 rows and columns
arr2[:2, :2]
list2[:2, :2]  # error

#> array([[ 1.,  2.],
#>        [ 3.,  4.]])

بالإضافة إلى ذلك، تدعم مصفوفات numpy الفهرسة المنطقية.

مصفوفة الفهرس المنطقي لها نفس شكل المصفوفة المراد تصفيتها، وتحتوي فقط على قيمتي “صحيح” و”خطأ”. تُحفظ القيم المقابلة لمواقع “صحيح” في المخرجات.

# Get the boolean output by applying the condition to each element.
b = arr2 > 4
b

#> array([[False, False, False, False],
#>        [False, False,  True,  True],
#>        [ True,  True,  True,  True]], dtype=bool)
arr2[b]

#> array([ 5.,  6.,  5.,  6.,  7.,  8.])

كيفية عكس الصفوف والمصفوفة بأكملها؟

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

# Reverse only the row positions
arr2[::-1, ]

#> array([[ 5.,  6.,  7.,  8.],
#>        [ 3.,  4.,  5.,  6.],
#>        [ 1.,  2.,  3.,  4.]])
# Reverse the row and column positions
arr2[::-1, ::-1]

#> array([[ 8.,  7.,  6.,  5.],
#>        [ 6.,  5.,  4.,  3.],
#>        [ 4.,  3.,  2.,  1.]])

كيفية تمثيل القيم المفقودة واللانهائية؟

يمكن تمثيل القيم المفقودة باستخدام الكائن np.nan، بينما يمثل np.inf قيمة لا نهائية. لنضع بعضها في arr2d.

# Insert a nan and an inf
arr2[1,1] = np.nan  # not a number
arr2[1,2] = np.inf  # infinite
arr2

#> array([[  1.,   2.,   3.,   4.],
#>        [  3.,  nan,  inf,   6.],
#>        [  5.,   6.,   7.,   8.]])
# Replace nan and inf with -1. Don't use arr2 == np.nan
missing_bool = np.isnan(arr2) | np.isinf(arr2)
arr2[missing_bool] = -1  
arr2

#> array([[ 1.,  2.,  3.,  4.],
#>        [ 3., -1., -1.,  6.],
#>        [ 5.,  6.,  7.,  8.]])

كيفية حساب المتوسط ​​​​والحد الأدنى والحد الأقصى على ndarray؟

يحتوي ndarray على التوابع المناسبة لحساب ذلك للمجموعة بأكملها.

# mean, max and min
print("Mean value is: ", arr2.mean())
print("Max value is: ", arr2.max())
print("Min value is: ", arr2.min())

#> Mean value is:  3.58333333333
#> Max value is:  8.0
#> Min value is:  -1.0

ومع ذلك، إذا كنت تريد حساب الحد الأدنى للقيم صفًا أو عمودًا، فاستخدم إصدار np.amin بدلاً من ذلك.

# Row wise and column wise min
print("Column wise minimum: ", np.amin(arr2, axis=0))
print("Row wise minimum: ", np.amin(arr2, axis=1))

#> Column wise minimum:  [ 1. -1. -1.  4.]
#> Row wise minimum:  [ 1. -1.  5.]

حساب الحد الأدنى صفًا واحدًا أمر جيد. ولكن ماذا لو أردتَ إجراء حسابات/وظائف أخرى صفًا واحدًا؟ يمكنكَ القيام بذلك باستخدام الدالة np.apply_over_axis التي ستراها في الموضوع التالي.

# Cumulative Sum
np.cumsum(arr2)

#> array([  1.,   3.,   6.,  10.,  13.,  12.,  11.,  17.,  22.,  28.,  35., 43.])

كيفية إنشاء مصفوفة جديدة من مصفوفة موجودة؟

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

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

لتجنب إتلاف المصفوفة الأصلية، عليك نسخها باستخدام دالة copy(). جميع مصفوفات numpy مزودة بهذه الدالة.

# Assign portion of arr2 to arr2a. Doesn't really create a new array.
arr2a = arr2[:2,:2]  
arr2a[:1, :1] = 100  # 100 will reflect in arr2
arr2

#> array([[ 100.,    2.,    3.,    4.],
#>        [   3.,   -1.,   -1.,    6.],
#>        [   5.,    6.,    7.,    8.]])
# Copy portion of arr2 to arr2b
arr2b = arr2[:2, :2].copy()
arr2b[:1, :1] = 101  # 101 will not reflect in arr2
arr2

#> array([[ 100.,    2.,    3.,    4.],
#>        [   3.,   -1.,   -1.,    6.],
#>        [   5.,    6.,    7.,    8.]])

كيفية إنشاء تسلسلات وتكرارات وأرقام عشوائية باستخدام numpy؟

تعتبر دالة np.arange مفيدة لإنشاء تسلسلات رقمية مخصصة مثل ndarray.

# Lower limit is 0 be default
print(np.arange(5))  

# 0 to 9
print(np.arange(0, 10))  

# 0 to 9 with step of 2
print(np.arange(0, 10, 2))  

# 10 to 1, decreasing order
print(np.arange(10, 0, -1))

#> [0 1 2 3 4]
#> [0 1 2 3 4 5 6 7 8 9]
#> [0 2 4 6 8]
#> [10  9  8  7  6  5  4  3  2  1]

يمكنك تحديد موضعي البداية والنهاية باستخدام np.arange. ولكن إذا كنت تُركز على عدد العناصر في المصفوفة، فسيتعين عليك حساب قيمة الخطوة المناسبة يدويًا.

لنفترض أنك تريد إنشاء مجموعة مكونة من 10 أرقام بالضبط بين 1 و50، هل يمكنك حساب قيمة الخطوة؟

حسنًا، سأستخدم np.linspace بدلاً من ذلك.

# Start at 1 and end at 50
np.linspace(start=1, stop=50, num=10, dtype=int)

#> array([ 1,  6, 11, 17, 22, 28, 33, 39, 44, 50])

لاحظ أنه منذ أن أجبرت صراحةً أن يكون نوع البيانات int، فإن الأرقام ليست متباعدة بشكل متساوٍ بسبب التقريب.

على غرار np.linspace، يوجد أيضًا np.logspace الذي يرتفع بمقياس لوغاريتمي. في np.logspace، قيمة البداية المُعطاة هي base^start وتنتهي بـ base^stop، بقيمة افتراضية 10.

# Limit the number of digits after the decimal to 2
np.set_printoptions(precision=2)  

# Start at 10^1 and end at 10^50
np.logspace(start=1, stop=50, num=10, base=10) 

#> array([  1.00e+01,   2.78e+06,   7.74e+11,   2.15e+17,   5.99e+22,
#>          1.67e+28,   4.64e+33,   1.29e+39,   3.59e+44,   1.00e+50])

تتيح لك الدالتان np.zeros وnp.ones إنشاء مصفوفات بالشكل المطلوب حيث تكون جميع العناصر إما 0 أو 1.

np.zeros([2,2])
#> array([[ 0.,  0.],
#>        [ 0.,  0.]])
np.ones([2,2])
#> array([[ 1.,  1.],
#>        [ 1.,  1.]])

كيفية توليد أرقام عشوائية؟

توفر وحدة random دوال رائعة لإنشاء أرقام عشوائية (وتوزيعات إحصائية أيضًا) لأي شكل معين.

# Random numbers between [0,1) of shape 2,2
print(np.random.rand(2,2))

# Normal distribution with mean=0 and variance=1 of shape 2,2
print(np.random.randn(2,2))

# Random integers between [0, 10) of shape 2,2
print(np.random.randint(0, 10, size=[2,2]))

# One random number between [0,1)
print(np.random.random())

# Random numbers between [0,1) of shape 2,2
print(np.random.random(size=[2,2]))

# Pick 10 items from a given list, with equal probability
print(np.random.choice(['a', 'e', 'i', 'o', 'u'], size=10))  

# Pick 10 items from a given list with a predefined probability 'p'
print(np.random.choice(['a', 'e', 'i', 'o', 'u'], size=10, p=[0.3, .1, 0.1, 0.4, 0.1]))  # picks more o's

#> [[ 0.84  0.7 ]
#>  [ 0.52  0.8 ]]

#> [[-0.06 -1.55]
#>  [ 0.47 -0.04]]

#> [[4 0]
#>  [8 7]]

#> 0.08737272424956832

#> [[ 0.45  0.78]
#>  [ 0.03  0.74]]

#> ['i' 'a' 'e' 'e' 'a' 'u' 'o' 'e' 'i' 'u']
#> ['o' 'a' 'e' 'a' 'a' 'o' 'o' 'o' 'a' 'o']

الآن، في كل مرة تقوم بتشغيل أي من الوظائف المذكورة أعلاه، ستحصل على مجموعة مختلفة من الأرقام العشوائية.

إذا أردت تكرار نفس مجموعة الأرقام العشوائية في كل مرة، فعليك ضبط البذرة أو الحالة العشوائية. يمكن أن تكون قيمة “see” أي قيمة. الشرط الوحيد هو ضبط البذرة على نفس القيمة في كل مرة تريد فيها توليد نفس مجموعة الأرقام العشوائية.

بمجرد إنشاء np.random.RandomState، تصبح جميع دوال وحدة np.random متاحة لكائن randomstate الذي تم إنشاؤه.

# Create the random state
rn = np.random.RandomState(100)

# Create random numbers between [0,1) of shape 2,2
print(rn.rand(2,2))

#> [[ 0.54  0.28]
#>  [ 0.42  0.84]]
# Set the random seed
np.random.seed(100)

# Create random numbers between [0,1) of shape 2,2
print(np.random.rand(2,2))

#> [[ 0.54  0.28]
#>  [ 0.42  0.84]]


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

Scroll to Top

اكتشاف المزيد من بايثون العربي

اشترك الآن للاستمرار في القراءة والحصول على حق الوصول إلى الأرشيف الكامل.

Continue reading