Сохранение, структуры и мост к pandas: что дальше

Финальный урок: как сохранять и загружать массивы, что такое структурированные массивы, как NumPy связан с pandas, и итоговый чек-лист эффективного кода.

Формат .npy — бинарный формат NumPy для сохранения одного массива со всеми метаданными (форма, dtype); читается мгновенно и без потерь, в отличие от текстовых форматов.

Сохранение и загрузка: save, load, savez

Сохранять массивы в текст (CSV) — расточительно: теряется точность, dtype, форма, файл занимает много места и медленно парсится. NumPy предлагает бинарные форматы. np.save пишет один массив в файл .npy, np.load читает его обратно — быстро и без потерь. Для нескольких массивов есть np.savez (несжатый) и np.savez_compressed (сжатый), создающие архив .npz.

import numpy as np
a = np.arange(12).reshape(3, 4)

np.save('data.npy', a)              # один массив
loaded = np.load('data.npy')        # читаем обратно
print(np.array_equal(a, loaded))    # True — без потерь

# Несколько массивов в один архив
np.savez('bundle.npz', train=a, labels=np.array([0, 1, 0]))
bundle = np.load('bundle.npz')
print(bundle['train'].shape, bundle['labels'])

Вывод:

True
(3, 4) [0 1 0]

Правило: для промежуточных результатов и быстрого обмена между скриптами используйте .npy/.npz. Текстовые np.savetxt/np.loadtxt уместны лишь когда файл должен читаться человеком или другой программой, не знающей NumPy.

Бинарный формат против текстового: что выбрать

Выбор формата хранения важнее, чем кажется. Текстовые форматы (CSV, обычный txt) хороши одним — их читает человек и любая программа. Но за это вы платите дорого: число вроде 0.1 при записи в текст и обратном чтении может потерять точность в последних знаках; dtype и форма не сохраняются (всё парсится как строки и переводится в float); файл занимает в разы больше места; чтение медленное, потому что каждое число нужно разобрать из текста. Бинарные форматы NumPy (.npy, .npz) лишены этих недостатков: они сохраняют точные байты чисел, всю форму и тип, читаются почти мгновенно (это просто загрузка блока в память) и компактны. Правило выбора простое: если файл предназначен для вашего же кода или обмена между Python-скриптами — берите бинарный .npy/.npz. Текстовый формат оставьте для случаев, когда данные должен прочитать человек или внешний инструмент, не знающий NumPy. Отдельно стоит savez_compressed для архивов: он сжимает данные, экономя место на диске ценой времени на распаковку, — полезно для редко читаемых, но объёмных наборов.

Структурированные массивы: разные типы в одной строке

Обычный ndarray однороден — один dtype на всё. Но иногда нужны «записи» с полями разных типов, как строки таблицы: имя (строка), возраст (целое), рост (вещественное). Для этого есть структурированные массивы — с составным dtype, описывающим именованные поля. К полям обращаются по имени.

import numpy as np
dt = np.dtype([('name', 'U10'), ('age', 'i4'), ('height', 'f4')])
people = np.array([('Аня', 25, 1.65),
                   ('Боря', 30, 1.80)], dtype=dt)

print(people['name'])       # доступ к полю по имени
print(people['age'].mean()) # агрегации по полю
print(people[0])            # одна запись целиком

Вывод:

['Аня' 'Боря']
27.5
('Аня', 25, 1.65)

Структурированные массивы полезны для чтения бинарных форматов с фиксированной схемой. Но для повседневной работы с табличными данными смешанных типов есть инструмент удобнее — pandas.

Когда нужны структурированные массивы, а когда pandas

Структурированные массивы — мощный, но нишевый инструмент. Их главная область — чтение и запись бинарных форматов с фиксированной схемой записи: файлы научных приборов, сетевые пакеты, бинарные форматы данных, где каждая «запись» имеет жёстко заданную структуру полей разных типов. Здесь структурированный dtype точно описывает раскладку байтов, и NumPy читает такие данные эффективно и без копирования. Но для анализа разнородных табличных данных — фильтрации, группировки, соединения таблиц, работы с пропусками и датами — структурированные массивы неудобны: у них нет высокоуровневых операций, которые есть в pandas. Поэтому правило простое: структурированные массивы — для низкоуровневого ввода-вывода бинарных форматов; pandas — для анализа таблиц. Не пытайтесь строить аналитику на структурированных массивах, повторяя то, что pandas делает из коробки. И наоборот, не тяните pandas туда, где нужно лишь быстро прочитать бинарный буфер фиксированной структуры. Каждый инструмент хорош в своей нише, и понимание границы между ними экономит силы.

Мост к pandas: где заканчивается NumPy

NumPy — это про однородные числовые массивы и скорость. Как только данные становятся табличными и разнородными (колонки разных типов, имена столбцов, метки строк, пропуски, группировки, работа с датами и категориями) — это территория pandas. И связь между ними прямая: pandas построен на NumPy. Каждая колонка DataFrame — это, по сути, NumPy-массив, а у DataFrame и Series есть атрибут .values (или .to_numpy()), отдающий данные как ndarray.

Все навыки этого курса переносятся в pandas: векторизация, broadcasting, маски, агрегации с axis — там работают так же, потому что под капотом тот же NumPy. Поэтому изучение NumPy — это фундамент, на котором стоит весь остальной анализ данных. Типичный поток: загрузить и подготовить таблицу в pandas, при необходимости вынуть числовой массив через .to_numpy() и скормить его в scikit-learn или собственные вычисления на NumPy.

Берите NumPy, когдаБерите pandas, когда
однородные числовые массивытаблицы со столбцами разных типов
матричные/тензорные вычислениянужны метки строк/столбцов
максимальная скорость и контрольгруппировки, джойны, временные ряды
основа для ML-библиотекочистка и разведка данных

NumPy как фундамент: куда идти дальше

Завершая курс, полезно увидеть NumPy в контексте всей экосистемы. Вы освоили не изолированную библиотеку, а общий язык числовых вычислений Python, на котором построено почти всё остальное. pandas добавляет поверх NumPy метки, разнородные колонки, группировки и работу с пропусками — но под капотом это те же массивы, и ваши навыки векторизации и масок там работают без изменений. matplotlib рисует графики из NumPy-массивов. scikit-learn принимает массивы признаков и возвращает массивы предсказаний; все его алгоритмы говорят на языке NumPy. SciPy расширяет NumPy продвинутой математикой — оптимизацией, интегрированием, обработкой сигналов, разреженными матрицами. Фреймворки глубокого обучения копируют интерфейс NumPy для своих тензоров, добавляя автоматическое дифференцирование и работу на GPU. Иными словами, NumPy — это корень дерева, из которого растёт весь научный и аналитический Python. Каждая концепция, которую вы здесь усвоили — ndarray и его память, views и copies, broadcasting, векторизация, оси и агрегации — переносится дальше почти буквально. Поэтому время, вложенное в глубокое понимание NumPy, окупается во всём, что вы будете делать с данными на Python. Это лучшая инвестиция в фундамент.

Итоговый чек-лист производительности

Соберём принципы всего курса в один список. Если код на NumPy медленный или прожорливый по памяти, пройдитесь по нему:

checklist = [
    "Нет ли явных Python-циклов по элементам массива?",
    "Заменены ли if/else на np.where и маски?",
    "Используется ли подходящий dtype (не шире нужного)?",
    "Нет ли лишних копий: astype, flatten, fancy-индексация в цикле?",
    "Предвыделены ли массивы вместо роста через append?",
    "Применяются ли out= и += в горячих местах?",
    "Идёт ли обход вдоль непрерывной (последней для C) оси?",
    "Правильно ли указан axis в агрегациях?",
]
for i, item in enumerate(checklist, 1):
    print("%d. %s" % (i, item))

Вывод:

1. Нет ли явных Python-циклов по элементам массива?
2. Заменены ли if/else на np.where и маски?
3. Используется ли подходящий dtype (не шире нужного)?
4. Нет ли лишних копий: astype, flatten, fancy-индексация в цикле?
5. Предвыделены ли массивы вместо роста через append?
6. Применяются ли out= и += в горячих местах?
7. Идёт ли обход вдоль непрерывной (последней для C) оси?
8. Правильно ли указан axis в агрегациях?

Типичные ошибки новичков: сводка

  • Циклы вместо векторизации — теряется вся скорость NumPy.
  • Правка view в уверенности, что это копия (и наоборот) — порча данных.
  • Переполнение узких целых и сравнение float через ==.
  • Ошибки broadcasting из-за невнимания к формам (выравнивание справа).
  • Путаница * и @, неверный axis в агрегациях.
  • Использование устаревшего np.random.seed вместо default_rng.

Связь навыков: всё переносится в pandas

Стоит ещё раз подчеркнуть главное практическое следствие того, что pandas построен на NumPy: почти всё, что вы здесь изучили, работает в pandas без переучивания. Векторные арифметические операции над колонками — это NumPy под капотом. Булевы маски для фильтрации строк DataFrame — те же маски, что мы строили над массивами. Агрегации с осями, сравнения, where, обработка пропусков через специальные функции — всё это знакомые концепции в новой обёртке. Когда вы пишете в pandas df[df['age'] > 18], вы применяете ровно тот приём булевой индексации, что разбирали в этом курсе. Поэтому переход от NumPy к pandas — это не изучение новой парадигмы, а добавление удобств (метки, разнородные колонки, группировки) поверх уже освоенной модели. И в обратную сторону: когда в pandas вам нужна максимальная скорость или операция, которой нет в высокоуровневом API, вы достаёте массив через .to_numpy() и работаете напрямую с NumPy. Эта бесшовная связь — причина, по которой освоение NumPy окупается мгновенно: вы получаете не одну библиотеку, а ключ ко всему стеку анализа данных на Python.

Что дальше

Вы освоили ядро NumPy: от устройства ndarray и памяти до broadcasting, линейной алгебры и производительности. Дальнейшие шаги: pandas для табличных данных, matplotlib для визуализации, scikit-learn для машинного обучения, SciPy для научных вычислений — все они стоят на NumPy и говорят на его языке. Понимание массивов, которое вы получили, — это пропуск во весь экосистему научного Python.

Подводные камни

  • Хранить числовые массивы в CSV. Медленно, теряется точность и dtype. Берите .npy/.npz.
  • Тянуть разнородные таблицы в NumPy. Структурированные массивы неудобны для анализа; это работа pandas.
  • Забывать, что pandas — это NumPy. Навыки переносятся; .to_numpy() даёт прямой мост.

Лучшие практики

  • Сохраняйте промежуточные массивы в бинарном .npy/.npz, а не в текст.
  • Для табличных разнородных данных переходите на pandas, оставляя NumPy для числового ядра.
  • Регулярно прогоняйте код по чек-листу производительности.
  • Рассматривайте NumPy как фундамент: освоив его, вы быстро войдёте в pandas, scikit-learn и SciPy.

Итог

  • Сохраняйте массивы в бинарном формате (save/load, savez) — быстро и без потерь.
  • Структурированные массивы дают поля разных типов, но для таблиц удобнее pandas.
  • pandas построен на NumPy; навыки векторизации и масок переносятся напрямую.
  • Чек-лист производительности и знание типичных ошибок — ваш итоговый инструмент.
Проверьте себя
1. Почему для сохранения числового массива .npy предпочтительнее CSV?
ACSV вообще не умеет хранить числа
B.npy — бинарный формат, сохраняющий точность, dtype и форму без потерь, читается быстро; CSV теряет точность и медленно парсится
C.npy можно открыть в текстовом редакторе, а CSV нет
DCSV ограничен размером 1 МБ
2. Какова связь между pandas и NumPy?
AЭто конкурирующие библиотеки без общего кода
Bpandas построен на NumPy: колонки DataFrame — по сути NumPy-массивы, и .to_numpy() даёт прямой мост; навыки векторизации переносятся
CNumPy построен на pandas
Dpandas заменяет NumPy полностью, и NumPy больше не нужен
3. Когда уместнее использовать pandas вместо чистого NumPy?
AДля матричного умножения больших однородных массивов
BДля табличных разнородных данных со столбцами разных типов, метками строк, группировками и временными рядами
CДля любых вычислений — NumPy устарел
DТолько для сохранения файлов
Поддержать проект