۳ بهمن ۱۳۹۹
1474 بازدید
مقالات تصادفی
- اینستاگرام داشبورد حرفهای جدیدی برای حسابهای کاربری تجاری معرفی کرد
- شناسایی هشت اپلیکیشن اندرویدی مخرب که حساب بانکی کاربران را خالی میکنند
- ایلان ماسک میخواهد شهری جدید با ارز رایج دوجکوین در تگزاس بسازد
- آموزش کامل و تصویری بازیابی پستهای حذف شده اینستاگرام
- آیا مایکروسافت برای انتشار ویندوز ۱۱ آماده میشود؟
- نرم افزار word چیست؟ آشنایی با ورد
- ۱۰ نرمافزار برتر طراحی رابط کاربری UI را بشناسید
- آموزش نحوه حذف کلی و کامل اکانت تلگرام
- 5 ویژگی که از اندروید 12 انتظار داریم
- حداقل و حداکثر هزینه طراحی وبسایت چقدر است؟
![راهنمای جامع کتابخانه نامپای در پایتون (NumPy)](https://arkascms.ir/upload/images/post-img/1611310849.png)
NumPy یک کتابخانه برای زبان برنامه نویسی پایتون (Python) است. با استفاده از این کتابخانه امکان استفاده از آرایهها و ماتریسهای بزرگ چند بعدی فراهم میشود. همچنین میتوان از تابعهای ریاضیاتی سطح بالا بر روی این آرایهها استفاده کرد. پیش از اینکه این آموزش را مطالعه کنید، میبایست دستکم اندکی با زبان برنامهنویسی پایتون آشنایی داشته باشید. همچنین اگر نیاز دارید معلوماتتان را در مورد پایتون یکبار یادآوری کنید، میتوانید از آموزش پایتون استفاده کنید.
همچنین اگر میخواهید بر روی نمونههای ارائه شده در این آموزش کار کنید، باید برخی نرمافزارها را روی رایانه خود نصب کنید. دستکم موارد زیر الزامی هستند.
- پایتون
- NumPy
این نرمافزارها هم مفید هستند.
- ipython یک پوسته (Shell) تعاملی بهبود یافته برای پایتون است که برای بررسی خصوصیات NumPy بسیار مناسب است.
- matplotlib به شما امکان رسم نمودار را میدهد.
- SciPy بسیاری از رویههای علمی را در خود دارد که بر روی NumPy عمل میکنند.
مفاهیم پایه
هدف اصلی NumPy فراهم ساختن امکان کار با آرایههای چندبعدی همگن است. این آرایهها جدولی از عناصر (معمولاً اعداد) هستند که همگی از یک نوع میباشند و با یک چندتایی، از اعداد صحیح مثبت اندیسگذاری میشوند. در NumPy ابعاد به نام محور (axe) شناخته میشوند. تعداد محورها رتبه (rank) نامیده میشود.
برای مثال، مختصات یک نقطه در فضای 3 بعدی [1, 2, 1] یک آرایه با رتبه 1 است زیرا یک محور دارد. این محور طولی بهاندازه 3 دارد. در مثال زیر آرایه رتبه 2 دارد (2 بعدی است). بعد (محور) نخست طولی به اندازه 2 دارد، بعد دوم طول 3 دارد.
[[ 1., 0., 0.],
[ 0., 1., 2.]]
کلاس آرایه Numpy بهصورت ndarray نامگذاری شده است. همچنین بهصورت مستعار array نامیده میشود. توجه داشته باشید که numpy.array همان کلاس کتابخانه استاندارد پایتون به نام array.array نیست. کتابخانه استاندارد پایتون تنها آرایههای تکبعدی را مدیریت میکند و کاربردهای اندکی دارد. خصوصیات مهمتر یک ndarray بدین ترتیب هستند.
ndarray.ndim
تعداد محور (ابعاد) آرایه است. در دنیای پایتون تعداد ابعاد بهصورت رتبه نامیده میشود.
ndarray.shape
ابعاد یک آرایه است. این خصوصیت از یک چندتایی اعداد صحیح تشکیل یافته است که نشاندهنده اندازه هر بعد آرایه هستند. برای یک ماتریس با n ردیف و m ستون، شکل (shape) بهصورت (n,m) خواهد بود. بدین ترتیب طول چندتایی shape برابر با رتبه آرایه یا تعداد ابعاد ndim است.
ndarray.size
تعداد کل عناصر آرایه است. این مقدار برابر با حاصلضرب اجزای shape است.
ndarray.dtype
نوع عناصر یک آرایه را توصیف میکند. فرد میتواند dtype آرایه را با استفاده از انواع استاندارد پایتون ایجاد یا توصیف کند. بهعلاوه NumPy انواع مخصوص به خود را نیز دارد. برای مثال numpy.int32، numpy.int16 و numpy.float64 نمونههایی از انواع آرایه تعریف شده در NumPy هستند.
ndarray.itemsize
اندازه بایتهای هر یک از عناصر آرایه است. برای نمونه itemsize یک آرایه از عناصری با نوع float64 برابر با 8 (64/8) است در حالی که itemsize یک آرایه از نوع complex32 برابر با 4 (32/8) است. این مقدار معادل ndarray.dtype.itemsize است.
ndarray.data
این بافر (buffer) حاوی عناصر واقعی آرایه است. بهطورمعمول ما نیاز نداریم از این خصوصیت استفاده کنیم، زیرا با استفاده از امکان اندیسگذاری میتوانیم به عناصر آرایه دسترسی داشته باشیم.
مثال
>>> from numpy import *
>>> a = arange(15).reshape(3, 5)
>>> a
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> a.shape
(3, 5)
>>> a.ndim
2
>>> a.dtype.name
'int32'
>>> a.itemsize
4
>>> a.size
15
>>> type(a)
numpy.ndarray
>>> b = array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
numpy.ndarray
ایجاد آرایه
چند روش برای ایجاد آرایه وجود دارند. برای مثال، میتوان با استفاده از تابع array یک آرایه را از فهرست معمولی پایتون یا چندتاییها ایجاد کرد. نوع آرایه حاصل، برابر با نوع عناصر موجود در دنبالههای تشکیل دهنده آن خواهد بود.
>>> from numpy import *
>>> a = array( [2,3,4] )
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int32')
>>> b = array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64')
یکی از خطاهای رایج در کار کردن با آرایههای چندبعدی زمانی رخ میدهد که قصد داریم array را با چند آرگومان عددی فراخوانی کنیم، در حالی که باید از فهرست منفردی از اعداد به عنوان آرگومان استفاده کنیم.
>>> a = array(1,2,3,4) # اشتباه
>>> a = array([1,2,3,4]) # صحیح
array دنبالهای از دنبالهها را به آرایههای چندبعدی تبدیل میکند، دنبالهای از دنبالههای دنبالهها به آرایههای سهبعدی تبدیل میشود و همینطور تا آخر.
>>> b = array( [ (1.5,2,3), (4,5,6) ] )
>>> b
array([[ 1.5, 2. , 3. ],
[ 4. , 5. , 6. ]])
نوع آرایه را میتوان در زمان ایجاد آن به طور صریح تعیین کرد.
<span class="line">>>> <span class="ID">c</span> = <span class="ID">array</span>( [ [<span class="Number">1</span>,<span class="Number">2</span>], [<span class="Number">3</span>,<span class="Number">4</span>] ], <span class="ID">dtype</span>=<span class="ResWord">complex</span> )</span>
<span class="line"><span id="line-2-5" class="anchor"></span>>>> <span class="ID">c</span></span>
<span class="line"><span id="line-3-5" class="anchor"></span><span class="ID">array</span>([[ <span class="Number">1.</span>+<span class="Number">0.j</span>, <span class="Number">2.</span>+<span class="Number">0.j</span>],</span>
<span class="line"><span id="line-4-4" class="anchor"></span> [ <span class="Number">3.</span>+<span class="Number">0.j</span>, <span class="Number">4.</span>+<span class="Number">0.j</span>]])</span>
معمولاً عناصر یک آرایه از ابتدا مشخص نیستند، اما اندازه آن مشخص است. از اینرو NumPy چند تابع برای ایجاد آرایه با جایگاههای ابتدایی مشخص پیشنهاد میکند. بدین ترتیب در ادامه نیازی به بسط آرایه که عملیات پرهزینهای است، وجود نخواهد داشت. تابع zeros یک آرایه با مقادیر تماماً صفر ایجاد میکند. تابع ones یک آرایه با مقادیر 1 تولید میکند و تابع empty یک آرایه ایجاد میکند که محتوای اولیه آن تصادفی است و به وضعیت حافظه بستگی دارد. به طور پیشفرض dtype آرایه ایجاد شده، برابر با float64 است.
>>> zeros( (3,4) )
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> ones( (2,3,4), dtype=int16 ) # را هم میتوان تعیین کرد dtype
array([[[ 1, 1, 1, 1],
[ 1, 1, 1, 1],
[ 1, 1, 1, 1]],
[[ 1, 1, 1, 1],
[ 1, 1, 1, 1],
[ 1, 1, 1, 1]]], dtype=int16)
>>> empty( (2,3) )
array([[ 3.73603959e-262, 6.02658058e-154, 6.55490914e-260],
[ 5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])
NumPy برای ایجاد دنبالههایی از اعداد یک تابع مشابه range ارائه کرده است که بهجای لیست، یک آرایه برمیگرداند.
>>> arange( 10, 30, 5 )
array([10, 15, 20, 25])
>>> arange( 0, 2, 0.3 ) # آرگومانهای اعشاری میپذیرد
array([ 0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8]
زمانی که arange با آرگومانهای اعشاری استفاده میشود، به دلیل دقت متناهی اعداد اعشاری، عموماً امکان پیشبینی تعداد عناصر به دست آمده وجود ندارد. به همین دلیل معمولاً استفاده از تابع linspace که تعداد عناصر مطلوب را نیز به عنوان یک آرگومان میگیرد، بهتر است:
>>> linspace( 0, 2, 9 ) # 9 عدد از 0 تا 2
array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ])
>>> x = linspace( 0, 2*pi, 100 ) # برای تابع ارزیابی در نقاط زیاد مناسب است
>>> f = sin(x)
پرینت کردن آرایهها
زمانی که یک آرایه را پرینت میکنید NumPy آن را به صوت یک فهرست تودرتو نمایش میدهد که طرح کلی آن بهصورت زیر است:
- آخرین محور از چپ به راست پرینت میشود.
- محور ماقبل آخر از بالا به پایین پرینت میشود.
- باقی محورها نیز از بالا به پایین پرینت و هرکدام با یک خط خالی از قبلی جدا میشوند.
بدین ترتیب آرایههای تکبعدی بهصورت ردیفی، آرایههای دوبعدی بهصورت ماتریس و آرایههای سهبعدی بهصورت فهرستی از ماتریسها پرینت میشوند.
>>> a = arange(6) # 1d آرایه
>>> print a
[0 1 2 3 4 5]
>>>
>>> b = arange(12).reshape(4,3) # 2d آرایه
>>> print b
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
>>>
>>> c = arange(24).reshape(2,3,4) # 3d آرایه
>>> print c
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]]
اگر یک آرایه برای پرینت گرفتن بسیار بزرگ باشد، NumPy به طور خودکار بخش مرکزی آرایه را قطع میکند و تنها ابتدا و انتهای آن را نمایش میدهد.
>>> print arange(10000)
[ 0 1 2 ..., 9997 9998 9999]
>>>
>>> print arange(10000).reshape(100,100)
[[ 0 1 2 ..., 97 98 99]
[ 100 101 102 ..., 197 198 199]
[ 200 201 202 ..., 297 298 299]
...,
[9700 9701 9702 ..., 9797 9798 9799]
[9800 9801 9802 ..., 9897 9898 9899]
[9900 9901 9902 ..., 9997 9998 9999]
برای اینکه این حالت را غیرفعال کنیم و NumPy کل آرایه را پرینت بگیرد، میتوانیم با استفاده از گزینه set_printoptions رفتار آن را تغییر دهیم.
>>> set_printoptions(threshold='nan')
عملیاتهای پایه
عملیاتهای حسابی بر روی آرایهها در سطح عناصر انجام مییابند. درنتیجه اجرای عملیات حسابی یک آرایه جدید ایجاد و مقادیر آن پر میشود.
>>> a = array( [20,30,40,50] )
>>> b = arange( 4 )
>>> b
array([0, 1, 2, 3])
>>> c = a-b
>>> c
array([20, 29, 38, 47])
>>> b**2
array([0, 1, 4, 9])
>>> 10*sin(a)
array([ 9.12945251, -9.88031624, 7.4511316 , -2.62374854])
>>> a<35
array([True, True, False, False], dtype=bool)
برخلاف بسیاری از زبانهای ماتریسی عملگر * در آرایههای NumPy بهصورت عنصر به عنصر، عمل ضرب را انجام میدهد. ضرب ماتریسی را میتوان با استفاده از تابع dot یا ایجاد اشیای matrix انجام داد.
>>> A = array( [[1,1],
... [0,1]] )
>>> B = array( [[2,0],
... [3,4]] )
>>> A*B # ضرب در سطح عناصر
array([[2, 0],
[0, 4]])
>>> dot(A,B) # ضرب در سطح ماتریس
array([[5, 4],
[3, 4]])
برخی عملیاتها مانند += و *= بهجای ایجاد یک آرایه جدید بر روی همان ماتریس موجود عمل میکنند.
>>> a = ones((2,3), dtype=int)
>>> b = random.random((2,3))
>>> a *= 3
>>> a
array([[3, 3, 3],
[3, 3, 3]])
>>> b += a
>>> b
array([[ 3.69092703, 3.8324276 , 3.0114541 ],
[ 3.18679111, 3.3039349 , 3.37600289]])
>>> a += b # به مقدار صحیح تبدیل میشود b
>>> a
array([[6, 6, 6],
[6, 6, 6]])
زمانی که بر روی آرایههایی با انواع مختلف، عملیاتی انجام میگیرد، نوع آرایه حاصل متناظر با نوع آرایه عمومیتر یا دقیقتر خواهد بود (این حالت به نام upcasting نامیده میشود).
>>> a = ones(3, dtype=int32)
>>> b = linspace(0,pi,3)
>>> b.dtype.name
'float64'
>>> c = a+b
>>> c
array([ 1. , 2.57079633, 4.14159265])
>>> c.dtype.name
'float64'
>>> d = exp(c*1j)
>>> d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
-0.54030231-0.84147098j])
>>> d.dtype.name
'complex128'
بسیاری از عملیاتهای تک آرایهای مانند جمع زدن همه عناصر یک آرایه بهصورت متدهایی در کلاس ndarray اجرا میشوند.
>>> a = random.random((2,3))
>>> a
array([[ 0.6903007 , 0.39168346, 0.16524769],
[ 0.48819875, 0.77188505, 0.94792155]])
>>> a.sum()
3.4552372100521485
>>> a.min()
0.16524768654743593
>>> a.max()
0.9479215542670073
این عملیاتها به طور پیشفرض طوری بر روی آرایهها اجرا میشوند که صرفنظر از شکلشان، گویی آرایهها فهرستی از اعداد هستند. بااینحال با تعیین پارامتر axis میتوان یک عملیات را در راستای یک محور تعیین شده در یک آرایه اجرا کرد:
>>> b = arange(12).reshape(3,4)
>>> b
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>>
>>> b.sum(axis=0) # sum of each column
array([12, 15, 18, 21])
>>>
>>> b.min(axis=1) # min of each row
array([0, 4, 8])
>>>
>>> b.cumsum(axis=1) # cumulative sum along each row
array([[ 0, 1, 3, 6],
[ 4, 9, 15, 22],
[ 8, 17, 27, 38]])
تابعهای سراسری
NumPy تابعهای ریاضیاتی متداولی مانند sin، cos و exp را در خود دارد. در NumPy این توابع به نام «تابعهای سراسری» (ufanc) نامیده میشوند. درون NumPy این تابعها در سطح عناصر یک آرایه اجرا میشوند و درنتیجه یک آرایه جدید ایجاد میکنند.
>>> B = arange(3)
>>> B
array([0, 1, 2])
>>> exp(B)
array([ 1. , 2.71828183, 7.3890561 ])
>>> sqrt(B)
array([ 0. , 1. , 1.41421356])
>>> C = array([2., -1., 4.])
>>> add(B, C)
array([ 2., 0., 6.])
اندیسگذاری، قطعهبندی و تکرار
آرایههای تکبعدی را میتوان همانند فهرستها و دیگر دنبالههای پایتون، اندیسگذاری کرد، قطعهبندی نمود و عملیاتی را بر روی آنها تکرار کرد.
>>> a = arange(10)**3
>>> a
array([ 0, 1, 8, 27, 64, 125, 216, 343, 512, 729])
>>> a[2]
8
>>> a[2:5]
array([ 8, 27, 64])
>>> a[:6:2] = -1000 # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -1000
>>> a
array([-1000, 1, -1000, 27, -1000, 125, 216, 343, 512, 729])
>>> a[::-1] # reversed a
array([ 729, 512, 343, 216, 125, -1000, 27, -1000, 1, -1000])
>>> for i in a:
... print i**(1/3.),
...
nan 1.0 nan 3.0 nan 5.0 6.0 7.0 8.0 9.0
آرایههای چندبعدی میتوانند برای هر محور خود یک اندیس داشته باشند. این اندیسها در یک چندتایی که با کاما از هم جدا میشود ارائه میشوند:
>>> def f(x,y):
... return 10*x+y
...
>>> b = fromfunction(f,(5,4),dtype=int)
>>> b
array([[ 0, 1, 2, 3],
[10, 11, 12, 13],
[20, 21, 22, 23],
[30, 31, 32, 33],
[40, 41, 42, 43]])
>>> b[2,3]
23
>>> b[0:5, 1] # bهر ردیف در ستون دوم
array([ 1, 11, 21, 31, 41])
>>> b[: ,1] # معادل مثال قبلی
array([ 1, 11, 21, 31, 41])
>>> b[1:3,: ] # b هر ستون در ردیف دوم و سوم
array([[10, 11, 12, 13],
[20, 21, 22, 23]])
زمانی که تعداد اندیسها کمتر از تعداد محورها باشد، اندیسهای ناموجود بهصورت قطعههای کامل در نظر گرفته میشوند:
>>> b[-1] # b[-1,:]ردیف آخر و معادل
array([40, 41, 42, 43])
در مورد آرایه [b[i عبارت داخل براکت را میتوان بدینصورت نوشت که ابتدا یک «i» قرار داد و سپس به تعداد محورهای باقیمانده «:» قرار داد. NumPy امکان استفاده از نقطه را نیز دارد یعنی […,b[i.
نقطهها (…) بدین معنی است که به NumPy میگوییم هر مقدار دونقطه (:) دیگر که نیاز است بگذار تا یک چندتایی کامل برای اندیسها ایجاد شود.
>>> c = array( [ [[ 0, 1, 2], # یک آرایه 3 بعدی (دو آرایه 2بعدی پشته شده)
... [ 10, 12, 13]],
...
... [[100,101,102],
... [110,112,113]] ] )
>>> c.shape
(2, 2, 3)
>>> c[1,...] # c[1,:,:] یا c[1]همانند
array([[100, 101, 102],
[110, 112, 113]])
>>> c[...,2] # c[:,:,2] همانند
array([[ 2, 13],
[102, 113]])
تکرار عملیات بر روی آرایههای چندبعدی با توجه به محور نخست انجام مییابد.
>>> for row in b:
... print row
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]
بااینحال، اگر کسی بخواهد یک عملیات را بر روی همه عناصر یک آرایه اجرا کند، میتواند از خصوصیت flat استفاده کند که باعث میشود عملیات بر روی همه عناصر آرایه تکرار شود:
>>> for element in b.flat:
... print element,
...
0 1 2 3 10 11 12 13 20 21 22 23 30 31 32 33 40 41 42 43
دستکاری شکل
چگونه میتوانیم شکل یک آرایه را تغییر دهیم؟ هر آرایهای شکلی دارد که بر اساس تعداد عناصر هر محور تعیین میشود:
>>> a = floor(10*random.random((3,4)))
>>> a
array([[ 7., 5., 9., 3.],
[ 7., 2., 7., 8.],
[ 6., 8., 3., 2.]])
>>> a.shape
(3, 4)
شکل یک آرایه را میتوان بهوسیله فرمانهای مختلف تغییر داد:
>>> a.ravel() # مسطح سازی آرایه
array([ 7., 5., 9., 3., 7., 2., 7., 8., 6., 8., 3., 2.])
>>> a.shape = (6, 2)
>>> a.transpose()
array([[ 7., 9., 7., 7., 6., 3.],
[ 5., 3., 2., 8., 8., 2.]])
ترتیب عناصر در آرایه حاصل از ()ravel بهطورمعمول به «سبک C» هستند یعنی اندیس منتهیالیه سمت راست «سریعتر از بقیه تغییر مییابد» بنابراین عنصر بعد از [a[0,0، عنصر [a[0,1 است. اگر شکل آرایه تغییر یابد، در حالت جدید هم بهصورت «سبک C» با آن برخورد میشود. NumPy بهطورمعمول آرایههایی ایجاد میکند که به این ترتیب ذخیره میشوند. بنابراین برای کپی کردن آرگومانهای آن نیازی به ()ravel نیست، اما اگر آرایه با استفاده از تکههایی از آرایههای دیگر یا با استفاده از گزینههای نامعمول ایجاد شده باشد، ممکن است نیاز باشد که ()ravel نیز کپی شود. تابعهای ()ravel و ()reshape را نیز میتوان با استفاده از آرگومانهای اختیاری تغییر داد و مثلاً از آرایههایی به سبک فرترن (FORTRAN) استفاده کرد که در آنها اندیس منتهیالیه سمت چپ قبل از همه تغییر مییابد.
تابع reshape آرگومانش را با شکل تغییر یافتهای برمیگرداند، در حالی که تابع resize خود آرایه را تغییر میدهد:
>>> a
array([[ 7., 5.],
[ 9., 3.],
[ 7., 2.],
[ 7., 8.],
[ 6., 8.],
[ 3., 2.]])
>>> a.resize((2,6))
>>> a
array([[ 7., 5., 9., 3., 7., 2.],
[ 7., 8., 6., 8., 3., 2.]])
اگر یک بعد در طی عملیات reshape بهصورت 1- تعیین شده باشد در این صورت ابعاد دیگر به طور خودکار محاسبه میشوند:
>>> a.reshape(3,-1)
array([[ 7., 5., 9., 3.],
[ 7., 2., 7., 8.],
[ 6., 8., 3., 2.]])
پشتهسازی (Stacking) با آرایههای مختلف
میتوان از چند آرایه برای پشتهسازی در راستای محورهای مختلف استفاده کرد:
>>> a = floor(10*random.random((2,2)))
>>> a
array([[ 1., 1.],
[ 5., 8.]])
>>> b = floor(10*random.random((2,2)))
>>> b
array([[ 3., 3.],
[ 6., 0.]])
>>> vstack((a,b))
array([[ 1., 1.],
[ 5., 8.],
[ 3., 3.],
[ 6., 0.]])
>>> hstack((a,b))
array([[ 1., 1., 3., 3.],
[ 5., 8., 6., 0.]])
تابع column_stack آرایههای یکبعدی را بهصورت ستونهایی در یک آرایه دوبعدی پشته میکند. این تابع معادل تابع vstack است که تنها در مورد آرایههای یکبعدی عمل میکند:
>>> column_stack((a,b)) # با آرایههای 2 بعدی
array([[ 1., 1., 3., 3.],
[ 5., 8., 6., 0.]])
>>> a=array([4.,2.])
>>> b=array([2.,8.])
>>> a[:,newaxis] # بدین ترتیب میتوان برداری با ستونهای 2 بعدی داشت
array([[ 4.],
[ 2.]])
>>> column_stack((a[:,newaxis],b[:,newaxis]))
array([[ 4., 2.],
[ 2., 8.]])
>>> vstack((a[:,newaxis],b[:,newaxis])) # متفاوت است vstack رفتار
array([[ 4.],
[ 2.],
[ 2.],
[ 8.]])
از سوی دیگر، تابع row_stack آرایههای یکبعدی را بهصورت ردیفهایی در آرایههای دوبعدی، پشته میکند. برای آرایههایی با ابعادی بیش از 2، تابع hstack در راستای محور دوم و vstack در راستای محور اول پشتهسازی میکند و تابع concatenate امکان تعیین تعداد محورهایی که پشتهسازی در راستای آنها انجام میگیرد را مهیا کرده است. در موارد پیچیدهتر تابعهای []_r و []_c برای ایجاد آرایههایی با پشتهسازی در راستای یک محور، مناسب هستند. این توابع امکان استفاده از عبارتهای محدودهای (“:”) را فراهم کردهاند:
>>> r_[1:4,0,4]
array([1, 2, 3, 0, 4])
[]_r و []_ c وقتی بر روی آرایهها به عنوان آرگومان استفاده میشوند، مشابه توابع vstack و hstack با رفتار پیشفرض عمل میکنند، اما این مزیت اضافی را دارند که امکان تعریف کردن تعداد محورهایی که پشتهسازی در راستای آنها انجام میشود را دارند.
افراز کردن یک آرایه به چند آرایه کوچکتر
با استفاده از تابع hsplit میتوان یک آرایه را در راستای محورهای افقیاش افراز کرد. این کار یا با تعیین تعداد آرایههای همشکلی که باید بازگردانده شوند، انجام میگیرد یا با تعیین ستونهایی که پس از آنها باید عمل تقسیم کردن انجام گیرد.
>>> a = floor(10*random.random((2,12)))
>>> a
array([[ 8., 8., 3., 9., 0., 4., 3., 0., 0., 6., 4., 4.],
[ 0., 3., 2., 9., 6., 0., 4., 5., 7., 5., 1., 4.]])
>>> hsplit(a,3) # به 3 بخش افراز میکند
[array([[ 8., 8., 3., 9.],
[ 0., 3., 2., 9.]]), array([[ 0., 4., 3., 0.],
[ 6., 0., 4., 5.]]), array([[ 0., 6., 4., 4.],
[ 7., 5., 1., 4.]])]
>>> hsplit(a,(3,4)) # آن را پس از ستونهای سوم و چهارم افراز میکند
[array([[ 8., 8., 3.],
[ 0., 3., 2.]]), array([[ 9.],
[ 9.]]), array([[ 0., 4., 3., 0., 0., 6., 4., 4.],
[ 6., 0., 4., 5., 7., 5., 1., 4.]])]
تابع Vsplit آرایه را در راستای محورهای عمودی افراز میکند و تابع array split امکان تعیین دقیق محورهایی که باید افراز شوند را فراهم کرده است.
کپیها و نمایشها
زمانی که بر روی آرایهها عملیاتی انجام میدهیم یا آنها را دستکاری میکنیم، در برخی موارد دادههای آنها به یک آرایه جدید کپی میشود و در برخی موارد نیز چنین نمیشود. این مسئله موجب سردرگمی افراد مبتدی میشود. بهطورکلی سه حالت وجود دارد:
هیچ کپی انجام نمیشود
در انتسابهای ساده هیچ کپی از اشیای آرایه یا دیگر دادهها ایجاد نمیشود.
>>> a = arange(12)
>>> b = a # هیچ شی جدیدی ایجاد نمیشود
>>> b is a # هستند ndarray هر دو همان شی
True
>>> b.shape = 3,4 # شکل آن تغییر مییابد
>>> a.shape
(3, 4)
پایتون اشیای تغییرپذیر را بهصورت مرجع ارسال میکند بنابراین فراخوانهای تابع هیچ کپی جدیدی ایجاد نمیکنند.
>>> def f(x):
... print id(x)
...
>>> id(a) # شناساگر منحصربهفرد شیء استid
148293216
>>> f(a)
148293216
نمایش (view) یا کپی سطحی
اشیای آرایهای مختلف میتوانند دادههای مشترکی داشته باشند. متد view یک شی آرایهای جدید ایجاد میکند که از دادههای مشابهی استفاده میکند.
>>> c = a.view()
>>> c is a
False
>>> c.base is a # استa نمایشی از دادههایی است که متعلق به c
True
>>> c.flags.owndata
False
>>>
>>> c.shape = 2,6 # تغییر نمیکند a شکل
>>> a.shape
(3, 4)
>>> c[0,4] = 1234 # تغییر میکند a دادههای
>>> a
array([[ 0, 1, 2, 3],
[1234, 5, 6, 7],
[ 8, 9, 10, 11]])
قطعهبندی کردن یک آرایه یک نمایش (view) از آن بازمیگرداند:
>>> s = a[: , 1:3] # "s = a[:,1:3]"برای خوانایی بیشتر فاصله اضافه شده است. همچنین میتوان نوشت:
>>> s[:] = 10 # s[:] is a view of s. Note the difference between s=10 and s[:]=10
>>> a
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])
کپی عمیق
متد copy یک کپی کامل از آرایه و دادههای آن ایجاد میکند.
>>> d = a.copy() # یک شیء دادهای جدید با دادههای جدید ایجاد شده است
>>> d is a
False
>>> d.base is a # هیچ چیز مشترکی ندارند. d و a
False
>>> d[0,0] = 9999
>>> a
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])
قواعد انتشار (Broadcasting)
قواعد انتشار به تابعهای سراسری اجازه میدهد که به یک روش منطقی با ورودیها کار کنند و لازم نیست که شکل یکسانی داشته باشند. قاعده نخست انتشار این است که اگر همه ورودیهای آرایه ابعاد یکسانی نداشته باشند، یک «1» مکرراً به شکلهای آرایهای کوچکتر اضافه میشود تا جایی که آرایه مذکور به تعداد ابعاد مشابه برسد. قاعده دوم انتشار تضمین میکند که آرایههایی که در یک بعد خاص، ابعادی به طول 1 دارند طوری عمل میکنند که گویی در آن بعد، اندازهای بزرگتر دارند. مقدار عنصر آرایه در آن بعد، برابر با مقدار عنصر در راستای همان بعد در آرایه «انتشار» در نظر گرفته میشود.
اندیسگذاری زیبا و ترفندهای اندیسگذاری
NumPy نسبت به دنبالههای معمولی پایتون امکانات اندیسگذاری بیشتری دارد. علاوه بر امکان اندیسگذاری بر اساس اعداد صحیح و قطعهها، که در بخشهای قبلی دیدیم، آرایهها میتوانند بهوسیلهی آرایهای از اعداد صحیح و آرایهای از مقادیر بولی اندیسگذاری شوند.
اندیسگذاری با آرایهها و اندیسها
>>> a = arange(12)**2 # نخستین 12 عدد مربع
>>> i = array( [ 1,1,3,8,5 ] ) # آرایهای از اندیسها
>>> a[i] # i در موقعیتهای a عناصر
array([ 1, 1, 9, 64, 25])
>>>
>>> j = array( [ [ 3, 4], [ 9, 7 ] ] ) # یک آرایه دوبعدی از اندیسها
>>> a[j] # jهمان شکل
array([[ 9, 16],
[81, 49]])
زمانی که آرایه اندیسگذاری شده a چندبعدی باشد، منظور از یک آرایه منفرد از اندیسها، در واقع بعد نخست a است. مثال زیر با تبدیل کردن یک تصویر از برچسبها به یک تصویر رنگی با استفاده از یک پالت این رفتار را نشان میدهد.
>>> palette = array( [ [0,0,0], # مشکی
... [255,0,0], # قرمز
... [0,255,0], # سبز
... [0,0,255], # آبی
... [255,255,255] ] ) # سفید
>>> image = array( [ [ 0, 1, 2, 0 ], # هر مقدار متناظر با یک رنگ در پالت است
... [ 0, 3, 4, 0 ] ] )
>>> palette[image] # (2,4,3) تصویر رنگی
array([[[ 0, 0, 0],
[255, 0, 0],
[ 0, 255, 0],
[ 0, 0, 0]],
[[ 0, 0, 0],
[ 0, 0, 255],
[255, 255, 255],
[ 0, 0, 0]]])
همچنین میتوان برای بیش از یک بعد اندیسهایی ارائه کرد. آرایهی اندیسهای هر بعد باید شکل یکسانی داشته باشند.
>>> a = arange(12).reshape(3,4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> i = array( [ [0,1], # aاندیسهای بعد نخست
... [1,2] ] )
>>> j = array( [ [2,1], # اندیسهای بعد دوم
... [3,3] ] )
>>>
>>> a[i,j] # باید ابعاد یکسانی داشته باشندj و i
array([[ 2, 5],
[ 7, 11]])
>>>
>>> a[i,2]
array([[ 2, 6],
[ 6, 10]])
>>>
>>> a[:,j] # i.e., a[: , j]
array([[[ 2, 1],
[ 3, 3]],
[[ 6, 5],
[ 7, 7]],
[[10, 9],
[11, 11]]])
به طور طبیعی میتوان i و j را در یک دنباله (مثلاً یک فهرست) قرار داد و سپس با استفاده از فهرست اندیسگذاری کرد.
>>> l = [i,j]
>>> a[l] # a[i,j]معادل
array([[ 2, 5],
[ 7, 11]])
ولی این کار را با قرار دادن i و j در یک آرایه نمیتوان انجام داد، زیرا آرایه به عنوان اندیسگذاری بعد نخست a تفسیر میشود.
>>> s = array( [i,j] )
>>> a[s] # همان چیزی که میخواستیم
Traceback (most recent call last):
File "<stdin>", line 1, in ?
IndexError: index (3) out of range (0<=index<=2) in dimension 0
>>>
>>> a[tuple(s)] # a[i,j]همانند
array([[ 2, 5],
[ 7, 11]])
استفاده رایج دیگر از اندیسگذاری آرایهها، جستجوی مقدار بیشینه سریهای وابسته به زمان است:
>>> time = linspace(20, 145, 5) # مقیاس زمانی
>>> data = sin(arange(20)).reshape(5,4) # 4 سری وابسته به زمان
>>> time
array([ 20. , 51.25, 82.5 , 113.75, 145. ])
>>> data
array([[ 0. , 0.84147098, 0.90929743, 0.14112001],
[-0.7568025 , -0.95892427, -0.2794155 , 0.6569866 ],
[ 0.98935825, 0.41211849, -0.54402111, -0.99999021],
[-0.53657292, 0.42016704, 0.99060736, 0.65028784],
[-0.28790332, -0.96139749, -0.75098725, 0.14987721]])
>>>
>>> ind = data.argmax(axis=0) # اندیس ماتریس برای هر سری
>>> ind
array([2, 0, 3, 1])
>>>
>>> time_max = time[ ind] # maxima زمان متناظر با هر
>>>
>>> data_max = data[ind, xrange(data.shape[1])] # => data[ind[0],0], data[ind[1],1]...
>>>
>>> time_max
array([ 82.5 , 20. , 113.75, 51.25])
>>> data_max
array([ 0.98935825, 0.84147098, 0.99060736, 0.6569866 ])
>>>
>>> all(data_max == data.max(axis=0))
True
همچنین میتوان از اندیسگذاری آرایهها به عنوان هدفی برای انتساب مقادیر به آنها استفاده کرد:
>>> a = arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> a[[1,3,4]] = 0
>>> a
array([0, 0, 2, 0, 0])
بااینحال وقتی یک فهرست از اندیسها شامل موارد تکراری باشد، این انتساب چند بار رخ میدهد و بخشی از مقادیر حذف میشوند:
>>> a = arange(5)
>>> a[[0,0,2]]=[1,2,3]
>>> a
array([2, 1, 3, 3, 4])
منطق این کار مشخص است اما ابتدا بررسی کنید که آیا میخواهید از سازه =+ پایتون استفاده کنید یا نه، چون ممکن است آن چیزی نباشد که انتظار دارید:
>>> a = arange(5)
>>> a[[0,0,2]]+=1
>>> a
array([1, 1, 3, 3, 4])
با اینکه مقدار 0 دو بار در فهرست اندیسها ظاهر شده است اما عنصر 0-ام تنها یک عنصر افزایش یافته است. این حالت به این دلیل رخ داده است که پایتون الزام میکند «a+=1» معادل «a=a+1» محاسبه شود.
اندیسگذاری با استفاده از آرایههای بولی
وقتی آرایهها را با استفاده از آرایههایی از اندیسها (اعداد صحیح) اندیسگذاری میکنیم در واقع فهرستی از اندیسها برای انتخاب کردن ارائه میدهیم. در مورد اندیسهای بولی، رویکرد اندکی متفاوت است؛ در این روش ما صریحاً انتخاب میکنیم که در یک آرایه کدام اندیسها را میخواهیم و کدام را نمیخواهیم. طبیعیترین روشی که ممکن است برای استفاده از اندیسگذاری بولی در ذهن داشته باشیم، داشتن آرایههای بولی است که همانند آرایه اصلی شکل یکسانی داشته باشند:
>>> a = arange(12).reshape(3,4)
>>> b = a > 4
>>> b #است a بولی و با شکل b
array([[False, False, False, False],
[False, True, True, True],
[True, True, True, True]], dtype=bool)
>>> a[b] # آرایه 1 بعدی با عناصر منتخب
array([ 5, 6, 7, 8, 9, 10, 11])
این خصوصیت میتواند در زمان انتساب مقادیر بسیار مفید باشد:
>>> a[b] = 0 # که بالاتر از 4 هستند به 0 تبدیل میشوند'a' همه عناصر
>>> a
array([[0, 1, 2, 3],
[4, 0, 0, 0],
[0, 0, 0, 0]])
این روش دوم اندیسگذاری با عبارتهای بولی به روش اندیسگذاری با اعداد صحیح شباهت بیشتری دارد، چون برای هر بعد آرایه، یک آرایه بولی 1 بعدی اختصاص میدهیم و تکههایی که میخواهیم را از میان آنها انتخاب میکنیم.
>>> a = arange(12).reshape(3,4)
>>> b1 = array([False,True,True]) # انتخاب بعد نخست
>>> b2 = array([True,False,True,False]) # انتخاب بعد دوم
>>>
>>> a[b1,:] # انتخاب ردیفها
array([[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>>
>>> a[b1] # همان
array([[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>>
>>> a[:,b2] # انتخاب ستونها
array([[ 0, 2],
[ 4, 6],
[ 8, 10]])
>>>
>>> a[b1,b2] # یک کار عجیب!
array([ 4, 10])
توجه کنید که طول آرایه بولی تکبعدی باید با طول ابعادی که میخواهیم قطعهبندی کنیم هماهنگ باشد.
تابع ()_ix
تابع ()_ix را میتوان برای ترکیب بردارهای مختلف استفاده کرد به طوری که برای هر n-تایی یک آرایه به دست آورد. برای مثال اگر بخواهیم همه مقادیر a+b*c را برای همه چندتاییهای به دست آمده از هر بردار a، b و c به دست آوریم، میتوانیم از تابع ()_ix استفاده کنیم.
>>> a = array([2,3,4,5])
>>> b = array([8,5,4])
>>> c = array([5,4,6,8,3])
>>> ax,bx,cx = ix_(a,b,c)
>>> ax
array([[[2]],
[[3]],
[[4]],
[[5]]])
>>> bx
array([[[8],
[5],
[4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> result = ax+bx*cx
>>> result
array([[[42, 34, 50, 66, 26],
[27, 22, 32, 42, 17],
[22, 18, 26, 34, 14]],
[[43, 35, 51, 67, 27],
[28, 23, 33, 43, 18],
[23, 19, 27, 35, 15]],
[[44, 36, 52, 68, 28],
[29, 24, 34, 44, 19],
[24, 20, 28, 36, 16]],
[[45, 37, 53, 69, 29],
[30, 25, 35, 45, 20],
[25, 21, 29, 37, 17]]])
>>> result[3,2,4]
17
>>> a[3]+b[2]*c[4]
17
همچنین میتوان از تابع reduce استفاده کرد:
def ufunc_reduce(ufct, *vectors):
vs = ix_(*vectors)
r = ufct.identity
for v in vs:
r = ufct(r,v)
return r
و سپس از آن بهصورت زیر استفاده کرد:
>>> ufunc_reduce(add,a,b,c)
array([[[15, 14, 16, 18, 13],
[12, 11, 13, 15, 10],
[11, 10, 12, 14, 9]],
[[16, 15, 17, 19, 14],
[13, 12, 14, 16, 11],
[12, 11, 13, 15, 10]],
[[17, 16, 18, 20, 15],
[14, 13, 15, 17, 12],
[13, 12, 14, 16, 11]],
[[18, 17, 19, 21, 16],
[15, 14, 16, 18, 13],
[14, 13, 15, 17, 12]]])
مزیت این نسخه از تابع reduce در مقایسه با نسخه معمولی ufanc.reduce این است که در آن از قواعد انتشار استفاده شده است تا از ایجاد یک آرایهی آرگومان در خروجی که اندازهای چند برابر بردارها دارد، اجتناب شود.
جبر خطی
عملیاتهای ساده بر روی آرایه
در ادامه برخی مثالها برای آشنایی با عملیاتهای ساده جبر خطی بر روی آرایهها ارائه شدهاند که برای کاربری در سطح متوسط نیاز به توضیح چندانی ندارند:
>>> from numpy import *
>>> from numpy.linalg import *
>>> a = array([[1.0, 2.0], [3.0, 4.0]])
>>> print a
[[ 1. 2.]
[ 3. 4.]]
>>> a.transpose()
array([[ 1., 3.],
[ 2., 4.]])
>>> inv(a)
array([[-2. , 1. ],
[ 1.5, -0.5]])
>>> u = eye(2) # واحد 2x2 ماتریس; "I"معادل "eye"
>>> u
array([[ 1., 0.],
[ 0., 1.]])
>>> j = array([[0.0, -1.0], [1.0, 0.0]])
>>> dot (j, j) # ضرب ماتریس
array([[-1., 0.],
[ 0., -1.]])
>>> trace(u) # trace
2.0
>>> y = array([[5.], [7.]])
>>> solve(a, y)
array([[-3.],
[ 4.]])
>>> eig(j)
(array([ 0.+1.j, 0.-1.j]),
array([[ 0.70710678+0.j, 0.70710678+0.j],
[ 0.00000000-0.70710678j, 0.00000000+0.70710678j]]))
Parameters:
square matrix
Returns
The eigenvalues, each repeated according to its multiplicity.
The normalized (unit "length") eigenvectors, such that the
column ``v[:,i]`` is the eigenvector corresponding to the
eigenvalue ``w[i]`` .
کلاس ماتریس
در ادامه مقدمه مختصری در مورد کلاس ماتریس ارائه شده است.
>>> A = matrix('1.0 2.0; 3.0 4.0')
>>> A
[[ 1. 2.]
[ 3. 4.]]
>>> type(A) # تعیین محل ذخیرهسازی ماتریس
<class 'numpy.matrixlib.defmatrix.matrix'>
>>> A.T # transpose
[[ 1. 3.]
[ 2. 4.]]
>>> X = matrix('5.0 7.0')
>>> Y = X.T
>>> Y
[[5.]
[7.]]
>>> print A*Y # ضرب ماتریس
[[19.]
[43.]]
>>> print A.I # معکوس سازی
[[-2. 1. ]
[ 1.5 -0.5]]
>>> solve(A, Y) #حل معادله جبری
matrix([[-3.],
[ 4.]])
اندیسگذاری: مقایسه ماتریسها و آرایههای دوبعدی
توجه کنید که برخی تفاوتهای مهم بین آرایههای NumPy و ماتریسها وجود دارند. NumPy دو شی بنیادی را ارائه میکند: یک شی آرایه N بعدی و یک شی تابع سراسری. در NumPy اشیای دیگر بر روی این دو شی بنیادی ساخته میشوند. به طور خاص ماتریسها اشیای آرایهای دوبعدی هستند که از شیء آرایهای NumPy به ارث رسیدهاند. در هر دو شی آرایه و ماتریس، اندیسها باید ترکیبی صحیح از یک یا چند مورد زیر باشند:
- اسکالرهای صحیح
- سهنقطه (…)
- فهرستی از مقادیر صحیح یا بولی
- یک چندتایی از مقادیر صحیح یا بولی
- یک آرایه 1 بعدی از مقادیر صحیح یا بولی
از یک ماتریس میتوان به عنوان اندیسی برای ماتریسها استفاده کرد، اما معمولاً آن را برای انجام یک کار مفروض بر روی یک آرایه، فهرست یا دیگر اشکال، نیاز خواهیم داشت. آرایهها در NumPy نیز همانند پایتون از صفر شروع میشوند. به طور سنتی یک آرایه 2 بعدی یا ماتریس بهصورت یک آرایه مستطیلی از ردیفها و ستونها نمایش مییابد که حرکت در راستای محور 0 در واقع حرکت در راستای ردیفها و حرکت در راستای محور 1، حرکت در راستای ستونها محسوب میشود.
در ادامه یک آرایه و یک ماتریس ایجاد میکنیم و آن را قطعهبندی میکنیم:
>>> A = arange(12)
>>> A
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
>>> A.shape = (3,4)
>>> M = mat(A.copy())
>>> print type(A)," ",type(M)
<type 'numpy.ndarray'> <class 'numpy.core.defmatrix.matrix'>
>>> print A
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
>>> print M
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
اینک در مورد برخی قطعهبندیهای ساده صحبت میکنیم. قطعهبندی ساده به قطعهبندی اشیا یا اعداد صحیح گفته میشود. برای مثال، ارزیابی [:]A و M[:] نمونههای آشنایی از قطعهبندی اندیسگذاری در پایتون هستند ولی باید به این نکته مهم توجه داشته باشیم که قطعهبندی در NumPy یک کپی از دادهها ایجاد نمیکند. قطعهبندی یک نمایش جدید از همان دادهها ایجاد میکند.
>>> print A[:]; print A[:].shape
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
(3, 4)
>>> print M[:]; print M[:].shape
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
(3, 4)
اینک برای اینکه متوجه شویم اندیسگذاری در NumPy متفاوت از پایتون عمل میکند، میتوانیم از اندیسهای جداشده با کاما برای اندیسگذاری همزمان در راستای چند محور آرایه استفاده کنیم.