NumPy kutubxonasi
Kirish
Har bir sohada bo’lgani kabi, biz ushbu sohada ham imkon qadar mavjud narsalardan unimli foydalanishga harakat qilishimiz zarur bo’ladi. Chunki ular yillar davomida sinovdan o’tgan hamda ma’qullangan bo’ladi. Shuning uchun ham ushbu bo’limda biz SI uchun eng zarur bo’lgan kutubxona – NumPyni qisman o’rganib chiqamiz.
NumPy kutbxonasi Python dasturlash tilida bir turdagi n o’lchamli tensorlarni saqlash uchun hamda ular ustida algebraik amallarni bajarish uchun zarur hisoblanadi. U 2005 yilda birinchi bo’lib “Numeric” va “Numarray” kutubxonalarining davomchisi sifatida ishlab chiqila boshladi. Ushbu kutubxona Python dasturlash tilida hamda to’lig’icha ochiq kodli bo’lib, u sonli hisoblar uchun mo’ljallangan.
Biz ushbu kutubxonani Python dasturlash tilida o’rgangan list
hamda tuple
turlarining o’rniga ishlatamiz. Bu esa bizga o’z navbatida bir qancha ustunliklarni taqdim etadi:
Tezlik. C tilida maxsus kuchaytirgichlar bilan yozilganligi uchun u nafaqat Pythonda yozilgan sodda kodlardan balki C/C++ tilida yozilgan kodlardan ham ancha samarali ishlaydi.
Funksiyalari. Chiziqli algebra hamda matritsalar ustida mavjud bo’lgan amallarning ko’pchiligi mavjudligi.
Asosiy kutubxona. Bir qancha boshqa kutubxonalar uchun asos tur sifatida xizmat qiladi.
Qolipi. Ushbu kutubxonaning yondashuvi asosida hozirgi zamonaviy chuqur o’rganish kutubxonalari yozilgan. Ya’ni bu kutubxonani o’rganish bilan PyTorch hamda Tensorflow kutubxonalarining tensorlari bilan ishlashni ham o’rganish mumkin.
Soddalik, qisqalik va tushanarlilik. Ushbu kutubxonadan foydalanish juda sodda bo’lib, biz bir qancha qatorlarda yozadigan kodni bir necha qatorda tushanarli qilib yozish imkoniyati mavjud.
Biz ushbu bo’limda Numpyning asoslarini o’rganib chiqamiz, keyinchalik esa ushbu sinfning boshqa yuqori imkoniyatlari bilan yana tanishib chiqamiz.
Numpy o’rnatish va qo’shish
Ushbu kutubxonani biz pip install numpy
buyrug’i orqali osongina o’rnatib olishimiz mumkin. Asosiy kodga qo’shishda esa uni np
deb qisqartirgan holda amalga oshiramiz:
[2]:
import numpy as np
Obyekt yaratish
Obyektlarni bir qancha usullar bilan yaratish mumkin, ular ichida biror boshqa turdagi o’zgaruvchidan ishbu turga o’tkazish ham fayldan o’qish ham mavjud. Keling, avvalo oddiy Python list
obyektini ushbu turga o’zkazib ushbu kutubxonaning asoslarini o’rganaylik, keyin esa fayldan o’qishni ko’rib chiqamiz.
Buning uchun biz kutobxonaning array()
funksiyasidan chaqirishib, uning birinchi argumenti sifatida list
obyektini berishimiz zarur:
[2]:
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(a)
[ 1 2 3 4 5 6 7 8 9 10]
Yuqorida biz list
obyektini NumPyning ndarray
turiga o’tkazdik. Endi biz ushbu ndarray
ning a
obyekting xususiyatlari bilan tanishib chiqamiz. a.shape
xususiyati a
da nechta o’lcham bor hamda har bir o’lchamda qanchadan element borligini tuple
obyekti sifatida qaytaradi:
[3]:
print(a.shape)
(10,)
Demak a
1 o’lchamli tensor(vektor) hisoblanib, unda 10 ta element bor. Keling endi ikki o’lchamli tensor(matritsa)ni ham bir qaraylik:
[49]:
A = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11, 12]
])
print(A.shape)
(4, 3)
Yodda tuting. Biz faqat to’g’iri to’rtburchak shakldagi tensorlarni yarata olamiz, ya’ni har bir o’qda bir xil sonda elementlar bo’lishi kerak. Masalan, quyidagicha obyekt yarata olmaymiz:
[50]:
A = np.array([
[1, 2, 3, 5],
[4, 5, 6],
[7, 8, 9, 6],
[10, 11, 12]
])
print(A.shape)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[50], line 1
----> 1 A = np.array([
2 [1, 2, 3, 5],
3 [4, 5, 6],
4 [7, 8, 9, 6],
5 [10, 11, 12]
6 ])
7 print(A.shape)
ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (4,) + inhomogeneous part.
Yuqorida biz matritsaning birinchi qatorida 4 element ikkinchi qatorida esa 3 ta elementdan iborat bo’lishini xohlayabmiz, buning esa iloji yo’q. Shuning uchun ham har bir o’qda bir xil sondagi elementlari bo’lishi zarur. Bunda cheklov list
turida yo’q, chunki u ichma ich obyektlarni saqlaydi.
Demak A
obyekti 4 ta qator va 3 ta ustunda iborat matritsa(2 o’lchovli tensor) ekan. Biz ushbu jarayoni yana takrorlash orqali 3 o’lchovli tensor yoki n o’lchovli tensor ham yaratishimiz mumkin. Lekin biz ikki o’lchovlidan kattasini tassavur qila olmaymiz yoki biror qiymat bilan to’ldirishni tushuna olmaymiz. Biroq ilmiy hisoblarda esa biz ulardan doimiy foydalanamiz. Masalan, ramslarni RGB shaklda saqlasak, unda bizga bitta rasm uchun 3 o’lchovli tensor kerak bo’ladi. Agar rasmlar bittadan
ko’p bo’lsa, 4 o’lchovlidan foydalanamiz. Agar bizga faqat tensorning necha o’lovchligi zarur bo’lsa, u holda len
funksiyasidan foydalanishimiz mumkin:
[10]:
print(len(A.shape))
2
Ba’zida umumiy emelentlar sonini aniqlashga to’g’iri keladi, bu holda esa size
xususiyatidan foydalanish mumkin bo’ladi:
[11]:
print(A.size)
12
Obyektlarni yaratish funksiyalari
Ba’zi hollarda elementlari ma’lum bir son(lar)dan iborat bo’lgan bir jinsli tensor yaratish zarurati tug’iladi. Buning uchun biz NumPyning mavjud funkisyalaridan yodalanishimiz mumkin bo’ladi:
np.zeros()
– qiymati noldan iborat bo’lgan tensor yaratib beradi:
[61]:
a = np.zeros(shape=(10, ))
print("a=", a)
A = np.zeros(shape=(3, 4))
print("A=", A)
a= [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
A= [[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
np.ones()
– qiymati birdan iborat bo’lgan tensor hosil qiladi:
[62]:
a = np.ones(shape=(10, ))
print("a=", a)
A = np.ones(shape=(3, 4))
print("A=", A)
a= [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
A= [[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
Ushbu ikki funksiyada ham bir qancha argumentlar mavjud, ularda biri dtype
hisoblandi. Bu agrument orqali biz hosil bo’layotgan obyektning turini oldindan belgilashimiz mumkin:
[66]:
a = np.ones(shape=(10, ), dtype=np.int32)
print("a=", a)
print(a.dtype)
A = np.ones(shape=(3, 4), dtype=np.int32)
print("A=", A)
print(A.dtype)
a= [1 1 1 1 1 1 1 1 1 1]
int32
A= [[1 1 1 1]
[1 1 1 1]
[1 1 1 1]]
int32
Ushbu funksiyalarning quyidagi ko’rinishi ham mavjud: np.zeros_like()
va np.ones_like()
. Bu ikki funksiya berilgan tensorga o’xshash o’lchamdagi qiymatlari 0 yoki bir bo’lgan tensorlar hosil qiladi:
[67]:
a = np.array([1, 2, 3, 4])
b = np.ones_like(a)
print("b=", b)
A = np.array([
[1, 2, 3],
[4, 5, 6]
])
B = np.ones_like(A)
print("B=", B)
b= [1 1 1 1]
B= [[1 1 1]
[1 1 1]]
np.arange()
funksiyasi Pythondagirange()
funksiyasiga o’xshash hisoblanadi, ya’nistart
boshlanish,stop
tugash hamdastep
qadam parameterlarini olib,start
dan boshlabstop
gacha (kirmaydi)step
qadam bilan sonlarni hosil qiladi va ularni bizga vektor ko’rinishida qaytaradi. Masalan, 1 dan 10 gacha bo’lgan butun sonlarni quyidagicha hosil qilish mumkin:
[ ]:
a = np.arange(0, 10, 1)
a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Yuqorida biz hamma parameterlarini berdik, zarur bo’lgandagina hamma parameterlarni berish lozim. Kelishuv bo’yicha esa start=0
, step=1
, lekin stop
har doim berilishi kerak:
[ ]:
a = np.arange(10)
a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Quyida 2 dan 100 gacha juft sonlarni hosil qilish keltirilgan:
[ ]:
a = np.arange(2, 100, 2)
a
array([ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34,
36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68,
70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98])
Yuqoridagi ikkita funskiyaga qo’shimcha ravishda bir qancha funksiayalarimizda kelgusi mavzularda batafsil o’rganib chiqamiz.
Qiymat(lar)ni olish va berish
Ushbu ikki amal NumPyning sal qiyinroq hamda, shu bilan birgalikda, yana bir muhim afzaligi hisoblanadi. Keling namunalarni matrtisalar ustida ko’rib chiqaylik, shunda biz matritsadagi o’zgarishlarni tushunishimiz oson bo’ladi. Bu ikki amalda ham bir vaqtda ham list
obyektida bo’lgani kabi indekslash bor ham kesish amallari bor, faqat list
turidan farqli ravishda biz hamma indekslarni bitta []
qavs ichiga o’qlarni vergul orqali ajratib yozamiz.
Qiymat(lar)ni olish
Quyida birinchi A
obyektniga matritsani yuklaylik hamda uning 1-qator hamda 2-ustun elementining qiymatini chop qilaylik.
Eslatma. Pythondagi kabi Numpyning
ndarray
obyektida ham indekslash 0 sonidan boshlanadi.
[41]:
A = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
print(A[1, 2])
7
Agar biror o’qdagi to’liq qiymatlarni olmoqchi bo’lsa, u holda biz yana ndarray
obyektiga ega bo’lamiz. Masalan, yuqoridagi matritsaning 1-chi qatorini olaylik.
[42]:
print(A[1])
[5 6 7 8]
Oldingi misolda, qatorni olish oddiy, chunki matritsaning qatori ndarray
obyektining birinchi o’qida joylashgan. Lekin biz 0-ustuni olmoqchi bo’lsak, u holda :
kesish operatorini birinchi(qator) o’qqiga quyishimiz kerak hamda ikkinchi o’q(ustun)ning sonini esa verguldan keyin yozishimiz zarur bo’ladi:
[43]:
print(A[:, 0])
[1 5 9]
Yuqoridagi misoldan sodda bitta narsani biz aniqlashimiz mumkin, bu :
kesish operatori o’zi kelganda butun boshli o’qdagi hamma elementi oladi. Shunga asosan biz list
obyektidagi kesish qoidalarini ndarray
obyektlari uchun ham ishlatishimiz mumkin bo’ladi. Masalan, keling matritsaning 1-qatoridagi o’rtadagi ikki elementni (6, 7) olaylik. Buning uchun biz 1-qatorni to’liq tanlab, ustundan 1-dan 3-gacha bo’lganlarini tanlashimiz kerak, ya’ni 1:3
. Bunda 3 kirmaydi, shuning uchun ham
bitta ortiqcha qiymat yozayabmiz.
Eslatma. Agar biz matritsaning birinchi qatorining qiymatlarini olsak hamda ustuniga
:
kesish amalini qo’ysak(print(A[1, :])
) ham birinchi bundan oldingi misoldagi natijani olamiz, faqat ortiqcha kod yoziladi xolos.
[45]:
print(A[1, 1:3])
[6 7]
Agar yuqoridagi misolda biz birinchi o’qni to’liq tanlasak, u holda matritsaga ega bo’lamiz. Ushbu matritsa hamma qatorlardagi 1-2-elementlardan iborat bo’ladi.
[46]:
print(A[:, 1:3])
[[ 2 3]
[ 6 7]
[10 11]]
Endi list
obyektida mavjud bo’lgan start:stop:step
amali, ya’ni kesish amalini to’liqroq ko’rib chiqamiz. Bunda biz har doim har bir o’qni alohida qaraymiz. Keling A
matritsadgi 0 va 1-ustun qiymatlarini olaylik. Bu holda biz step=2
deb olishimiz zarur bo’ladi. Kelishuv bo’yicha esa bu step=1
.
[48]:
print(A[:, ::2])
[[ 1 3]
[ 5 7]
[ 9 11]]
Yuqorida faqat step
parametrga qiymat berdik, qolganlariga agar qiymat berilmasa, holda ular mos ravishda 0 va o’qdagi elementlar sonini oladi.
Oldin ta’kidlaganimizdek, NumPyning kengaytirilgan imkoniyatlari qismida ushbu bo’limga qo’shimchalar qo’shamiz.
Qiymat(lar) berish
Qiymatlar berish ham olishga o’xshash amalga oshadi odatda, faqat qiymat berishda biz ba’zi cheklovlarga e’tibor berishimiz zarur bo’ladi:
berilayotgan qiymat bilan ajratilgan sohaning o’lchami bir xil bo’lishi yoki umumiy qoidalarga mos tushishi zarur;
ndarray
obyketi o’zgaruvchan(muttable) hisoblangani uchun biz osongina qiymatlari indekslash amallari orqali berishimiz mumkin bo’ladi. Masalan, quyida matritsaning 2-qatorining 1-ustuni elementini o’zgartiraylik:
[51]:
A = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
A[2, 1] = 100
print(A)
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 100 11 12]]
Endi biror o’qning qiymatini to’g’icha bitta qiymat bilan almashtiraylik:
[52]:
A = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
A[0] = 0
print(A)
[[ 0 0 0 0]
[ 5 6 7 8]
[ 9 10 11 12]]
Ko’rib turganimizdek biz butun qatorgai hamma elementlarni 0 qiymatiga o’zgartirdik. Agar biror ustun uchun shu amalni bajarmoqchi bo’lsak, u holda shunchaki :
kesish operatoridan foydalanishimiz yetarli bo’ladi:
[53]:
A = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
A[:, 2] = 0
print(A)
[[ 1 2 0 4]
[ 5 6 0 8]
[ 9 10 0 12]]
Xuddi shu kabi biror sohaga ham qiymat bersak bo’ladi. Bu holda faqat ma’lum bir sohani tanlashimiz zarur bo’ladi. Sohani tanlashni esa kesish operatori orqali bajarishini yuqorida ko’rib chiqdik.
[56]:
A = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
A[1:, :2] = 0
print(A)
A = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
A[1:, ::2] = 0
print(A)
[[ 1 2 3 4]
[ 0 0 7 8]
[ 0 0 11 12]]
[[ 1 2 3 4]
[ 0 6 0 8]
[ 0 10 0 12]]
Bunday misollarni ko’plab keltirish mumkin. Keling endi bitta son emas ndarray
obyektini biror sohaga beraylik. Bunining uchun birinchi navbatda sohalarning o’lchami mos bo’lishi kerak.
[57]:
A = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
A[1:, ::2] = np.array([
[11, 22],
[33, 44]
])
print(A)
[[ 1 2 3 4]
[11 6 22 8]
[33 10 44 12]]
Qiymat berishining yana bir turi bu quyidagi teng kuchli misolarda ko’rishimiz mumkin.
[58]:
A = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
A[1:, ::2] = np.array([[11, 22]])
print(A)
[[ 1 2 3 4]
[11 6 22 8]
[11 10 22 12]]
[59]:
A = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
A[1:, ::2] = np.array([11, 22])
print(A)
[[ 1 2 3 4]
[11 6 22 8]
[11 10 22 12]]
Yuqoridagi ikki misol ham bir xil vazifani bajarayabdi. Bu kabi yuqori darajadagi misollarni keyinchalik yana qayta o’rganib chiqamiz.
Element bo’yicha amallar
Ushbu kutubxona yuqorida takidlaganimizdek juda ko’p imkoniyatlarga ega bo’lib, hozir shulardan birinchisi – vektorlashgan amallarni ko’rib chiqamiz. Biz vektorlashgan amallar deganda for
yoki while
operatorlari orqali takrorlash asosida bajarildigan algorimtlarni biz shu operatorlarsiz tezlikda bajarishimiz mumkin bo’ladi. Keling soddalik uchun birinchi 10 000 000 sondan iborat bo’lgan vektorning har bir elementiga biror soni qo’shib ko’raylik. Avallo list
va ndarray
turida
ikkita qiymatlari bir bo’lgan obyekt yaratamiz.
[30]:
n = 10_000_000
py_nums = list(range(n))
numpy_nums = np.array(py_nums)
So’ng esa list
obyektning har bir elementining qiymatini 1 ga oshirib chiqamiz, buning uchun biz for
operatoridan quydagicha foydalanamiz:
[31]:
%timeit for i in range(n): py_nums[i] += 1
593 ms ± 4.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Yuqorida %timeit
sehrli buyrug’i biz shu qator uchun ketgan vaqtni hisoblash hamda qolgan statistikalarni chiqarishga yordam beradi. Xususan, bu kod jami 7 marta ishga tushirilib, keyin natijalarning o’rtachasi olingan.
[32]:
%timeit numpy_nums1 = numpy_nums + 1
15.6 ms ± 137 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Ko’rib turganimizdek, oddiy list
obyekti uchun 593 ms vaqt ketgan bo’lsa, ndarray
obyekti uchun esa 15.6 ms safrlangan. Bundan tashqari kodimiz ham ancha sodda yozilgan Numpy asosida.
Numpy tensorlari ustida amalar bajargan biz bir qancha narsalarga sinchikovlik bilan e’tibor berishimiz zarur, aks holda biz o’zimiz o’ylamagan natijaga erishamiz yoki murakkab masalalarni takrorlash operatorlarisiz bajara olmay qolamiz. Masalan, yuqorida biz numpy_nums
obyektning har bir elementini bittaga ochiqib numpy_nums1
obyektini hosil qildik, lekin takrorlash amallaridan foydalanmadik. Bu kod uchun biz qisqacha qilib quyidagi savolni berishimiz mumkin “Aslida vektorga soni
qo’shish mumkinmi (matematik jihatdan)?” Ko’pchilik aytishi mumkin “yo’q deyishi aniq.” Lekin NumPy kutubxonasi ilmiy masalalarni yechishda tensorlar ustida amallarni imkon qadar optimlashtirish orqali amalga oshirgan va buni biz ingliz tilida “broadcasting” deb ataymiz. Shuning uchun ham yuqoridagi kodda ikkita turdagi obyekt o’rtasida qo’shish amali bajarilyabdi va natija esa “broadcasting” qoidalari asosida. Bu haqida rasmiy sahifada batafsil bilib olishingiz mumkin: NumPy
broadcasting.
NumPy obyekti va son ustida amallar
To’rta arfimetik amallar hamda %
, //
amallari tensorning o’lchami qanday bo’lishidan qattiy nazar tensorning har bir elementiga alohida qo’laniladi. Masalan, quyidagi matritsani va 2 sonini qaraylik.
[33]:
A = np.array([
[1, 2, 3],
[4, 5, 6],
])
print(A)
[[1 2 3]
[4 5 6]]
[38]:
B = A + 2
print("A+2=\n", B)
B = A - 2
print("A-2=\n", B)
B = A * 2
print("A*2=\n", B)
B = A / 2
print("A/2=\n", B)
B = A // 2
print("A//2=\n", B)
B = A % 2
print("A%2=\n", B)
A+2=
[[3 4 5]
[6 7 8]]
A-2=
[[-1 0 1]
[ 2 3 4]]
A*2=
[[ 2 4 6]
[ 8 10 12]]
A/2=
[[0.5 1. 1.5]
[2. 2.5 3. ]]
A//2=
[[0 1 1]
[2 2 3]]
A%2=
[[1 0 1]
[0 1 0]]
Yuqoridagi natijalardan ma’lumki 2 sonini A
tensorining har bir elementiga o’ng tomondan amalga mos ta’sir qilmoqda. Keyingi holat esa agar shu son amalning chap tomonida kelsa, qanday natija bo’ladi shuni ko’raylik:
[39]:
B = 2 + A
print("A+2=\n", B)
B = 2 - A
print("A-2=\n", B)
B = 2 * A
print("A*2=\n", B)
B = 2 / A
print("A/2=\n", B)
B = 2 // A
print("A//2=\n", B)
B = 2 % A
print("A%2=\n", B)
A+2=
[[3 4 5]
[6 7 8]]
A-2=
[[ 1 0 -1]
[-2 -3 -4]]
A*2=
[[ 2 4 6]
[ 8 10 12]]
A/2=
[[2. 1. 0.66666667]
[0.5 0.4 0.33333333]]
A//2=
[[2 1 0]
[0 0 0]]
A%2=
[[0 0 2]
[2 2 2]]
Ushbu holatda ham natija tensor bo’ladi, faqat 2 sonini A
tensorning har elementiga ko’rsatilgan amal asosida natija hosil bo’ladi. Xuddi shunday tarizda, boshqa ixtiyoriy o’lchamdagi tensor uchun yuqoridagidek holatni qo’llashimiz mumkin bo’ladi.
Bir xil o’lchamli tensorlar ustida amallar
Agar tensorlarning ikkisining o’lchamlari bir xil bo’lsa, u holda mos elementlarining o’rtasidagi qaralayotgan amal oddiy ikkita son ustida bo’lgani kabi ishlaydi. Quyida shunga misollar ko’rishimiz mumkin:
[69]:
A = np.array([
[1, 2, 3],
[4, 5, 6],
])
B = np.array([
[6, 5, 4],
[3, 2, 1],
])
C = A + B
print("A+B=\n", C)
A+B=
[[7 7 7]
[7 7 7]]
Yuqorida bir matrisani ikkinchisiga qo’shamiz. Ushbu qo’shish amali algebraik ma’noda ham o’rinli hisoblanadi. Lekin ikki matritsani ko’paytirishning algebraik ma’nosi oddiy *
ko’paytirish amali bilan hosil bo’lmaydi. Bu yerda *
amali ikki matritsaning mos elementlarini ko’paytirishdan hosil bo’lgan, yangi matritsani hosil qiladi:
[21]:
A = np.array([
[1, 2, 3],
[4, 5, 6],
])
B = np.array([
[6, 5, 4],
[3, 2, 1],
])
C = A * B
print("A*B=\n", C)
A*B=
[[ 6 10 12]
[12 10 6]]
Yuqoridagi qolgan amallar ham xuddi shunday element bo’yicha bo’ladi:
[71]:
print("A-B=\n", A - B)
print("A/B=\n", A / B)
print("A//B=\n", A // B)
print("A%B=\n", A % B)
A-B=
[[-5 -3 -1]
[ 1 3 5]]
A/B=
[[0.16666667 0.4 0.75 ]
[1.33333333 2.5 6. ]]
A//B=
[[0 0 0]
[1 2 6]]
A%B=
[[1 2 3]
[1 1 0]]
Ushbu amallar bir o’lchamdagi bo’lganlar uchun amallarni tushunish oson. Biz keyinchalik Numpyning keyngaytirilgan bobida turli xildagi tensorlar ustida ham amallarni ko’rib chiqamiz.
Algebraik amallar
Biz endi ba’zi algebraik amallarni bajarish ko’rib chiqamiz.
matritsani transfanerlash juda sodda bo’lib, shunchaki har bir matritsaning
.T
xususiyatini chaqirishimiz yetarli:
[27]:
print("A matritsaning transfanerlangani: ")
print(A.T)
A matritsaning transfanerlangani:
[[1 4]
[2 5]
[3 6]]
Ushbu xususiyatni matritsalar ustida ishlatganda tushunishda muammo yo’q, lekin o’lcham 2 tadan ortiq bo’lgan tensorlarda esa biz qiynalamiz. Shuning uchun esa umumiy holda, transpose()
funksiyasi va metodi mavjud. Ushbu metodga biz parameter sifatida qaysi o’lchamlarning joyi qanday o’zgarishini aytishimiz zarur. Masalan, oldingi transfanerlashni quyidagicha bajaramiz:
Eslatma. Metod va funkiyaning farqi deyarli qo’llanishda yo’q, faqat metod obyektga tegishli bo’ladi. Funskiya esa o’zi alohida hisoblanadi. Bu haqida Obyektga yo’nlantirilgan dasturlash qismida o’ganamiz.
[28]:
A.transpose(1, 0)
[28]:
array([[1, 4],
[2, 5],
[3, 6]])
Yuqorida biz 0 va 1 deb A
matritsaning o’lchovlarini joyini almashtirishni ko’rsatdik.
Bir xil o’lchamli ikki vektorni skalyar(ichki) ko’paytirish uchun, biz
np.inner()
funksiyasidan foydalanamiz:
[29]:
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
np.inner(a, b)
[29]:
32
Vektorni vektorga tashqi ko’paytirish amali mavjud bo’lib, bu amal bizga matritsa qaytaradi. Bu amalni NumPy yordamida bajarish uchun
np.outer()
funksiyasini ishlatamiz:
[30]:
a = np.array([1, 2, 3])
b = np.array([4, 5, 6, 7])
np.outer(a, b)
[30]:
array([[ 4, 5, 6, 7],
[ 8, 10, 12, 14],
[12, 15, 18, 21]])
Matritsani matritsaga ko’patirish uchun biz
np.matmul()
funskiyasidan foydalanamiz. Ushbu amalni bajarish uchun birinchi matritsaning ikkinchi o’lchovi ikkinchi matritsaning birinchi o’choviga teng bo’lishi kerak:
[32]:
A = np.array([
[1, 2, 3],
[4, 5, 6],
])
np.matmul(A, A.T)
[32]:
array([[14, 32],
[32, 77]])
Ba’zida vektorni matritsaga ko’patirish ham zarur bo’lib qoladi, u holda vektorni biz matritsa ko’rinishida tasvirlab olamiz:
[33]:
a = np.array(A[0])
np.matmul(A, a)
[33]:
array([14, 32])
Bunga o’xshagan amallar keyiinchalik yanada chuquroq o’rganib chiqamiz. Chunki bunday amallar soni yetarlicha ko’p bo’lib, ularni avval algebraik ma’nolarini hamda ularning SIda ishlatilishini bilib olishimiz zarur hisoblanadi.
Qayta shakllash
Tensorning .shape
xususiyati bizga o’sha tensorning qanday shaklda ekanligini, ya’ni har bir o’lchamida nechtadan element (qiymat) bor ekenaligini ifodalaydi. Ushbu narsani biz tensorning “shakl”i deb tarjima qilsak bo’ladi. Endi bu mavzuda biz qanday qilib tensorga qayta shakl berishni o’rganamiz. Ingliz tilida bu iborani “reshaping” deb atashadi.
Ushbu amalni tushinish uchun biz tensorlar qanday qilib elementlarini saqlashini o’rganishimiz kerak bo’ladi. Buni biz hozir eng qisqacha o’rganamiz va keyinroq esa chuqoroq qayta o’rganamiz. O’lchamlar sonini qanday bo’lishidan qa’tiy nazar, tensorlar elementlarini odatda uzluksiz ko’rinishda saqlaydi. Masalan, quyida matritsani ko’raylik:
[34]:
A = np.array([
[1, 2, 3],
[4, 5, 6],
])
A
[34]:
array([[1, 2, 3],
[4, 5, 6]])
Ushbu matritsadagi qiymatlar biz uchun ikki o’lchovli ko’rinib turibdi, lekin aslida esa bir o’lchovda bo’lib, ya’ni:
[48]:
A = np.arange(1, 7)
A
[48]:
array([1, 2, 3, 4, 5, 6])
O’chovlarni aniqlash uchun esa alohida tuple
turida .shape
xususiyati ishlatiladi. Bunda A.shape = (2, 3)
. Bu esa bizga birinchi A
ning hamma elementlarini teng ikkiga bo’lishimizni bildiradi. Agar shunday qilsak, u holda ikkinchi o’lchamga 3 tadan qiymat qoladi. Agar tensor uch o’lchovli bo’lsa:
[37]:
A = np.array([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]],
[[9, 10],
[11, 12]]])
print(A)
print(A.shape)
[[[ 1 2]
[ 3 4]]
[[ 5 6]
[ 7 8]]
[[ 9 10]
[11 12]]]
(3, 2, 2)
u holda biz hamma elementlarni yana bitta qator ko’rinishida deb:
[49]:
A = np.arange(1, 13)
A
[49]:
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
so’ng birinchi teng 3 ta qismga ajratamiz va ajratilgan har bir qismni yana teng ikki qismga ajratamiz. Shu orqali NumPy odatda tensorlarni uzliksiz ko’rinishda saqlaydi.
Keling endi tensorlarning .reshape()
metodi yoki np.reshape()
funksiyasini qarab chiqaylik. Birinchi quyidagi bir o’lchovli tensorni ikki o’lochli ko’rinishga o’tkazaylik, bunda A.shape = (4, 3)
:
[50]:
A = np.arange(1, 13)
B = A.reshape(4, 3)
print(B)
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
Yuqorida biz aytgan maqsadga erishish uchun reshape()
metodiga zarur bo’lgan o’lchamlarni berdik, ya’ni A.reshape(4, 3)
ko’rinishida. Xuddi shu vazifani biz np.reshape()
funksiyasi yordamida ham qilishimiz mumkin:
[42]:
B = np.reshape(A, (4, 3))
print(B)
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
Ikki xil bajarishda faqat ba’zi bir parameterlarni o’zgartirdik. Yuqoridagi ikki yo’l ham bizga yangi tensorni natija sifatida qaytaradi. Agar biz to’g’iridan-to’g’iri natijani A
tensorga ta’sir qilishini xohlasak, u holda A.resize()
metodidan foydalanishimiz zarur bo’aldi:
[43]:
A.resize(4, 3)
print(A)
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
Xuddi shu kabi boshqa o’lovhlarga ham o’tish mumkin, masalan shape=(2, 2, 3)
:
[82]:
A.resize(2, 2, 3)
print(A)
[[[4 7 0]
[1 4 1]]
[[3 6 1]
[6 1 1]]]
Yoki shape=(2, 3, 2)
:
[83]:
A.resize(2, 3, 2)
print(A)
[[[4 7]
[0 1]
[4 1]]
[[3 6]
[1 6]
[1 1]]]
Qayta shakl berishdagi eng asosiy narsa bu umumiy elementlar soni yangi o’lchamlardagi elementlar sonining ko’paytmasiga teng bo’lishi kerak. Yuqoridagi misolda, 12=4*3
. Aks holda xatolik vujudga keladi:
[51]:
A = np.arange(1, 13)
B = A.reshape(5, 2)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[51], line 2
1 A = np.arange(1, 13)
----> 2 B = A.reshape(5, 2)
ValueError: cannot reshape array of size 12 into shape (5,2)
Yig’ishlar
Biz Ingliz tilidagi “Aggregation” atamasini O’zbek tiliga “Yig’ish” deb tarjima qildik. Chunki biz biror o’lchov bo’yicha hamda ma’lum qoida asosida qiymatlarni “yig’ib” chiqamiz. Bu yerda biz faqat sonlarni qo’shishni amalini nazarda tutganimiz yo’q. Masalan, “har bir qatordan eng a’lochi o’quvchilarni (yig’ib yoki tanlab) oling deyish”ni ham biz “yig’ish” deb atashimiz mumkin.
Yig’ish uchun NumPy bizga bir qator funskiyalarni taqdim etadi. Biz shulardan eng asosiylarini ko’rib chiqamiz. Bundan oldin qiymatlarimiz turli xil bo’lishi uchun keling qisqacha np.random
modulinining np.random.randint()
funkisyasini o’rganib olaylik. Bu funksiya yordamida biz turli xil tensorlarni hosil qilib ular yordamida yig’ish amallarini o’rganib chiqamiz. Ushbu funskiya 3 ta parameter qabul qiladi: low
kichigi, high
(kirmaydi) kattasi va size
tensorning o’lchami.
Ushbu parameterlarni bu funksiya qabul qilib, shu oraliqdan butun sonlarni tasodifiy ravishda hosil qilib beradi. Faqat natijalar har safar har qil bo’lishining oldini olish maqsadida biz np.random
modulidan quyidagicha obyekt yasab olamiz:
[91]:
rng = np.random.default_rng(42)
Yuqoridagi np.random.default_rng()
funskiyasidagi seed
parameteri biz uchun asosiy hisoblanadi. Agar shu soni o’zgartirsak bizga np.random.randint()
funksiyasi boshqa sonlarni hosil qilib beradi. Endi biz yuqoridagi rng
obyektidan quyidagicha foydalanishimiz mumkin:
[92]:
A = rng.integers(0, 10, size=(4, 3))
A
[92]:
array([[0, 7, 6],
[4, 4, 8],
[0, 6, 2],
[0, 5, 9]])
Yuqorida biz np.random.randint()
funksiyasini rng.integers()
funksiyasi bilan almashtirdik hamda zarur parameterlar bilan chaqirdik. Oxir oqibatda, biz 4 ta qator va 3 ta ustundan iboray matritsaga ega bo’ldik. Bu yerda ushbu funksiyaning low
parameteri kelishuv bo’yicha 0 ga teng.
np.max()
funksiyasini eng katta qiymatni topish uchun ishlatiladi:
[93]:
a = rng.integers(20, size=(12, ))
print(a)
print("Eng katta qiymat:", np.max(a))
[14 15 14 15 10 2 16 9 10 7 3 18]
Eng katta qiymat: 18
Yig’ish funksiyalarida eng asosiy vazifani bajaruvchi parameter axis
hisoblanib, u qaysi o’q(o’lchov) bo’yicha yig’ish amalini barajish kerakligini belgilaydi. Masalan, matritsaning har bir qatoridagi qiymatlarning kattasini topmoqchi bo’lsak, u holda axis=1
bo’lishi kerak:
[94]:
A = rng.integers(25, size=(4, 3))
print(A)
print("Matritsaning qatoridagi katta elementlari:", np.max(A, axis=1))
[[19 16 10]
[20 13 11]
[11 5 2]
[13 22 1]]
Matritsaning qatoridagi katta elementlari: [19 20 11 22]
yoki ustundagi elementlari bo’yicha, axis=0
:
[95]:
A = rng.integers(25, size=(4, 3))
print(A)
print("Matritsaning ustunidagi katta elementlari:", np.max(A, axis=0))
[[21 20 6]
[15 4 18]
[17 8 1]
[24 11 22]]
Matritsaning ustunidagi katta elementlari: [24 20 22]
np.min()
funksiyasini eng kichik qiymatni topish uchun ishlatiladi:
[96]:
a = rng.integers(20, size=(12, ))
print(a)
print("Eng kichik qiymat:", np.min(a))
A = rng.integers(25, size=(4, 3))
print(A)
print("Matritsaning qatoridagi kichik elementlari:", np.min(A, axis=1))
print("Matritsaning ustunudagi kichik elementlari:", np.min(A, axis=0))
[13 15 15 3 7 9 9 0 10 3 14 13]
Eng kichik qiymat: 0
[[23 18 9]
[24 10 8]
[22 9 1]
[11 19 4]]
Matritsaning qatoridagi kichik elementlari: [9 8 1 4]
Matritsaning ustunudagi kichik elementlari: [11 9 1]
np.argmax()
vanp.argmin()
funksiyalari eng katta va kichik qiymat joylashgan o’rinlarni mos ravishda qaytaradi:
[98]:
a = rng.integers(20, size=(12, ))
print(a)
print("Eng katta qiymat joylashgan o'rin:", np.argmax(a))
print("Eng kichik qiymat joylashgan o'rin:", np.argmin(a))
A = rng.integers(25, size=(4, 3))
print(A)
print("Qatoridagi katta elementlarning o'rni:", np.argmax(A, axis=1))
print("Qatoridagi kichik elementlarning o'rni:", np.argmin(A, axis=1))
print("Ustunidagi katta elementlarning o'rni:", np.argmax(A, axis=0))
print("Ustunidagi kichik elementlarning o'rni:", np.argmin(A, axis=0))
[ 4 13 12 2 16 3 16 0 15 15 15 13]
Eng katta qiymat joylashgan o'rin: 4
Eng kichik qiymat joylashgan o'rin: 7
[[11 17 6]
[19 13 11]
[12 14 0]
[ 3 6 2]]
Qatoridagi katta elementlarning o'rni: [1 0 1 1]
Qatoridagi kichik elementlarning o'rni: [2 2 2 2]
Ustunidagi katta elementlarning o'rni: [1 0 1]
Ustunidagi kichik elementlarning o'rni: [3 3 2]
np.sum()
funksiyalari ko’rsatilgan o’q bo’yicha elementlarning yig’indisini hisoblaydi:
[99]:
a = rng.integers(20, size=(12, ))
print(a)
print("Yig'indi:", np.sum(a))
A = rng.integers(25, size=(4, 3))
print(A)
print("Qatoridagi elementlar yig'indisi:", np.sum(A, axis=1))
print("Ustunidagi elementlar yig'indisi:", np.sum(A, axis=0))
[ 8 13 13 9 17 11 1 15 11 12 11 11]
Yig'indi: 132
[[ 2 13 19]
[ 7 15 0]
[ 8 10 24]
[ 5 6 10]]
Qatoridagi elementlar yig'indisi: [34 22 42 21]
Ustunidagi elementlar yig'indisi: [22 44 53]
np.mean()
funksiyalari ko’rsatilgan o’q bo’yicha elementlarning o’rtachasini hisoblaydi:
[100]:
a = rng.integers(20, size=(12, ))
print(a)
print("O'rtachasi:", np.mean(a))
A = rng.integers(25, size=(4, 3))
print(A)
print("Qatoridagi elementlar o'rtachasi:", np.mean(A, axis=1))
print("Ustunidagi elementlar o'rtachasi:", np.mean(A, axis=0))
[19 17 0 4 16 1 17 5 18 5 8 13]
O'rtachasi: 10.25
[[ 3 13 12]
[19 24 16]
[10 10 10]
[20 8 4]]
Qatoridagi elementlar o'rtachasi: [ 9.33333333 19.66666667 10. 10.66666667]
Ustunidagi elementlar o'rtachasi: [13. 13.75 10.5 ]