惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
aimingoo的专栏
aimingoo的专栏
B
Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志
Engineering at Meta
Engineering at Meta
IT之家
IT之家
V
Visual Studio Blog
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
A
About on SuperTechFans
博客园 - 聂微东
Blog — PlanetScale
Blog — PlanetScale
N
News and Events Feed by Topic
A
Arctic Wolf
WordPress大学
WordPress大学
小众软件
小众软件
C
CERT Recently Published Vulnerability Notes
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
D
Darknet – Hacking Tools, Hacker News & Cyber Security
F
Fortinet All Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Y
Y Combinator Blog
T
Threat Research - Cisco Blogs
Latest news
Latest news
Simon Willison's Weblog
Simon Willison's Weblog
Cyberwarzone
Cyberwarzone
S
Schneier on Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
L
Lohrmann on Cybersecurity
Stack Overflow Blog
Stack Overflow Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Privacy International News Feed
J
Java Code Geeks
Spread Privacy
Spread Privacy
宝玉的分享
宝玉的分享
I
Intezer
L
LangChain Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
G
GRAHAM CLULEY
博客园 - 叶小钗
博客园 - 三生石上(FineUI控件)
The GitHub Blog
The GitHub Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
N
News and Events Feed by Topic
AWS News Blog
AWS News Blog
Attack and Defense Labs
Attack and Defense Labs
Security Archives - TechRepublic
Security Archives - TechRepublic
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
NumPy с нуля: понятный гайд для тех, кто хочет в Data Science
enamored_poc · 2026-04-29 · via Все публикации подряд на Хабре

Время на прочтение13 мин

Охват и читатели482

Туториал

Привет, Хабр! Мы все любим Python за его лаконичность и читаемость. Стандартные списки (list) в нём — прекрасный и гибкий инструмент. В один список можно закинуть целые числа, строки, словари и даже другие списки. Удобно? Очень.

Но у этой гибкости есть суровая цена — производительность. Когда вы начинаете заниматься анализом данных, машинным обучением или просто сложными вычислениями, где счет идет на миллионы элементов, базовый Python показывает свою слабую сторону. Попытка перемножить два больших списка через стандартный цикл for заставит вас успеть заварить кофе (а то и выпить его), пока код выполняется.

Именно здесь на сцену выходит NumPy (Numerical Python) — библиотека, которая де-факто является фундаментом для всей экосистемы Data Science в Python. На ней базируются SciPy, Scikit-learn и, конечно, знаменитый Pandas (кстати, если после прочтения захотите освоить его на практике, заглядывайте на мой бесплатный курс «Pandas для анализа данных: Полный курс»). NumPy написан на языке C и работает с данными на совершенно другом уровне абстракции и скорости.

Что вас ждет в этой статье? Мы не будем лезть в академические дебри. Это практический гайд для первого знакомства. К концу статьи вы:

  1. Поймете, как устроены и как работают многомерные массивы.

  2. Научитесь элегантно извлекать и менять нужные данные.

  3. Познакомитесь с «магией» векторизации.

  4. Раз и навсегда отучитесь использовать циклы for там, где математика может сделать всё за доли секунды.

2. Зачем нужен NumPy, если есть списки Python?

Казалось бы, зачем учить новый инструмент, если в Python уже есть отличные встроенные списки? Проблема в том, что стандартный list создавался для максимальной гибкости, а за гибкость приходится платить ресурсами компьютера.

Давайте разберем три главные причины, почему в Data Science используют именно NumPy.

1. Скорость и то, как данные лежат в памяти

Список в Python — это, по сути, массив указателей. Каждый элемент списка — это отдельный полноценный объект в памяти (со своей метаинформацией, типом и счетчиком ссылок), и эти объекты могут быть разбросаны по оперативной памяти как угодно. Чтобы пройтись по списку, процессору нужно постоянно «прыгать» по памяти, собирая данные.

Массив в NumPy (ndarray) устроен иначе. Он хранит данные единым, непрерывным блоком в памяти (как массивы в языке C). Кроме того, все элементы в массиве NumPy имеют один и тот же тип данных. Процессор обожает такую структуру: он может загрузить огромный кусок массива в свой быстрый кэш и обработать его за доли секунды. А еще почти вся математика внутри NumPy написана на высокооптимизированном C, что позволяет обходить медленный интерпретатор Python.

2. Экономия оперативной памяти

Из-за того, что NumPy не хранит служебную информацию (тип объекта, размер и т.д.) для каждого числа, массивы занимают в разы меньше места. Если вы работаете с датасетом на несколько гигабайт, обычные списки Python могут просто «уронить» систему из-за нехватки памяти (OOM), тогда как NumPy спокойно всё переварит.

3. Удобство и векторизация

Чтобы умножить каждый элемент списка на 2, в чистом Python вам придется писать цикл или использовать генератор списков: [x * 2 for x in my_list].

NumPy вводит понятие векторизации — вы применяете математическую операцию ко всему массиву сразу, как будто это одно число. Никаких циклов писать не нужно, библиотека сделает всё под капотом.

Практика:

Давайте посмотрим на разницу в скорости на простейшем примере. Умножим миллион чисел на 2. Для замера времени в Jupyter Notebook / IPython есть удобная магическая команда %timeit.

import numpy as np

# Создаем миллион чисел
size = 1_000_000
python_list = list(range(size))
numpy_array = np.arange(size)

# 1. Классический Python (генератор списков)
%timeit [x * 2 for x in python_list]
# Вывод: 42.1 ms ± 1.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

# 2. NumPy (векторизация)
%timeit numpy_array * 2
# Вывод: 820 µs ± 15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Результат: NumPy выполнил ту же самую задачу примерно в 50 раз быстрее (820 микросекунд против 42 миллисекунд). И чем сложнее будут математические формулы, тем больше будет этот разрыв. В мире больших данных эта разница означает выбор между «модель обучится за пару часов» и «мы будем ждать до следующего года».

3. Установка и импорт

Начать работу с NumPy предельно просто. Если библиотека у вас еще не установлена, откройте терминал (или командную строку) и выполните стандартную команду:

pip install numpy

(Примечание: если вы используете сборку Anaconda, то NumPy там уже установлен «из коробки»).

Теперь библиотеку нужно подключить к вашему Python-скрипту или Jupyter Notebook. В мире Data Science существует жесткий, хоть и негласный стандарт того, как именно это делать:

import numpy as np

Почему именно np? Технически вы можете написать просто import numpy или даже import numpy as my_math. Код будет работать абсолютно так же. Но делать так не стоит.

Аббревиатура np — это общемировой стандарт. Вы будете обращаться к функциям этой библиотеки сотни раз за день, и сокращение до двух букв банально сэкономит вам время и сделает код визуально чище. К тому же, любой другой разработчик или аналитик, открыв ваш скрипт (или ответ на StackOverflow), моментально поймет, что np.array — это вызов метода NumPy. Пишите на общепринятом «диалекте»!

4. Создание массивов: от списков к генераторам

Любая работа в NumPy начинается с создания объекта ndarray (n-dimensional array). Есть два основных пути: превратить уже существующий список в массив или сгенерировать его с нуля инструментами самой библиотеки.

Способ 1: Из обычного списка Python

Функция np.array() — ваш главный мостик между чистым Python и NumPy.

# Одномерный массив (вектор)
vector = np.array([1, 2, 3])
print(vector)
# Вывод: [1 2 3]

# Двумерный массив (матрица) создается из списка списков
matrix = np.array([[1, 2], [3, 4]])
print(matrix)
# Вывод:
# [[1 2]
#  [3 4]]

Небольшая деталь: при выводе массивов NumPy на экран элементы разделяются пробелами, а не запятыми, как в классических списках. Это верный визуальный признак того, что перед вами именно массив NumPy.

Способ 2: Встроенные генераторы (Так делают профи)

В реальных Data Science задачах вы редко будете вбивать числа руками. Обычно вам нужно создать «заготовку» определенного размера или сгенерировать математическую последовательность.

1. Массивы из нулей и единиц Идеально подходят для создания массивов, которые вы планируете заполнять расчетными данными в процессе работы программы.

# Массив из 5 нулей
zeros = np.zeros(5)
print(zeros)
# Вывод: [0. 0. 0. 0. 0.]

# Матрица 2x3 из единиц (обратите внимание: размер передается в двойных скобках, как кортеж!)
ones = np.ones((2, 3))
print(ones)
# Вывод:
# [[1. 1. 1.]
#  [1. 1. 1.]]

(Точки после чисел означают, что по умолчанию NumPy создает числа с плавающей точкой — float64).

2. Последовательности: arange и linspace Если вам нужен аналог стандартного питоновского range(), используйте np.arange(). Его киллер-фича — умение работать с дробным шагом, чего встроенный range() не позволяет.

# От 0 до 10 (не включая 10) с шагом 2
seq = np.arange(0, 10, 2)
print(seq)
# Вывод: [0 2 4 6 8]

# Дробный шаг
float_seq = np.arange(0, 2, 0.5)
print(float_seq)
# Вывод: [0.  0.5 1.  1.5]

Но что, если вам нужно ровно 5 точек на отрезке от 0 до 1, и вам не хочется высчитывать шаг вручную? Для этого (и особенно для построения графиков в будущем) незаменим np.linspace():

# 5 равноудаленных точек от 0 до 1 (важно: здесь правая граница включена!)
points = np.linspace(0, 1, 5)
print(points)
# Вывод: [0.   0.25 0.5  0.75 1.  ]

3. Случайные числа Внутри NumPy есть мощнейший подмодуль random. Он — ваш лучший друг для генерации тестовых данных, симуляций или инициализации весов в нейросетях.

# Массив из 3 случайных чисел в диапазоне [0, 1)
random_arr = np.random.rand(3)
print(random_arr)
# Вывод: [0.5488135  0.71518937 0.60276338]

5. Анатомия массива: как понять, что перед вами?

В реальной работе вы редко будете создавать массивы вручную. Чаще всего вы будете загружать их из файлов (картинки, CSV-таблицы) или получать на выходе из других функций. И первое, что нужно сделать с незнакомым массивом — «осмотреть» его.

У каждого объекта ndarray есть встроенные атрибуты (обратите внимание: они вызываются без скобок на конце, так как это свойства, а не функции). Давайте разберем три самых главных.

import numpy as np

# Создадим подопытную матрицу 2x3
matrix = np.array([[1, 2, 3], 
                   [4, 5, 6]])

1. ndim: количество измерений (осей)

Показывает, в каком пространстве живет наш массив.

  • 1 — одномерный массив (вектор/список).

  • 2 — двумерный массив (матрица/таблица).

  • 3 — трехмерный массив (например, цветная картинка, где есть высота, ширина и три цветовых канала RGB).

print(matrix.ndim)
# Вывод: 2 (у нас двумерная матрица)

2. shape: форма массива (Самый важный атрибут!)

Если ndim говорит просто «это таблица», то shape дает точные габариты. Он возвращает кортеж с размерами массива по каждой оси. Для двумерной матрицы это всегда (строки, столбцы).

Запомните этот атрибут! 90% ошибок новичков в NumPy и нейросетях связаны с тем, что формы массивов не совпадают при математических операциях (ошибки вида shape mismatch). Проверка .shape — это ваш главный инструмент отладки.

print(matrix.shape)
# Вывод: (2, 3) — то есть 2 строки и 3 столбца

3. dtype: тип данных

Мы уже упоминали, что массивы NumPy хранятся в памяти единым блоком. Чтобы это работало, все элементы массива должны быть строго одного типа. Вы не можете положить в один массив NumPy число, строку и булево значение, как это позволяет делать обычный list.

print(matrix.dtype)
# Вывод: int64 (64-битные целые числа)

А что будет, если попытаться обмануть NumPy? Если вы скормите ему список с разными типами данных, он не выдаст ошибку, но сделает upcasting — приведет все элементы к самому «вместительному» типу (обычно к строкам).

# Пытаемся смешать число 1, число с плавающей точкой и строку
mixed_array = np.array([1, 2.5, "Хабр"])

print(mixed_array)
# Вывод: ['1' '2.5' 'Хабр'] 
# NumPy превратил все числа в строки, чтобы сохранить однородность!

print(mixed_array.dtype)
# Вывод: <U32 (строки Unicode)

Из-за этого массив потеряет всю свою математическую суперсилу, поэтому за однородностью данных нужно следить.

6. Индексация и срезы: как достать нужные данные

Создать массив — полдела. В реальных задачах (например, при подготовке данных для машинного обучения) вам постоянно придется «вытаскивать» из него определенные строки, столбцы или конкретные значения для анализа.

Одномерные массивы: старая добрая классика

Здесь для вас не будет сюрпризов. Если вы умеете работать со срезами в обычных списках Python, вы уже умеете работать с одномерными массивами NumPy. Индексы начинаются с нуля, срезы работают по правилу [старт:стоп:шаг].

arr = np.array([10, 20, 30, 40, 50])

print(arr[0])     # Вывод: 10 (первый элемент)
print(arr[-1])    # Вывод: 50 (последний элемент)
print(arr[1:4])   # Вывод: [20 30 40] (срез с 1-го по 3-й индекс включительно)

Многомерные массивы: прощайте, вложенные скобки!

Главное отличие начинается, когда мы переходим к матрицам. В классическом Python, чтобы достать элемент из списка списков, вы бы написали так: matrix[0][1]. В NumPy это считается устаревшим подходом и работает медленнее.

NumPy использует элегантный синтаксис через запятую: [строка, столбец].

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

# Достаем число 6 (индекс строки 1, индекс столбца 2)
print(matrix[1, 2])  
# Вывод: 6

Мощь срезов в 2D: Настоящая сила синтаксиса с запятой раскрывается в срезах. Представьте, что это таблица (например, выгрузка из базы данных), и вам нужно забрать весь первый столбец (скажем, только имена пользователей). В чистом Python пришлось бы писать цикл. В NumPy двоеточие : означает «взять всё по этой оси».

# Берем ВСЕ строки (:), но только нулевой столбец (0)
first_column = matrix[:, 0]
print(first_column)
# Вывод: [1 4 7]

# Берем подматрицу (первые две строки и последние два столбца)
sub_matrix = matrix[0:2, 1:3]
print(sub_matrix)
# Вывод:
# [[2 3]
#  [5 6]]

Булева индексация

А теперь пристегнитесь, мы переходим к настоящей магии. Допустим, у вас есть массив с тысячей чисел, и вам нужно выбрать только те, которые больше 50.

В обычном Python вы бы написали что-то вроде [x for x in data if x > 50]. NumPy позволяет делать это без циклов, невероятно быстро и лаконично, с помощью так называемых «масок». Вы можете передать условие прямо в квадратные скобки.

data = np.array([12, 85, 34, 99, 5, 67, 42])

# Сначала посмотрим, что делает само условие
print(data > 50)
# Вывод: [False  True False  True False  True False]
# (NumPy вернул массив из True/False для каждого элемента)

# А теперь передаем это условие внутрь квадратных скобок в качестве индекса!
result = data[data > 50]
print(result)
# Вывод: [85 99 67]

Почему это вызывает восторг? Потому что вы можете легко комбинировать условия фильтрации данных! Нужно найти числа больше 20, но меньше 50? Легко. Главное — использовать побитовые операторы & (и) или | (или) вместо классических питоновских and/or и обязательно брать условия в круглые скобки:

# Числа от 20 до 50
print(data[(data > 20) & (data < 50)])
# Вывод: [34 42]

Это не просто короче в написании. Под капотом фильтрация происходит на уровне языка C, что критически важно при обработке датасетов на миллионы строк. Именно на этой механике строится львиная доля фильтрации данных в библиотеке Pandas!

7. Базовая математика и Векторизация

Мы подошли к главной причине, по которой NumPy стал стандартом де-факто в вычислениях. Это векторизация — способность применять математические операции сразу ко всему массиву, не прибегая к медленным циклам for уровня Python.

Операции с числами (скалярами)

Представьте, что у вас есть массив цен в магазине, и вам нужно добавить к каждой цене 100 рублей. В стандартном Python пришлось бы писать цикл или генератор. В NumPy вы просто прибавляете число к массиву, как будто это единичный объект.

prices = np.array([150, 300, 450, 1000])

# Прибавляем 100 к каждому элементу
new_prices = prices + 100
print(new_prices)
# Вывод: [ 250  400  550 1100]

# Можно использовать любые операторы: -, *, /, **, //
discounted = prices * 0.8  # скидка 20%
print(discounted)
# Вывод: [120. 240. 360. 800.]

Код читается как обычное предложение на английском языке, а выполняется на скоростях языка C.

Операции между массивами

Если у вас есть два массива одинаковой формы, вы можете применять арифметику прямо к ним. Операции будут выполняться строго поэлементно (первый элемент с первым, второй со вторым и т.д.).

revenue = np.array([1000, 2500, 3000]) # Выручка
costs = np.array([400, 900, 1200])     # Затраты

# Считаем чистую прибыль
profit = revenue - costs
print(profit)
# Вывод: [ 600 1600 1800]

Важное замечание для тех, кто помнит линейную алгебру: Символ * в NumPy означает именно поэлементное умножение, а не классическое матричное (строка на столбец). Если вам вдруг понадобится умножить матрицы по правилам линейной алгебры, для этого используется оператор @ (или функция np.dot()).

Broadcasting (Транслирование)

Возникает логичный вопрос: «А что будет, если попытаться сложить массивы разной формы?».

По умолчанию математика работает только для массивов одинакового размера. Но в NumPy есть Broadcasting (транслирование). Если формы массивов не совпадают, NumPy попытается «растянуть» (продублировать) меньший массив так, чтобы он совпал по размерам с большим.

На самом деле, вы уже видели Broadcasting в действии минуту назад! Когда мы писали prices + 100, NumPy виртуально «растянул» число 100 в массив [100, 100, 100, 100], и только потом выполнил сложение. Это работает и для более сложных структур (например, когда к матрице прибавляют одномерный вектор), что позволяет писать невероятно компактный код без лишних выделений памяти.

8. Изменение формы (Reshape)

В мире данных информация редко приходит к нам в идеальном виде. Часто бывает так, что вы загружаете изображение, а библиотека считывает его как длинную одномерную «колбасу» из пикселей, хотя для работы вам нужна двумерная матрица (высота и ширина).

Здесь на помощь приходит метод .reshape(). Он позволяет перестраивать геометрию массива на лету, не меняя и не копируя сами данные в памяти. Главное (и единственное) правило — общее количество элементов должно оставаться неизменным.

Как свернуть вектор в матрицу

Давайте возьмем одномерный массив из 12 чисел и превратим его в матрицу 3х4 (3 строки, 4 столбца).

# Создаем одномерный массив от 0 до 11
arr = np.arange(12)
print(arr)
# Вывод: [ 0  1  2  3  4  5  6  7  8  9 10 11]

# Меняем форму на 3 строки и 4 столбца
matrix = arr.reshape(3, 4)
print(matrix)
# Вывод:
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

Если вы попытаетесь сделать .reshape(3, 5) из 12 элементов, NumPy справедливо выдаст ошибку ValueError. Математика неумолима: 3 умножить на 5 будет 15, а у нас в наличии только 12 чисел. Нехватка или переизбыток данных не допускается.

Лайфхак с минус единицей (-1)

А теперь представьте реальную задачу из машинного обучения. У вас есть огромный массив данных, и вам нужно переформатировать его так, чтобы получилось ровно 2 столбца. Вы совершенно не хотите (или не можете, если размер датасета меняется) вручную делить общее количество элементов на 2, чтобы узнать количество строк.

Для этого в NumPy есть гениальный трюк: вместо одного из измерений вы можете передать -1. Это сигнал для NumPy: «Я точно знаю, что столбцов должно быть 2, а количество строк посчитай за меня, исходя из длины массива».

# Просим сделать 2 столбца, а строки (-1) пусть NumPy высчитывает сам
auto_matrix = arr.reshape(-1, 2)

print(auto_matrix)
# Вывод:
# [[ 0  1]
#  [ 2  3]
#  [ 4  5]
#  [ 6  7]
#  [ 8  9]
#  [10 11]]

# Проверяем форму — NumPy сам понял, что строк должно быть 6
print(auto_matrix.shape)
# Вывод: (6, 2)

Этот лайфхак с -1 сэкономит вам уйму времени и нервов при подготовке данных (особенно когда вы начнете вытягивать массивы признаков перед подачей их в модели Scikit-learn или слои нейросетей). Вы можете использовать -1 на любой позиции, но, разумеется, только один раз за один вызов reshape (иначе уравнение будет иметь бесконечное множество решений).

9. Статистика и Агрегация: сводим данные воедино

Обычно мы загружаем данные в массивы не просто так, а чтобы найти в них какие-то закономерности. Кто потратил больше всего денег? Каков средний чек? Сколько всего товаров продано?

В чистом Python для этого пришлось бы писать циклы с кучей промежуточных переменных-счетчиков. NumPy позволяет получить базовую статистику вызовом одного метода.

Глобальная статистика (по всему массиву)

Если вызвать методы агрегации без аргументов, NumPy схлопнет весь массив (даже если он многомерный) до одного числа.

sales = np.array([[10, 20, 30], 
                  [40, 50, 60]])

print(sales.sum())   # Сумма всех элементов: 210
print(sales.mean())  # Среднее арифметическое: 35.0
print(sales.max())   # Максимальное значение: 60
print(sales.min())   # Минимальное значение: 10

Магия осей: axis=0 и axis=1

А что, если наша таблица sales — это продажи трех разных магазинов (столбцы) за два дня (строки)? Нам вряд ли нужна общая сумма. Мы захотим узнать либо выручку за каждый день, либо общую выручку каждого магазина.

Для этого в методы агрегации передается параметр axis (ось). Это самая частая точка путаницы для новичков, поэтому давайте разберем её визуально.

Представьте двумерную матрицу. У неё есть два направления движения:

  • axis=0 — движение сверху вниз (по столбцам).

  • axis=1 — движение слева направо (по строкам).

       axis=1 (по строкам →)
         [ 10,  20,  30 ]
       ↓ [ 40,  50,  60 ]
     axis=0 
  (по столбцам)

Правило большого пальца: параметр axis указывает, какую ось мы схлопываем (уничтожаем), чтобы получить результат.

  1. Если мы хотим посчитать сумму продаж каждого магазина (сумма по столбцам), мы должны «схлопнуть» строки. Двигаемся сверху вниз — это axis=0.

# Сумма по вертикали (по столбцам)
print(sales.sum(axis=0))  
# Вывод: [50 70 90] 
# (10+40, 20+50, 30+60)
  1. Если мы хотим посчитать сумму продаж за каждый день (сумма по строкам), мы «схлопываем» столбцы. Двигаемся слева направо — это axis=1.

# Сумма по горизонтали (по строкам)
print(sales.sum(axis=1))  
# Вывод: [ 60 150] 
# (10+20+30, 40+50+60)

Это правило работает абсолютно для всех статистических функций NumPy, а в будущем оно перейдет вместе с вами и в библиотеку Pandas, где логика axis=0 и axis=1 точно такая же.

10. Заключение: фундамент заложен

Вот мы и разобрали базу NumPy. Мы прошли путь от медленной «черепахи» классических списков до скорости оптимизированного C-кода. Кратко резюмируем, что теперь есть в вашем арсенале. Вы умеете:

  • Быстро создавать массивы нужного размера с помощью генераторов (np.zeros, np.arange).

  • Понимать структуру незнакомых данных с помощью главного атрибута .shape.

  • Элегантно нарезать матрицы и фильтровать данные по сложным условиям с помощью булевых масок

  • Менять геометрию данных на лету через .reshape() и лайфхак с -1.

  • Использовать векторизацию и собирать статистику по нужным осям (axis) — и всё это без единого цикла for!

Что дальше? Куда развиваться?

NumPy — это невероятно мощный, но всё-таки низкоуровневый математический инструмент. Если вы целитесь в Data Science или аналитику, ваш следующий логичный шаг — библиотека Pandas. Она целиком построена поверх NumPy и превращает «голые» массивы в удобные таблицы (DataFrames) с названиями колонок и индексов. Это как Excel на стероидах для программистов.

Анонсы новых статей, полезные материалы, а так же если в процессе у вас возникнут сложности, обсудить их или задать вопрос по этой статье можно в моём Telegram-сообществе. Смело заходите, если что-то пойдет не так, — постараемся разобраться вместе.

А чтобы сухие цифры превращались в понятные инсайты, обязательно познакомьтесь с библиотеками для визуализации, такими как Matplotlib или Seaborn. Они бесшовно работают с массивами NumPy и позволяют строить графики любой сложности буквально в пару строк кода.