هوش مصنوعی

کار با کتابخانه NumPy

مقدمه

در این قسمت به کار با کتابخانه NumPy خواهیم پرداخت.
(Numerical Python) NumPy یکی از کتابخانه های بسیار مهم و پرکاربرد زبان برنامه نویسی پایتون است که عملا بخش عملیات محاسباتی و ریاضی پایتون را دربر خواهد گرفت.

آرایه در NumPy

آرایه‌ها در NumPy تا حدودی شبیه لیست در پایتون هستند اما در اصل با آن تفاوت دارند. اجازه بدهید به طور کامل این موضوع را تشریح کنیم. همان‌طور که از نام این کتابخانه مشخص است، NumPy از Numeric Python برگرفته شده و امکان کار با اعداد را به طور گسترده فراهم می‌کند و آرایه‌ی NumPy ساختار داده‌ی این کتابخانه است. یعنی برای اینکه بتوانیم با داده‌ها در این کتابخانه کار کنیم باید آن‌ها را به شکل آرایه تبدیل کرد. در ادامه‌ی مقاله‌ی آموزش کامل کتابخانه NumPY ، با ایجاد یک آرایه‌ی numpy و عملیات ریاضی بر روی آرایه‌ها آشنا می‌شوید.

ایجاد یک آرایه‌ی NumPy

ساده‌ترین روش برای ساخت این آرایه استفاده از لیست پایتون است:

myPythonList = [1,9,8,3]

برای تبدیل لیست پایتون به آرایه‌ی NumPy از دستور np.array استفاده می‌کنیم. (برای چاپ خروجی در ژوپیتر نام متغیر numpy_array_from_list را تایپ می‌کنیم.)

import numpy as np
numpy_array_from_list = np.array(myPythonList)
numpy_array_from_list
array([1, 9, 8, 3])

در عمل نیازی به تعریف لیست پایتونی به طور مجزا نیست و می‌توان به شکل زیر آرایه را تعریف کرد:

a  = np.array([1,9,8,3])

توجه: در مستندات NumPy از np.darray هم برای ایجاد آرایه استفاده شده، اما روش ذکر شده مرسوم‌تر است. همچنین به جای لیست می‌توان از تاپل (tuple) نیز استفاده کرد.

عملیات ریاضی بر روی آرایه‌ها

عملیات ریاضی همچون جمع، تفریق، ضرب و تقسیم بر روی آرایه‌ها قابل انجام است. دستور کار به صورت استفاده از نام آرایه و عملگر موردنظر (*,?,+,-) و عملوند است:

numpy_array_from_list + 10
array([11, 19, 18, 13])

توجه کنید که عدد ۱۰ با تمامی عناصر آرایه جمع زده شده است. در این خصوص مفهوم Broadcasting را در ادامه توضیح می‌دهیم.

Broadcasting یا انتشار همگانی در Numpy

 

واژه‌ی Broadcasting به چگونگی رفتار Numpy با آرایه‌هایی با Shape متفاوت در خلال عملگرهای محاسباتی اشاره دارد. به طور خلاصه، آرایه‌ی کوچکتر به اندازه‌ی آرایه‌ی بزرگتر پخش می‌شود تا به شکل و Shape یکسانی با آن تبدیل شود. اینکه از Broadcast استفاده بکنیم یا خیر؛ به شرایط داده و الگوریتم بستگی دارد و می‌تواند با توجه به آن‌ها به‌کارگیری این مفهوم در کارایی برنامه تأثیر مثبت یا منفی داشته باشد. Broadcasting در Numpy با توجه به محدودیت‌های مسأله و داده، کارایی را کنترل می‌کند.

قانون Broadcasting: باید اندازه‌ی محور نهایی یا آخر (یا همان مقدار آخرین Shape)، در هر دو آرایه‌ داده‌ یکسان باشد؛ یا مقدار بُعد آخرِ حداقل یکی از آرایه‌ها برابر یک باشد.

حالت‌های زیر را در نظر بگیرید:
A(2-D array): 4 x 3 # these numbers are the shape of array
B(1-D array): 3
Result : 4 x 3
A(4-D array): 7 x 1 x 6 x 1
B(3-D array): 3 x 1 x 5
Result : 7 x 3 x 6 x 5

اما این یکی قابلیت Broadcasting ندارد. چون نه مقدار بُعد آخرشان یکی است(یکی ۳ و دیگری ۰) و نه بُعد نهایی هیچ یک برابر عدد ۱ است:
A: 4 x 3
B: 4
ساده‌ترین حالت Broadcast حین انجام عملیات محاسباتی میان یک آرایه و یک عدد اسکالر است.

import numpy as np 
  
a = np.array([1.0, 2.0, 3.0]) 
  
# Example 1 
b = 2.0
print(a * b) 
  
# Example 2 
c = [2.0, 2.0, 2.0] 
print(a * c)
[ ۲٫ ۴٫ ۶٫] [ ۲٫ ۴٫ ۶٫]

می‌توان این‌گونه تصور کرد که عدد اسکالر به تعداد اندازه‌ی آرایه‌ای که قرار است با آن در محاسبات شرکت کند، کپی می‌شود. البته این یک توصیف مفهومی از کار است ( تصویر ۴) و Numpy با توجه به هوشمندی که در طراحی‌اش به کار رفته، برای حفظ کارایی عملکرد و حافظه، چنین کپی‌ای از آن عدد اسکالر ایجاد نمی‌کند.

کتابخانه numpy

حال مثالی را بررسی می‌کنیم که هر دو آرایه باید گسترش یابند:

import numpy as np 
  
a = np.array([0.0, 10.0, 20.0, 30.0]) 
b = np.array([0.0, 1.0, 2.0]) 
  
print(a[:, np.newaxis] + b)
[[ ۰٫ ۱٫ ۲٫] [ ۱۰٫ ۱۱٫ ۱۲٫] [ ۲۰٫ ۲۱٫ ۲۲٫] [ ۳۰٫ ۳۱٫ ۳۲٫]]

آرایه در NumPy

شکل آرایه (Shape of Array)

در این بخش از مقاله‌ی آموزش کامل کتابخانه NumPY ، شکل آرایه را توضیح خواهیم داد، که می‌تواند یک بعدی، دوبعدی، یا سه بعدی باشد. شکل آرایه را می‌توان با دستور shape. بدست آورد و نیز با دستور dtype. می‌توان نوع داده‌های آرایه و مقدار فضای اختصاص یافته به آن را فهمید.

import numpy as np
a  = np.array([1,2,3])
print(a.shape)
print(a.dtype)
#### Different type
b  = np.array([1.1,2.0,3.2])
(۳, ) int64
float64

آرایه‌ی دو بعدی

بعد دوم را می‌توان با افزودن یک کاما “,” تعریف کرد. البته توجه کنید که داده‌های پس از کاما هم داخل براکت باز و بسته [] ( ویا پرانتز () اگر که تاپل باشد ) باید قرار گیرند.

### ۲ dimension
c = np.array([(1,2,3),
              (۴,۵,۶)])
print(c.shape)
(۲, ۳)

آرایه‌ی سه بعدی

آرایه‌ها با ابعاد بالاتر به شکل زیر تعریف می‌شوند: (دوباره یک کاما و یک براکت باز و بسته ی دیگر افزوده می‌شود.)

### ۳ dimension
d = np.array([
    [[۱, ۲,۳],
        [۴, ۵, ۶]],
    [[۷, ۸,۹],
        [۱۰, ۱۱, ۱۲]]
])
print(d.shape)
(۲, ۲, ۳)

اگر بخواهید فضای اشغال‌شده توسط آرایه را به دست‌ آورید از تابع itemsize استفاده کنید. به مثال زیر توجه کنید:

x = np.array([1,2,3], dtype=np.complex128)
x.itemsize
۱۶
# element x has 16 bytes

آرایه با مقادیر صفر و یا با مقادیر یک در NumPy

می‌توان با دستورات np.zeros و np.ones به ترتیب ماتریس‌هایی ایجاد کرد که تمامی عناصر آن صفر و یک باشند. چنین ماتریس‌هایی می‌توانند به طور مثال برای مقداردهی اولیه‌ی ماتریس وزن‌ها در شبکه‌ی عصبی مورد استفاده قرار گیرند. قاعده‌ی کلی به شکل زیر است:

numpy.zeros(shape, dtype=float, order='C')
numpy.ones(shape, dtype=float, order='C')

shape شکل آرایه را تعیین می‌کند و dtype یا همان datatype نوع داده را مشخص می‌کند و پارامتری اختیاری است که به طور پیش‌فرض مقدار آن float64 است. پارامتر order هم اختیاری است و مقدار پیش‌فرض C را می‌گیرد و یک استایل پایه برای سطرهای ماتریس است. در زیر چند مثال را می‌بینیم.

#Example numpy zero
import numpy as np
np.zeros((2,2))

#Example numpy zero with datatype
np.zeros((2,2), dtype=np.int16)

#Example numpy one 2D Array with datatype
np.ones((1,2,3), dtype=np.int16)

خروجی‌ها به ترتیب زیر هستند:

array([[0., 0.], [0., 0.]])
array([[0, 0], [0, 0]], dtype=int16)
array([[[1, 1, 1], [1, 1, 1]]], dtype=int16)

numpy.reshape و numpy.flatten در پایتون

گاهی شما نیاز به تغییر (reshape) داده‌ها از فرم عرضی به طولی دارید. برای این کار از تابع reshape به شکل زیر استفاده می‌کنیم: (a ورودی ماست.)

numpy.reshape(a, newShape, order='C')
import numpy as np
e  = np.array([(1,2,3), (4,5,6)])
print(e)
e.reshape(3,2)
// Before reshape [[1 2 3] [4 5 6]] //After Reshape array([[1, 2], [3, 4], [5, 6]])

گاهی در حین کار با شبکه‌های عصبی نیاز دارید تا آرایه‌ی خود را مسطح (flat) کنید. برای این‌کار از flatten. به شکل زیر استفاده می‌کنیم:

numpy.flatten(order='C')
e.flatten()
array([1, 2, 3, 4, 5, 6])

اتصال آرایه‌ها به هم در NumPy (Stacking)

چندین آرایه می‌توانند در امتداد محورهای مختلف به همدیگر متصل شوند. که در این بخش از مقاله‌ی آموزش کامل کتابخانه NumPY ، انواع اتصال آرایه‌ها به هم را توضیح می‌دهیم.
np.vstack: آرایه‌ها را به طور عمودی به هم وصل می‌کند.
np.hstack: آرایه‌ها را به طور افقی متصل می‌کند.
np.column_stack: برای اتصال آرایه‌ی یک بُعدی به عنوان ستون به سطر آرایه‌ی دوبعدی.
np.concatenate: برای اتصال آرایه‌ها به همدیگر در خلال یک محور معین (یعنی محور یا همان axis به عنوان پارامتر ورودی تابع تعریف می‌شود.)
مثال زیر نحوه‌ی کارکرد هر یک را نشان می‌دهد.

import numpy as np 
  
a = np.array([[1, 2], 
              [۳, ۴]]) 
  
b = np.array([[5, 6], 
              [۷, ۸]]) 
  
# vertical stacking 
print("Vertical stacking:\n", np.vstack((a, b))) 
  
# horizontal stacking 
print("\nHorizontal stacking:\n", np.hstack((a, b))) 
  
c = [5, 6] 
  
# stacking columns 
print("\nColumn stacking:\n", np.column_stack((a, c))) 
  
# concatenation method  
print("\nConcatenating to 2nd axis:\n", np.concatenate((a, b), 1)) 
Vertical stacking: [[1 2] [3 4] [5 6] [7 8]]
Horizontal stacking: [[1 2 5 6] [3 4 7 8]]
Column stacking: [[1 2 5] [3 4 6]]
Concatenating to 2nd axis: [[1 2 5 6] [3 4 7 8]]

تقسیم آرایه در NumPy (Splitting)

در این بخش از مقاله‌ی آموزش کامل کتابخانه NumPY ، توابعی برای تقسیم آرایه‌ها را معرفی می‌کنیم.
توابعی برای تقسیم آرایه‌ها تعریف شده که به شرح زیر است:
np.hsplit: تقسیم آرایه به شکل افقی.
np.vsplit: تقسیم آرایه به شکل عمودی.
np.array_split: آرایه را در خلال امتداد تعیین شده در ورودی (با پارامتر axis) تقسیم می‌کند.

import numpy as np 
  
a = np.array([[1, 3, 5, 7, 9, 11], 
              [۲, ۴, ۶, ۸, ۱۰, ۱۲]]) 
  
# horizontal splitting 
print("Splitting along horizontal axis into 2 parts:\n", np.hsplit(a, 2)) 
  
# vertical splitting 
print("\nSplitting along vertical axis into 2 parts:\n", np.vsplit(a, 2))
Splitting along horizontal axis into 2 parts: [array([[1, 3, 5], [2, 4, 6]]), array([[ 7, 9, 11], [ 8, 10, 12]])]
Splitting along vertical axis into 2 parts: [array([[ 1, 3, 5, 7, 9, 11]]), array([[ 2, 4, 6, 8, 10, 12]])]

پیمایش آرایه در NumPy

پیمایش آرایه یا Iterate کردن آن یعنی عناصر آن را یکی یکی مشاهده کنیم. چون با آرایه‌های چند بعدی در numpy سروکار داریم، پیمایش را می‌توان با حلقه‌ی for در پایتون انجام داد. آرایه‌ی یک بعدی (۱D) را به شکل زیر پیمایش می‌کنیم:

import numpy as np

arr = np.array([1, 2, 3])

for x in arr:
  print(x)
۱ ۲ ۳

در آرایه‌ی دو بعدی (۲D) پیمایش به صورت سطر به سطر انجام می‌شود:

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])

for x in arr:
  print(x)
[۱ ۲ ۳] [۴ ۵ ۶]

اگر بخواهیم مقادیر به صورت اسکالر برگردانده شود، به شکل زیر عمل می‌کنیم.

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])

for x in arr:
  for y in x:
    print(y)
۱ ۲ ۳ ۴ ۵ ۶

در پیمایش آرایه‌ی سه بعدی (۳D) آرایه‌های دوبعدی آن را جدا می‌کنیم:

import numpy as np

arr = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])

for x in arr:
  print(x)
[[۱ ۲ ۳] [۴ ۵ ۶]] [[ ۷ ۸ ۹] [۱۰ ۱۱ ۱۲]]

برای برگرداندن هر عنصر نیز باید آرایه را در هر بعد، جداگانه پیمایش کنیم:

import numpy as np

arr = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])

for x in arr:
  for y in x:
    for z in y:
      print(z)
۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۱۰ ۱۱ ۱۲

پیمایش آرایه با nditer

تابع nditer تابعی است که می‌تواند از ساده‌ترین تا پیچیده‌ترین حالت پیمایش را اجرا کند و بسیاری از مشکلات ما را در زمینه‌ی Iteration حل می‌کند. به طور مثال برای استخراج عناصر آرایه‌ی سه بعدی به شکل اسکالر می‌توان با این تابع به شکل زیر عمل کرد و درگیر پیچیدگی forهای تودرتو نشد:

import numpy as np

arr = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])

for x in np.nditer(arr):
  print(x)
 
۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸

پیمایش آرایه با نوع داده‌ای مختلف

می‌توان از آرگومان op_dtypes استفاده کرد و نوع داده‌ها را به نوع داده‌ی مورد نظر حین پیمایش تغییر داد. Numpy قادر نیست به طور درجا نوع داده‌ها را تغییر دهد و برای این کار به فضای اضافی نیاز دارد که این فضای اضافه بافر نام دارد و برای فعال‌سازی بافر در تابع nditer از flag=[‘buffered’] استفاده می‌کنیم.

import numpy as np

arr = np.array([1, 2, 3])

for x in np.nditer(arr, flags=['buffered'], op_dtypes=['S']):
  print(x)
b'1' b'2' b'3'

پیمایش با گام‌های متفاوت

می‌توانیم داده‌ها را به طور فیلتر شده پیمایش کنیم. به طور مثال در آرایه‌ی دوبعدی زیر یک در میان عناصر را خواند.

import numpy as np

arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])

for x in np.nditer(arr[:, ::2]):
  print(x)
۱ ۳ ۵ ۷

پیمایش با اندیس‌ها

گاهی حین پیمایش عناصر به اندیس عنصر مورد نظر نیاز داریم. متد ndenumerate برای این منظور به کار می‌رود.

import numpy as np

arr = np.array([1, 2, 3])

for idx, x in np.ndenumerate(arr):
  print(idx, x)
(۰,) ۱ (۱,) ۲ (۲,) ۳

مثالی از آرایه‌ی دو بعدی را هم در این خصوص ببینید:

import numpy as np

arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])

for idx, x in np.ndenumerate(arr):
  print(idx, x)
(۰, ۰) ۱ (۰, ۱) ۲ (۰, ۲) ۳ (۰, ۳) ۴ (۱, ۰) ۵ (۱, ۱) ۶ (۱, ۲) ۷ (۱, ۳) ۸

جبرخطی در NumPy

در ادامه‌ی مقاله‌ی آموزش کامل کتابخانه NumPY به آموزش جبر خطی در NumPY می‌پردازیم، ماژول جبرخطی در NumPy متدهای بسیاری را برای اعمال روی انواع آرایه‌ها در NumPy فراهم می‌کند. شما با این ماژول قادر هستید موارد زیر را محاسبه کنید:

  • مرتبه، اثر ماتریس (trace of matrix) و دترمینان یک آرایه
  • مقادیر ویژه‌ی یک ماتریس (eigen values)
  • ضرب‌های ماتریسی و برداری (ضرب نقطه‌ای، داخلی، خارجی و …)
  • حل معادلات خطی یا تانسور

مثال زیر چگونگی اعمال عملیات جبری را نشان می‌دهد.

import numpy as np 
  
A = np.array([[6, 1, 1], 
              [۴, -۲, ۵], 
              [۲, ۸, ۷]]) 
  
print("Rank of A:", np.linalg.matrix_rank(A)) 
  
print("\nTrace of A:", np.trace(A)) 
  
print("\nDeterminant of A:", np.linalg.det(A)) 
  
print("\nInverse of A:\n", np.linalg.inv(A)) 
  
print("\nMatrix A raised to power 3:\n", np.linalg.matrix_power(A, 3))
Rank of A: 3
Trace of A: 11
Determinant of A: -306.0
Inverse of A: [[ 0.17647059 -0.00326797 -0.02287582] [ 0.05882353 -0.13071895 0.08496732] [-0.11764706 0.1503268 0.05228758]]
Matrix A raised to power 3: [[336 162 228] [406 162 469] [698 702 905]]

تصور کنید می‌خواهیم این معادله‌ی خطی را محاسبه کنیم.
x + 2*y = 8
۳*x + 4*y = 18
این معادله با به‌کارگیری متد linalg.solve قابل حل است.

import numpy as np 
  
# coefficients 
a = np.array([[1, 2], [3, 4]]) 
# constants 
b = np.array([8, 18]) 
  
print("Solution of linear equations:", np.linalg.solve(a, b))
Solution of linear equations: [ 2.  3.]

در آخر مثالی را بررسی می‌کنیم که چگونگی محاسبه‌ی رگرسیون خطی را با استفاده از روش کاهش مربعات خطا نشان می‌دهد.

یادآوری

رگرسیون خطی، خطی است که به فرم w1xi+w2=y است و سعی دارد wiها را طوری تعیین کند که مجموع مربعات فاصله‌ی نقاطی مانند yi از خط حاصل به کمترین مقدار برسد. (yi مقدار حقیقی برای صفات xi است و w1xi+w2 مقدار تخمینی توسط رگرسیون خطی است.) برای مطالعه‌ی بیشتر می‌توانید به توضیحات مقاله‌ی رگرسیون خطی مراجعه کنید.

import numpy as np 
import matplotlib.pyplot as plt 
  
# x co-ordinates 
x = np.arange(0, 9) 
A = np.array([x, np.ones(9)]) 
  
# linearly generated sequence 
y = [19, 20, 20.5, 21.5, 22, 23, 23, 25.5, 24] 
# obtaining the parameters of regression line 
w = np.linalg.lstsq(A.T, y)[0]  
  
# plotting the line 
line = w[0]*x + w[1] # regression line 
plt.plot(x, line, 'r-') 
plt.plot(x, y, 'o') 
plt.show()

خروجی این معادله به شکل زیر است:

ایجاد یک آرایه‌ی NumPy

تولید اعداد تصادفی در NumPy

برای تولید اعداد تصادفی در NumPy به طور کلی از تابع random استفاده می‌کنیم و می‌توانیم برای اینکه این اعداد تصادفی در یک توزیع خاص آماری با ویژگی‌های مورد نظر ما باشند، از توابع دیگری نیز کمک بگیریم.

numpy.random.rand(d0,d1,…,dn): آرایه‌ای در ابعاد مشخص ایجاد می‌کند و درایه‌های آن را با اعداد تصادفی پر می‌کند. d0,d1,…,dn ابعاد آرایه را مشخص می‌کنند و اعدادی صحیح و اختیاری‌اند. اگر آرگومانی داده نشود یک مقدار اعشاری تولید می‌کند.
numpy.random.normal(loc, scale, size): آرایه‌ای در شکل و ابعاد مشخص تولید می‌کند و درایه‌های آن را با مقادیر تصادفی که بخشی از یک توزیع نرمال (گوسی) محسوب می‌شوند، پر می‌کند. به این توزیع به خاطر شکلی که دارد، منحنی زنگ هم می‌گویند. و اما پارامترهای این تابع:

  • loc: میانگین توزیع.
  • scale: انحراف معیار توزیع.
  • size: تعداد نمونه داده‌هاست.

مثال‌های زیر را درنظر بگیرید:

# numpy.random.rand() method 
   
import numpy as np 
   
# ۱D Array 
array = np.random.rand(5) 
print("1D Array filled with random values : \n", array) 
۱D Array filled with random values :
[ ۰٫۸۴۵۰۳۹۶۸  ۰٫۶۱۵۷۰۹۹۴  ۰٫۷۶۱۹۹۴۵   ۰٫۳۴۹۹۴۸۰۳  ۰٫۴۰۱۱۳۷۶۱]

در مثال بعدی آرایه‌ای یک بُعدی که از توزیع گوسی پیروی می‌کند، تولید می‌کنیم:

import numpy as np 
   
# ۱D Array 
array = np.random.normal(0.0, 1.0, 5) 
print("1D Array filled with random values "
      "as per gaussian distribution : \n", array) 
# ۳D array 
array = np.random.normal(0.0, 1.0, (2, 1, 2)) 
print("\n\n3D Array filled with random values "
      "as per gaussian distribution : \n", array) 
۱D Array filled with random values as per gaussian distribution :
[-۰٫۹۹۰۱۳۱۷۲ -۱٫۵۲۵۲۱۸۰۸  ۰٫۳۷۹۵۵۶۸۴  ۰٫۵۷۸۵۹۲۸۳  ۱٫۳۴۳۳۶۸۶۳]
 
۳D Array filled with random values as per gaussian distribution :
[[[-۰٫۰۳۲۰۳۷۴   ۲٫۱۴۹۷۷۸۴۹]]
 
[[ ۰٫۳۷۸۹۵۸۵   ۰٫۱۷۶۹۲۱۲۵]]]

مثال زیر نیز تفاوت تولید اعداد تصادفی در حالت معمول با حالتی که از توزیع نرمال پیروی می‌کند را نمایش می‌دهد (از کتابخانه‌ی matplot برای رسم نمودار کمک گرفته‌ایم):

import numpy as np 
import matplotlib.pyplot as plot 
   
# ۱D Array as per Gaussian Distribution 
mean = 0 
std = 0.1
array = np.random.normal(0, 0.1, 1000) 
print("1D Array filled with random values "
      "as per gaussian distribution : \n", array); 
  
# Source Code :  
# https://docs.scipy.org/doc/numpy-1.13.0/reference/ 
# generated/numpy-random-normal-1.py 
count, bins, ignored = plot.hist(array, 30, normed=True) 
plot.plot(bins, 1/(std * np.sqrt(2 * np.pi)) *
          np.exp( - (bins - mean)**2 / (2 * std**2) ), 
          linewidth=2, color='r') 
plot.show() 
  
  
# ۱D Array constructed Randomly 
random_array = np.random.rand(5) 
print("1D Array filled with random values : \n", random_array) 
  
plot.plot(random_array) 
plot.show() 

خروجی این کد به شکل زیر است:

عملیات ریاضی بر روی آرایه‌ها

توابع دیگری نیز برای تولید اعداد تصادفی با ویژگی‌های مختلف وجود دارد که پیشنهاد می‌دهیم آن‌ها را از داکیومنت مربوطه، مطالعه کنید.

کار با تاریخ و زمان در NumPy

در این بخش از مقاله‌ی آموزش کامل کتابخانه NumPY کار با تاریخ و زمان در NumPY را آموزش می‌دهیم، NumPy دارای انواعی از ساختارهای داده (Data Structure) است که به طور طبیعی از عملکرد datetime پشتیبانی می‌کنند. این نوع داده ‘datetime64’ نامیده می‌شود که از کتابخانه‌ای در زبان پایتون با همین نام برگرفته شده‌است. در مثال زیر چند نمونه از کاربرد زمان و تاریخ را می‌بینید.

mport numpy as np 
  
# creating a date 
today = np.datetime64('2017-02-12') 
print("Date is:", today) 
print("Year is:", np.datetime64(today, 'Y')) 
  
# creating array of dates in a month 
dates = np.arange('2017-02', '2017-03', dtype='datetime64[D]') 
print("\nDates of February, 2017:\n", dates) 
print("Today is February:", today in dates) 
  
# arithmetic operation on dates 
dur = np.datetime64('2017-05-22') - np.datetime64('2016-05-22') 
print("\nNo. of days:", dur) 
print("No. of weeks:", np.timedelta64(dur, 'W')) 
  
# sorting dates 
a = np.array(['2017-02-12', '2016-10-13', '2019-05-22'], dtype='datetime64') 
print("\nDates in sorted order:", np.sort(a))
Date is: 2017-02-12 Year is: 2017
Dates of February, 2017: ['2017-02-01' '2017-02-02' '2017-02-03' '2017-02-04' '2017-02-05' '2017-02-06' '2017-02-07' '2017-02-08' '2017-02-09' '2017-02-10' '2017-02-11' '2017-02-12' '2017-02-13' '2017-02-14' '2017-02-15' '2017-02-16' '2017-02-17' '2017-02-18' '2017-02-19' '2017-02-20' '2017-02-21' '2017-02-22' '2017-02-23' '2017-02-24' '2017-02-25' '2017-02-26' '2017-02-27' '2017-02-28'] Today is February: True
No. of days: 365 days No. of weeks: 52 weeks
Dates in sorted order: ['2016-10-13' '2017-02-12' '2019-05-22']

numpy.asarray در پایتون

تابع as array برای تبدیل داده‌ی ورودی به آرایه استفاده می‌شود. ورودی می‌تواند به شکل لیست، تاپل و یا ndarray و… باشد. فرم کلی به شکل زیر است:

numpy.asarray(data, dtype=None, order=None)
  • data: داده‌ای که می‌خواهید به آرایه تبدیل کنید.
  • dtype: یک پارامتر اختیاری است و اگر مقداردهی نشود، مقدار آن از نوع داده‌های ورودی گرفته می‌شود.
  • order: یک پارامتر اختیاری است و مقدار پیش‌فرض ان C است و استایل پایه برای سطرهاست و مقدار دیگری که می‌تواند بگیرد F است.

ماتریس دو بُعدی زیر را که شامل چهار سطر و ستون پرشده با یک است در نظر بگیرید:

import numpy as np
A = np.matrix(np.ones((4,4)))

نمی‌توان مقدار درایه‌های این ماتریس را با کپی و انتساب به شکل زیر تغییر داد:

np.array(A)[2]=2
print(A)	
[[۱٫ ۱٫ ۱٫ ۱٫] [۱٫ ۱٫ ۱٫ ۱٫]  [۱٫ ۱٫ ۱٫ ۱٫]  [۱٫ ۱٫ ۱٫ ۱٫]]

ماتریس تغییرناپذیر است. اگر بخواهید تغییری در مقادیر درایه‌ها داشته باشید، می‌توانید از تابع asarray استفاده کنید و مثلاً مقادیر سطر سوم را به دو تغییر دهیم:

np.asarray(A)[2]=2
print(A)
[[۱٫ ۱٫ ۱٫ ۱٫] [۱٫ ۱٫ ۱٫ ۱٫] [۲٫ ۲٫ ۲٫ ۲٫] # new value [1. 1. 1. 1.]]

numpy.arrange در پایتون

این تابع در NumPy آرایه‌ای را برمی‌گرداند که نتیجه‌ی آن بازه‌ای از اعداد است که فاصله‌ی بین آن‌ها برابر است. قاعده‌ی کلی دستور به شکل زیر است:

numpy.arrange(start, stop,step)

  • start: شروع بازه.
  • stop: پایان بازه (که خود این عدد را شامل نمی‌شود.)
  • step: فاصله‌ی میان اعداد در بازه که به‌طور پیش‌فرض یک است.

array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

مثال دیگر:

import numpy np
np.arrange(1, 14, 4)
array([ 1, 5, 9, 13])

numpy.linspace و numpy.logspace در پایتون

این تابع نیز دنباله‌ای از اعداد را تولید می‌کند و قاعده‌ی تعریف آن به صورت زیر است:

numpy.linspace(start, stop, num, endpoint)

  • start: مقدار شروع دنباله.
  • stop: مقدار پایان دنباله (خود مقدار پایانی در نظر گرفته می‌شود.)
  • num: تعداد اعداد مورد نظر که مقدار پیش‌فرض ۵۰ را دارد.
  • endpoint: اگر True باشد، (که به طور پیش فرض هست) مقدار stop را به عنوان مقدار نهایی درنظرمی‌گیرد و اگر False بود آن را در نظر نمی‌گیرد.

به عنوان مثال می‌توان ده مقدار با فاصله‌ی یکسان در بازه‌ی ۱ تا ۵ به شکل زیر تولید کرد:

import numpy as np
np.linspace(1.0, 5.0, num=10)
 array([1. , 1.44444444, 1.88888889, 2.33333333, 2.77777778, 3.22222222, 3.66666667, 4.11111111, 4.55555556, 5. ])

اگر نمی‌خواهید آخرین عدد در بازه‌ جزو اعداد تولیدی باشد، همان‌طور که گفته‌ شد مقدار endpoint را برابر False قرار دهید:

np.linspace(1.0, 5.0, num=5, endpoint=False)
array([1. , 1.8, 2.6, 3.4, 4.2])

np.logspace هم مشابه linspace عمل می‌کند با این تفاوت که توان اعداد را محاسبه می‌کند و پایه به طور پیش‌فرض برابر عدد ۱۰ است. قاعده‌ی آن به شکل زیر است:

numpy.logspace(start, stop, num, endpoint)

حالات مختلف را با مثال‌های زیر بررسی می‌کنیم:

np.logspace(3.0, 4.0, num=4)
array([ 1000. , 2154.43469003, 4641.58883361, 10000. ])
np.logspace(4.0, 5.0, num=3, endpoint=False)
array([ 10000. , 21544.34690032, 46415.88833613])
np.logspace(4.0, 5.0, num=3, base=2.0)
array([ 16. , 22.627417, 32. ])

اندیس‌دهی (Indexing) و برش (Slicing) آرایه‌های NumPy در پایتون

برش ماتریس و یا آرایه توسط NumPy کار ساده‌ای است. به مثال‌های زیر توجه کنید:

## Slice
import numpy as np
e  = np.array([(1,2,3), (4,5,6)])
print(e)
[[۱ ۲ ۳], [۴ ۵ ۶]]
print('First row:', e[0])
print('Second row:', e[1])
First row: [1 2 3] Second row: [4 5 6]

یادآوری:
در زبان پایتون داریم:

  • مقدار قبل از کاما در براکت، به سطر اشاره می‌کند.
  • مقدار سمت راست کاما به ستون اشاره می‌کند.
  • اگر بخواهید تنها ستون را انتخاب کنید، قبل از شماره‌ی ستون باید دو نقطه (:) قرار دهید [ :column_index] .
  • دو نقطه [ :column_index] به این معنی است که شما تمامی سطرهای آن ستون را انتخاب کرده‌اید.
print('Second column:', e[:,1])
Second column: [2 5]

برای برگرداندن دو مقدار اول از سطر دوم (اندیس یکم) آرایه‌ی e دستور را به شکل زیر می‌نویسند: (توجه کنید باید دو نقطه را قرار دهید تا همه‌ی مقادیر پیش از ستون سوم یعنی اندیس دوم، با اندیس‌های صفر و یک، انتخاب شوند.)

## Second Row, two values
  print(e[1, :2])			
 #result
 [۴ ۵]	

توابع آماری NumPy

این کتابخانه تعدادی تابع آماری برای محاسبه‌ی مواردی همچون مینیمم، ماکزیمم، میانگین، میانه، دهک و صدک، انحراف معیار، واریانس و … برای اعمال روی آرایه‌ها و ماتریس‌های ورودی دارد. در جدول زیر برخی از این توابع آمده‌اند:

کار با کتابخانه NumPy

آرایه‌ی زیر را در نظر بگیرید:

import numpy as np
normal_array = np.random.normal(5, 0.5, 10)
print(normal_array)
[۵٫۵۶۱۷۱۸۵۲ ۴٫۸۴۲۳۳۵۵۸ ۴٫۶۵۳۹۲۷۶۷ ۴٫۹۴۶۶۵۹ ۴٫۸۵۱۶۵۵۶۷ ۵٫۶۱۲۱۱۳۱۷ ۴٫۴۶۷۰۴۲۴۴ ۵٫۲۲۶۷۵۷۳۶ ۴٫۴۹۸۸۸۹۳۶ ۴٫۶۸۷۳۱۱۲۵]

توابع آماری را روی داده‌های تولید شده به شکل زیر اعمال می‌کنیم:

### Min 
print(np.min(normal_array))

### Max 
print(np.max(normal_array))

### Mean 
print(np.mean(normal_array))

### Median
print(np.median(normal_array))

### Std
print(np.std(normal_array))
۴٫۴۶۷۰۴۲۴۳۵۲۶۶۹۱۳ ۵٫۶۱۲۱۱۳۱۷۱۹۹۰۲۰۱ ۴٫۹۳۴۸۴۱۰۰۲۲۷۰۵۹۳ ۴٫۸۴۶۹۹۵۶۲۵۷۸۶۶۶۳ ۰٫۳۸۷۵۰۱۹۳۶۷۳۹۵۳۱۶

ضرب در پایتون به کمک NumPy

numpy.dot ضرب نقطه‌ای بین ماتریس‌ها یا همان ضرب ماتریسی را انجام می‌دهد. قاعده‌ی کلی به شکل زیر است:

numpy.dot(x, y, out=None)

x و y آرایه‌ی ورودی‌اند که باید ۱D یا ۲D (یک بُعدی یا دو بُعدی) باشند. خروجی اگر ورودی آرایه یک بعدی باشد، مقدار اسکالر و در غیر این‌صورت یک آرایه است. به مثال زیر توجه کنید:

## Linear algebra
### Dot product: product of two arrays
f = np.array([1,2])
g = np.array([4,5])
### ۱*۴+۲*۵
np.dot(f, g)
۱۴

اگر ورودی‌ها به صورت اسکالر باشند و یا ضرب عضو به عضو اعضای آرایه‌ها مدنظر باشد از numpy.multiply می‌توان استفاده کرد. اما اگر ضرب ماتریسی برای آرایه‌های دوبعدی می‌خواهیم انجام دهیم، numpy.matmul ترجیح داده می‌شود.

تفاوت میان Copy و View در NumPy

تفاوت میان یک کپی و یک view از یک آرایه در این است که کپی یک آرایه‌ی جدید است اما؛ view فقط یک دید از آرایه ایجاد می‌کند. کپی مسئول داده‌های خود است و هر گونه تغییر داده در آن، مستقل از آرایه‌ی اولیه است و بالعکس، هر تغییری در آرایه‌ی اولیه در کپی آن بی‌تأثیر است. درمورد view، این قضیه صادق نیست و تغییرات داده‌ها در view و آرایه‌ی اولیه بر یکدیگر اثر می‌گذارد.

Copy

یک کپی از آرایه ایجاد می‌کنیم و تغییرات آرایه و کپی آن را بر همدیگر مشاهده می‌کنیم:

import numpy as np

arr = np.array([1, 2, 3, 4, 5])
x = arr.copy()
arr[0] = 42

print(arr)
print(x)
[۴۲ ۲ ۳ ۴ ۵] [۱ ۲ ۳ ۴ ۵]

View

یک view از آرایه ایجاد می‌کنیم و تغییرات آرایه و view آن را بر همدیگر مشاهده می‌کنیم:

import numpy as np

arr = np.array([1, 2, 3, 4, 5])
x = arr.view()
x[0] = 31

print(arr)
print(x)
[۳۱ ۲ ۳ ۴ ۵] [۳۱ ۲ ۳ ۴ ۵]

همان‌طور که گفته شد کپی بر داده‌های خود مالکیت دارد اما view نه. برای بررسی مالکیت آرایه‌ای درNumPy ، می‌توان مقدار ویژگی base آن را بررسی کرد. اگر None بود، یعنی مالکیت دارد و اگر به آبجکت (Object) اصلی و اولیه اشاره کرد یعنی مالکیت ندارد.

import numpy as np

arr = np.array([1, 2, 3, 4, 5])

x = arr.copy()
y = arr.view()

print(x.base)
print(y.base)
None [1 2 3 4 5]

نتیجه‌گیری

در مقاله‌ی آموزش کامل کتابخانه NumPY ، با توجه به اهمیت انجام محاسبات در علم داده با توابع کتابخانه‌ی NumPy که از پکیج‌های اساسی در ساخت سایر پکیج‌های داده‌کاوی با پایتون است، آشنا شدیم. NumPy به عنوان کتابخانه‌ای با استفاده ی گسترده در محاسبات نقشی مهم در سایر کتابخانه‌های محاسباتی پایتون همچون sikit-learn ،tensorflow ،matplot ،opencv و… دارد. داشتن درک درستی از این کتابخانه در فهم سایر کتابخانه‌های پیشرفته‌تر بسیار مفید خواهد بود. (این بخش برگرفته از آموزش Numpy در وبسایت ۷learn میباشد.)

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا