Гузариш ба мундариҷа
Мавзӯи 6 аз 6

Матритсаҳо, мураттабсозӣ ва қиматҳои ягона

Ин барои чӣ лозим аст

Мавзӯи охирини курс абзорҳоеро ҷамъ мекунад, ки бе онҳо на таҳлили маълумот ва на омӯзиши мошинӣ иҷро намешавад. Зарби матритсаҳо «ҳар харидор чӣ қадар пардохт» ё «модел чӣ пешгӯӣ медиҳад»-ро бо як амал ҳисоб мекунад. Ҳалли системаҳои муодилаҳо бузургиҳои номаълумро аз рӯи якчанд шарт якбора меёбад. Мураттабсозӣ, қиматҳои ягона ва ҷустуҷӯ дар массиви мураттаб ба саволҳои «кӣ беҳтарин аст», «ҳар қимат чанд маротиба вомехӯрад», «кадом ҷой дар рейтинг» ҷавоб медиҳанд.

Ҳамаи ин дар numpy ҳатто дар миллионҳо унсур тез кор мекунад — хеле тезтар аз ҳамин кор дар Python-и холис.

Зарби матритсавӣ: @

Дар хотир доред: * дар массивҳо унсур ба унсур зарб мекунад. Зарби матритсавӣ амали дигар аст, онро бо амалгари @ (ё функсияи np.dot) ишора мекунанд.

Аз зарби ҷадвал ба вектор оғоз мекунем. Нархи се мол ва чанд адад аз ҳар мол ду харидор харидаанд, маълум аст. Ҳар кадом чӣ қадар пардохт?

import numpy as np

prices = np.array([50, 120, 30])
baskets = np.array([[2, 0, 5],
                    [1, 1, 0]])
print(baskets @ prices)

Натиҷа:

[250 170]

Харидори якум: 2·50 + 0·120 + 5·30 = 250. Дуюм: 1·50 + 1·120 + 0·30 = 170. Ҳар сатри ҷадвал унсур ба унсур ба вектор зарб мешавад ва ҳосилҳои зарб ҷамъ мешаванд. Бе @ бояд (baskets * prices).sum(axis=1) менавиштем — натиҷа ҳамон аст.

Зарби ду матритса

Матритса — танҳо массиви дученакаи ададҳо аст. Ҳосили зарби матритсаҳои A @ B — матритсаи нав аст, ки унсураш дар сатри i ва сутуни j ба ҳосили ҷамъи ҳосилҳои зарби сатри i-и матритсаи A ба сутуни j-и матритсаи B баробар аст.

import numpy as np

a = np.array([[1, 2],
              [3, 4]])
b = np.array([[5, 6],
              [7, 8]])
print(a @ b)
print(a * b)

Натиҷа:

[[19 22]
 [43 50]]
[[ 5 12]
 [21 32]]

Унсури чапи болоиро месанҷем: сатри [1, 2] ба сутуни [5, 7] — 1·5 + 2·7 = 19. a * b бошад — танҳо ҳосилҳои зарби ҷуфтӣ: 1·5, 2·6 ва ғайра.

Барои он ки «сатр ба сутун» маъно дошта бошад, дарозиҳо бояд мувофиқ оянд: шумораи сутунҳои матритсаи якум = шумораи сатрҳои дуюм. Матритсаи (n, k) ба (k, m) зарб мешавад, натиҷа — (n, m).

import numpy as np

a = np.ones((2, 3))
b = np.ones((3, 4))
print((a @ b).shape)

Натиҷа:

(2, 4)

Тартиб муҳим аст: a @ b ва b @ a — одатан матритсаҳои гуногунанд (ва баъзан дуюмашро умуман ҳисоб кардан мумкин нест).

Матритсаи воҳидӣ

Матритсаи воҳидӣ — квадратӣ, бо якҳо дар диагонали асосӣ ва сифрҳо дар ҷойҳои дигар. Зарб ба он ҳеҷ чизро тағйир намедиҳад, мисли зарби адад ба 1. Бо np.eye(n) сохта мешавад:

import numpy as np

e = np.eye(2, dtype=int)
a = np.array([[1, 2],
              [3, 4]])
print(e)
print(a @ e)

Натиҷа:

[[1 0]
 [0 1]]
[[1 2]
 [3 4]]

Модули np.linalg

Функсияҳои алгебраи хаттӣ дар зермодули np.linalg ҷамъ шудаанд:

  • np.linalg.det(m) — детерминант: як адад, ки аз рӯи он маълум мешавад, ки матритса «таназзулёфта» аст ё не (агар он 0 бошад, системаи бо ин матритса ҳалли ягона надорад);
  • np.linalg.inv(m) — матритсаи баръакс: чунон ки m @ inv(m) матритсаи воҳидӣ медиҳад;
  • np.linalg.solve(m, b) — ҳалли системаи муодилаҳо.
import numpy as np

m = np.array([[2.0, 1.0],
              [1.0, 3.0]])
print(round(np.linalg.det(m), 6))
print(np.linalg.inv(m))
print(np.allclose(m @ np.linalg.inv(m), np.eye(2)))

Натиҷа:

5.0
[[ 0.6 -0.2]
 [-0.2  0.4]]
True

round дар ин ҷо лозим аст, зеро ҳисобҳои касрӣ хатогиҳои хурдакак медиҳанд, масалан 4.999999999999999. Бо ҳамин сабаб m @ inv(m)-ро бо матритсаи воҳидӣ на бо ==, балки бо функсияи np.allclose — «бо хатогии хурд баробар» муқоиса мекунанд.

Ҳалли системаи муодилаҳо

Системаи муодилаҳои хаттӣ — якчанд муодилаи намуди «ҳосили ҷамъи номаълумҳо бо коэффитсиентҳо ба адад баробар аст». Масалан:

2x + y = 4 x + 3y = 7

Онро дар шакли матритсавии m @ v = b менависанд: дар матритсаи m — коэффитсиентҳо (як сатр барои ҳар муодила), дар вектори b — тарафҳои рост, v = [x, y] — номаълумҳо. Чунин системаро np.linalg.solve ҳал мекунад:

import numpy as np

m = np.array([[2.0, 1.0],
              [1.0, 3.0]])
b = np.array([4.0, 7.0])
v = np.linalg.solve(m, b)
print(v)
print(m @ v)

Натиҷа:

[1. 2.]
[4. 7.]

Ҳамин тавр, x = 1, y = 2. Санҷиши m @ v тарафҳои ростро баргардонд — ҳал дуруст аст. solve аз ҳисоб кардани матритсаи баръакс ва зарб ба он дақиқтар ва тезтар аст.

Мисол аз ҳаёт: 3 дафтар ва 2 қалам 130 сомонӣ, 1 дафтар ва 4 қалам бошад 110 сомонӣ меистанд. Ҳар чиз чанд пул аст?

import numpy as np

m = np.array([[3, 2],
              [1, 4]])
b = np.array([130, 110])
print(np.linalg.solve(m, b))

Натиҷа:

[30. 20.]

Дафтар — 30 сомонӣ, қалам — 20.

Мураттабсозӣ: sort ва argsort

np.sort(a) массиви мураттабшудаи нав бармегардонад, усули a.sort() бошад худи массивро дар ҷояш мураттаб мекунад. Мураттабсозӣ ҳамеша бо тартиби афзуншавӣ аст; бо тартиби камшавӣ — натиҷаро бо бурриши [::-1] баръакс кардан лозим.

import numpy as np

a = np.array([7, 2, 9, 4])
print(np.sort(a))
print(np.sort(a)[::-1])
print(a)
a.sort()
print(a)

Натиҷа:

[2 4 7 9]
[9 7 4 2]
[7 2 9 4]
[2 4 7 9]

Дар ҷадвал np.sort бо пешфарз ҳар сатрро алоҳида мураттаб мекунад (axis=-1), бо axis=0 бошад — ҳар сутунро.

np.argsort на қиматҳо, балки индексҳои унсурҳоро бо тартиби афзуншавӣ бармегардонад. Ин вақте лозим аст, ки на танҳо қимат, балки аз куҷо будани он муҳим аст:

import numpy as np

scores = np.array([82, 95, 78])
order = np.argsort(scores)
print(order)
print(scores[order])

Натиҷа:

[2 0 1]
[78 82 95]

Чунин хонда мешавад: хурдтарин унсур дар мавқеи 2, навбатӣ — дар мавқеи 0, калонтарин — дар мавқеи 1 аст.

Бо ёрии argsort ва интихоб аз рӯи индексҳо як маълумотро аз рӯи маълумоти дигар мураттаб мекунанд — масалан, номҳоро аз рӯи холҳо:

import numpy as np

names = np.array(["Зарина", "Бахтиёр", "Нигина"])
scores = np.array([82, 95, 78])
order = np.argsort(scores)[::-1]
print(names[order])
print(scores[order])

Натиҷа:

['Бахтиёр' 'Зарина' 'Нигина']
[95 82 78]

Қиматҳои ягона: np.unique

np.unique қиматҳои гуногуни массивро бо тартиби афзуншавӣ бармегардонад. Бо return_counts=True — инчунин ҳар кадом чанд маротиба вомехӯрад:

import numpy as np

votes = np.array(["ҳа", "не", "ҳа", "ҳа", "бетараф"])
values, counts = np.unique(votes, return_counts=True)
print(values)
print(counts)

Натиҷа:

['бетараф' 'не' 'ҳа']
[1 1 3]

Сатрҳо аз рӯи тартиби рамзҳои ҳарфҳо мураттаб мешаванд. Функсия ду массиви дарозиашон якхела бармегардонад, бинобар ин натиҷаро дарҳол ба ду тағйирёбанда мекушоянд. Қимати аз ҳама зиёд вохӯрандаро чунин ёфтан мумкин: values[counts.argmax()].

Бо return_index=True донистан мумкин, ки ҳар қимат бори аввал дар куҷо вохӯрд:

import numpy as np

a = np.array([5, 3, 5, 1])
values, first = np.unique(a, return_index=True)
print(values, first)

Натиҷа:

[1 3 5] [3 1 0]

Ҷустуҷӯ дар массиви мураттаб: np.searchsorted

np.searchsorted(s, x) ба савол ҷавоб медиҳад: x-ро ба кадом ҷойи массиви мураттаби s гузоштан лозим, то тартиб вайрон нашавад? Ин ҳамон шумораи унсурҳои аз x хурд аст.

import numpy as np

s = np.array([10, 20, 20, 30])
print(np.searchsorted(s, 20))
print(np.searchsorted(s, 20, side="right"))
print(np.searchsorted(s, 25))
print(np.searchsorted(s, [5, 25, 40]))

Натиҷа:

1
3
3
[0 3 4]
  • side="left" (бо пешфарз) — ҷой пеш аз унсурҳои баробар = чанд унсур аз x қатъиян хурд аст;
  • side="right" — ҷой пас аз унсурҳои баробар = чанд унсур аз x хурд ё баробар аст;
  • якбора массиви қиматҳоро додан мумкин — массиви ҷойҳоро мегиред.

Мисол: чанд хонанда баҳои на зиёд аз 70 гирифтанд?

import numpy as np

grades = np.sort(np.array([65, 90, 70, 55, 85, 70]))
k = np.searchsorted(grades, 70, side="right")
print(grades)
print(k)

Натиҷа:

[55 65 70 70 85 90]
4

Дар дохил чӣ тавр кор мекунад

Зарби матритсаҳо ва solve-ро numpy худаш ҳисоб намекунад: вай корро ба китобхонаҳои BLAS ва LAPACK месупорад, ки даҳсолаҳо барои протсессорҳо беҳтар карда шудаанд. Бинобар ин зарби матритсаҳои 1000×1000 (як миллиард зарб) як ҳиссаи сонияро мегирад.

np.sort мураттабсозии тезро бо забони C истифода мебарад — тақрибан n log n амал. np.searchsorted бошад — ҷустуҷӯи дуӣ аст: ба мобайни массив менигарад, нисфашро мепартояд ва такрор мекунад. Барои массиви як миллион унсур тақрибан 20 қадам кифоя аст. Бинобар ин ҳатто як миллион дархост ба массиви мураттаб фавран иҷро мешавад — баръакси ҳисоб кардани «чандто аз x калон» бо азназаргузаронӣ барои ҳар дархост.

np.unique низ тавассути мураттабсозӣ кор мекунад: массивро мураттаб мекунад ва баъд ҷойҳоеро меёбад, ки унсурҳои ҳамсоя фарқ мекунанд.

Хатоҳои маъмул

1. Ба ҷои @ — *. a * b — ҳосилҳои зарби ҷуфтӣ аст, на зарби матритсавӣ. Агар дар масъала «сатр ба сутун» бошад — @ лозим аст.

2. Андозаҳои номувофиқ ҳангоми @.

# хато: (2, 3) @ (2, 3) — якумӣ 3 сутун, дуюмӣ 2 сатр дорад
import numpy as np
a = np.ones((2, 3))
print(a @ a)

Натиҷа:

ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)

3. Система бе ҳалли ягона. Агар як муодила дигарашро такрор кунад, solve хато медиҳад:

# хато: муодилаи дуюм — якумӣ зарби 2 аст
import numpy as np
m = np.array([[1.0, 2.0], [2.0, 4.0]])
print(np.linalg.solve(m, np.array([3.0, 6.0])))

Натиҷа:

numpy.linalg.LinAlgError: Singular matrix

4. Интизор шудан, ки np.sort(a) a-ро тағйир медиҳад. Функсия массиви нав бармегардонад. Ё натиҷаро нигоҳ доред (a = np.sort(a)), ё усули a.sort()-ро даъват кунед.

5. searchsorted дар массиви мураттабнашуда. Функсия тартибро намесанҷад ва хомӯшона ҷавоби бемаъно медиҳад. Аввал np.sort, баъд ҷустуҷӯ.

Ёддошти кӯтоҳ

Чӣ лозим Чӣ тавр навиштан
зарби матритсавӣ a @ b ё np.dot(a, b) — (n, k) @ (k, m) → (n, m)
зарби унсур ба унсур a * b
матритсаи воҳидӣ np.eye(n)
детерминант / баръакс np.linalg.det(m), np.linalg.inv(m)
ҳалли системаи m @ x = b np.linalg.solve(m, b)
мураттабсозӣ (массиви нав) np.sort(a), бо камшавӣ np.sort(a)[::-1]
мураттабсозӣ дар ҷояш a.sort()
индексҳо бо тартиби афзуншавӣ np.argsort(a)
якеро аз рӯи дигаре мураттаб кардан names[np.argsort(scores)]
қиматҳои гуногун ва басомадҳо np.unique(a, return_counts=True)
қимати аз ҳама зиёд вохӯранда values[counts.argmax()]
чанд унсур < x np.searchsorted(s, x) (s мураттаб аст)
чанд унсур <= x np.searchsorted(s, x, side="right")