Ҳисоббарорӣ бе давраҳо
Ин барои чӣ лозим аст
Баҳои миёнаи синф, даромади мағоза дар як ҳафта, гармтарин рӯзи моҳ — ҳамаи ин як адади аз бисёр ададҳо ҳисобшуда аст. «Ҳамаи ҳароратҳоро ба Фаренгейт гузарондан» ё «аз ҳар баҳо миёнаро тарҳ кардан» бошад — як амали якхела болои ҳар унсур аст.
Дар numpy ҳарду бе давраҳо иҷро мешаванд: амалҳо ба тамоми массив якбора татбиқ мешаванд, барои натиҷаҳо бошад усулҳои тайёри sum, mean, max ва дигарҳо ҳастанд. Ин векторизатсия ном дорад — сабаби асосие, ки numpy-ро дар ҳама ҷое, ки ададҳо бисёранд, истифода мебаранд. Дар ин мавзӯъ арифметика болои массивҳо, агрегатҳо, параметри axis барои ҷадвалҳо ва broadcasting — қоидаеро, ки numpy бо массивҳои андозаашон гуногун кор мекунад, баррасӣ мекунем.
Чаро бе давраҳо тезтар аст
Давраи Python ва numpy-ро дар як миллион адад муқоиса мекунем:
import time
import numpy as np
nums = list(range(1_000_000))
start = time.perf_counter()
squares = [x * x for x in nums]
loop_time = time.perf_counter() - start
a = np.arange(1_000_000)
start = time.perf_counter()
squares = a * a
numpy_time = time.perf_counter() - start
print(loop_time > numpy_time)
Натиҷа:
True
Дар компютери одӣ давра тақрибан 0,05 сония вақт мегирад, numpy бошад тақрибан 0,001: фарқ даҳҳо маротиба. Дар маълумоти калон ин дақиқаҳо муқобили сонияҳост.
Арифметика унсур ба унсур
Агар ду массиви дарозиашон якхела-ро ҷамъ, зарб ё муқоиса кунед, амал ҷуфт-ҷуфт иҷро мешавад: якум бо якум, дуюм бо дуюм ва ғайра.
import numpy as np
a = np.array([1, 4, 9])
b = np.array([10, 20, 30])
print(a + b)
print(b - a)
print(a * b)
print(b / a)
print(b // 7, b % 7)
Натиҷа:
[11 24 39]
[ 9 16 21]
[ 10 80 270]
[10. 5. 3.33333333]
[1 2 4] [3 6 2]
Ҳамаи амалгарҳои шинос кор мекунанд: + - * / // % **. Тақсими / ҳамеша ададҳои касрӣ медиҳад, мисли Python-и одӣ.
Мисол аз ҳаёт — гузарондани ҳарорат аз Селсий ба Фаренгейт бо формулаи F = C · 9/5 + 32:
import numpy as np
celsius = np.array([-10, 0, 25, 37])
print(celsius * 9 / 5 + 32)
Натиҷа:
[14. 32. 77. 98.6]
Функсияҳои математикии numpy
Функсияҳои модули math (math.sqrt, math.sin) танҳо бо як адад кор карда метавонанд. numpy версияҳои худро дорад, ки ба ҳар унсур татбиқ мешаванд:
import numpy as np
a = np.array([1, 4, 9])
print(np.sqrt(a))
print(np.abs(np.array([-2, 3, -5])))
print(np.round(np.array([1.234, 5.678]), 1))
print(np.exp(np.array([0, 1])))
Натиҷа:
[1. 2. 3.]
[2 3 5]
[1.2 5.7]
[1. 2.71828183]
Зарурӣтаринҳо: np.sqrt (реша), np.abs (модул), np.round (яклухткунӣ), np.exp ва np.log (экспонента ва логарифм), np.sin, np.cos, np.floor ва np.ceil (яклухткунӣ ба поён ва боло). Чунин функсияҳоро функсияҳои универсалӣ (ufunc) меноманд.
Агрегатҳо: як адад аз массив
Агрегат — функсияест, ки массивро ба як адад «мепечонад»: ҳосили ҷамъ, миёна, максимум.
import numpy as np
t = np.array([12, 15, 9, 20, 14])
print(t.sum())
print(t.mean())
print(t.min(), t.max())
print(t.argmin(), t.argmax())
print(round(t.std(), 3))
Натиҷа:
70
14.0
9 20
2 3
3.633
| Усул | Чиро ҳисоб мекунад |
|---|---|
sum() |
ҳосили ҷамъ |
mean() |
миёнаи арифметикӣ |
min(), max() |
хурдтарин ва калонтарин |
argmin(), argmax() |
мавқеи (индекс аз сифр) хурдтарин ва калонтарин |
std() |
инҳирофи стандартӣ — қиматҳо дар атрофи миёна чӣ қадар пароканда ҳастанд |
prod() |
ҳосили зарб |
argmax махсусан вақте муфид аст, ки на «чӣ қадар», балки «кӣ» донистан лозим: дар кадом рӯз ҳарорат аз ҳама баланд буд, кадом хонанда беҳтарин аст. Агар якчанд максимуми якхела бошад, мавқеи якумаш бармегардад:
import numpy as np
scores = np.array([7, 9, 4, 9])
print(scores.argmax())
Натиҷа:
1
Ҳамин агрегатҳо дар шакли функсияҳо низ ҳастанд: np.sum(t), np.mean(t), np.max(t) — натиҷа якхела аст.
Меҳвари axis дар ҷадвалҳо
Дар ҷадвал агрегатро бо се роҳ ҳисоб кардан мумкин: аз рӯи тамоми ҷадвал, аз рӯи ҳар сутун ё аз рӯи ҳар сатр. Инро бо параметри axis интихоб мекунанд.
Бигзор дар ҷадвал фурӯши ду мағоза дар се рӯз бошад: сатр — мағоза, сутун — рӯз.
import numpy as np
sales = np.array([[5, 3, 8],
[2, 7, 4]])
print(sales.sum())
print(sales.sum(axis=0))
print(sales.sum(axis=1))
Натиҷа:
29
[ 7 10 12]
[16 13]
- бе
axis— ҳосили ҷамъи ҳамаи ададҳо; axis=0— ҳосили ҷамъ аз рӯи ҳар сутун (барои ҳар рӯз):5+2,3+7,8+4;axis=1— ҳосили ҷамъ аз рӯи ҳар сатр (барои ҳар мағоза):5+3+8,2+7+4.
Чӣ тавр дар хотир нигоҳ доштан: рақами меҳвар — самтест, ки мепечад. Меҳвари 0 аз боло ба поён меравад (аз рӯи сатрҳо); ҳангоми axis=0 сатрҳо якҷоя ҷамъ мешаванд ва барои ҳар сутун як адад мемонад. Меҳвари 1 аз чап ба рост меравад; ҳангоми axis=1 сутунҳо мепечанд ва барои ҳар сатр як адад мемонад.
axis дар ҳамаи агрегатҳо кор мекунад:
import numpy as np
sales = np.array([[5, 3, 8],
[2, 7, 4]])
print(sales.mean(axis=1))
print(sales.max(axis=0))
print(sales.argmax(axis=1))
Натиҷа:
[5.33333333 4.33333333]
[5 7 8]
[2 1]
Сатри охирин чунин хонда мешавад: дар мағозаи якум беҳтарин рӯз — бо индекси 2, дар дуюм — бо индекси 1.
Broadcasting: массивҳои андозаашон гуногун
Агар ҷадвалро бо адад ё бо сатр ҷамъ кунем, чӣ мешавад? numpy кӯшиш мекунад массиви хурдтарро то андозаи калонтар «дароз кунад». Ин қоида broadcasting (аз англ. «паҳн кардан») ном дорад.
Ҷадвал ва адад — адад ба ҳар унсур татбиқ мешавад:
import numpy as np
m = np.array([[1, 2, 3],
[4, 5, 6]])
print(m * 10)
Натиҷа:
[[10 20 30]
[40 50 60]]
Ҷадвал ва сатр — сатри дарозиаш 3 ба ҷадвали се сутуна мувофиқ меояд ва ба ҳар сатри ҷадвал татбиқ мешавад:
import numpy as np
m = np.array([[1, 2, 3],
[4, 5, 6]])
print(m - np.array([1, 1, 1]))
print(m * np.array([1, 0, -1]))
Натиҷа:
[[0 1 2]
[3 4 5]]
[[ 1 0 -3]
[ 4 0 -6]]
Ҷадвал ва сутун. Барои он ки ҳар сатр адади худро дошта бошад, сутун лозим аст — массиви шакли (2, 1). Масалан, аз ҳар сатр миёнаи худашро тарҳ мекунем:
import numpy as np
m = np.array([[1, 2, 3],
[4, 5, 6]])
row_means = m.mean(axis=1)
print(row_means, row_means.shape)
col = row_means.reshape(2, 1)
print(m - col)
Натиҷа:
[2. 5.] (2,)
[[-1. 0. 1.]
[-1. 0. 1.]]
Қоидаи мувофиқат: андозаҳо аз охир муқоиса мешаванд. Ду андоза мувофиқанд, агар баробар бошанд ё яке аз онҳо 1 бошад (як дароз карда мешавад). Шаклҳои (2, 3) ва (3,) — мувофиқанд, (2, 3) ва (2, 1) — мувофиқанд, (2, 3) ва (2,) бошад не: андозаҳои охирини 3 ва 2 баробар нестанд.
Усули маъмул — аз ҳар сутун миёнаи онро тарҳ кардан, то бинем, ки қиматҳо дар куҷо аз муқаррарӣ баландтар ё пасттаранд:
import numpy as np
grades = np.array([[80, 90],
[70, 100],
[60, 95]])
print(grades.mean(axis=0))
print(grades - grades.mean(axis=0))
Натиҷа:
[70. 95.]
[[ 10. -5.]
[ 0. 5.]
[-10. 0.]]
Дар ин ҷо миёна аз рӯи сутунҳо шакли (2,) дорад — аз охир ба ҷадвали (3, 2) мувофиқ меояд, бинобар ин аз ҳар сатр тарҳ мешавад.
Хондани ҷадвал аз воридот
Ҷадвалро аз маълумоти воридотӣ дарҳол ба массив хондан қулай аст: n сатрро хонед, ҳар кадомро бо split() ҷудо кунед, dtype бошад матнро ба ададҳо табдил медиҳад.
import numpy as np
lines = ["1 2 3", "4 5 6"]
a = np.array([line.split() for line in lines], dtype=float)
print(a)
print(a.shape)
Натиҷа:
[[1. 2. 3.]
[4. 5. 6.]]
(2, 3)
Дар масъала ба ҷои рӯйхати тайёри lines input() хоҳад буд:
n, m = map(int, input().split())
a = np.array([input().split() for _ in range(n)], dtype=float)
Дар дохил чӣ тавр кор мекунад
Вақте шумо a + b менависед, Python унсурҳоро худаш намегузарад — вай ҳарду массивро ба дохили numpy мефиристад, ки дар он ҷо давраи компилятсияшуда бо забони C аз болои қисмҳои яклухти хотира мегузарад. Чунин давра дар ҳар қадам навъҳоро намесанҷад ва метавонад фармонҳои махсуси протсессорро истифода барад, ки якбора якчанд ададро коркард мекунанд.
Broadcasting дар ин ҳолат маълумотро нусхабардорӣ намекунад: numpy дар хотира ҷадвали дарозшуда аз сатрҳои якхела намесозад, балки як сатрро бисёр маротиба мехонад. Бинобар ин m - row ҳатто барои ҷадвали азим тез кор мекунад.
Монандӣ барои axis: ҷадвал — дастаи варақҳо. axis=0 — варақҳоро болои ҳам гузоштан ва аз байнашон нигоҳ кардан (натиҷа — як варақ-сатр аз рӯи сутунҳо), axis=1 — ҳар варақро ба як нуқта фишурдан.
Хатоҳои маъмул
1. Ба ҷои np math-ро истифода бурдан. Функсияҳои math бо массив кор карда наметавонанд:
# хато: math.sqrt як ададро қабул мекунад
import math
import numpy as np
print(math.sqrt(np.array([1, 4, 9])))
Натиҷа:
TypeError: only length-1 arrays can be converted to Python scalars
Ислоҳ: np.sqrt(...).
2. axis=0 ва axis=1-ро омехтан. Агар натиҷа «барои ҳар сутун як адад» лозим бошад — ин axis=0 аст. Худро аз рӯи шакли натиҷа санҷед: дар ҷадвали (n, m) натиҷа бо axis=0 дарозии m, бо axis=1 — дарозии n дорад.
3. Андозаҳои номувофиқ. Сатри дарозиаш 2-ро ба ҷадвали се сутуна татбиқ кардан мумкин нест:
# хато: шаклҳои (2, 3) ва (2,) номувофиқанд
import numpy as np
m = np.arange(6).reshape(2, 3)
print(m - np.array([1, 2]))
Натиҷа:
ValueError: operands could not be broadcast together with shapes (2,3) (2,)
Ислоҳ: агар барои ҳар сатр адади худ лозим бошад, сутун созед — np.array([1, 2]).reshape(2, 1).
4. Пуршавии ададҳои бутун. Дар сайт numpy ададҳои бутунро бо пешфарз ҳамчун int32 (то ~2·10⁹) нигоҳ медорад. Ҳосили ҷамъи ададҳои калон хомӯшона «печида» нодуруст мешавад:
import numpy as np
a = np.array([2_000_000_000, 2_000_000_000], dtype=np.int32)
print(a.sum())
print(a.astype(np.int64).sum())
Натиҷа:
-294967296
4000000000
Ислоҳ: агар ададҳо ё ҳосилҳои ҷамъ аз 2 · 109 калон шуда тавонанд, маълумотро бо dtype=np.int64 хонед.
5. argmax ва max-ро омехтан. max() — худи қимати калонтарин, argmax() — индекси он. Дар масъалаҳое, ки рақамгузорӣ аз як аст, ба индекс 1 илова кардан лозим.
Ёддошти кӯтоҳ
| Чӣ лозим | Чӣ тавр навиштан |
|---|---|
| арифметикаи унсур ба унсур | a + b, a * 2, a ** 2, a / b |
| функсияҳо барои ҳар унсур | np.sqrt, np.abs, np.round(a, k), np.exp, np.log |
| ҳосили ҷамъ, миёна | a.sum(), a.mean() |
| минимум, максимум | a.min(), a.max() |
| мавқеи минимум / максимум | a.argmin(), a.argmax() |
| парокандагӣ | a.std() |
| аз рӯи сутунҳои ҷадвал | m.sum(axis=0) — дарозӣ = шумораи сутунҳо |
| аз рӯи сатрҳои ҷадвал | m.sum(axis=1) — дарозӣ = шумораи сатрҳо |
| адади худ ба ҳар сатр | m - v.reshape(-1, 1) |
| адади худ ба ҳар сутун | m - v (дарозии v = шумораи сутунҳо) |
| ҷадвал аз воридот | np.array([input().split() for _ in range(n)], dtype=float) |
| ададҳои бутуни калон | dtype=np.int64 |