NumPy и Pandas: в чём разница и когда что использовать
a = [1, 2] и b = [1, 2]. Какие значения дадут выражения a == b и a is b?Содержание:
Зачем понимать разницу
Почти любой курс по анализу данных в Python начинается с двух библиотек: NumPy и Pandas. Их ставят рядом, импортируют одной строкой и часто используют в одном скрипте, поэтому новичку легко решить, что это просто две версии одного инструмента. Это не так. Разница между ними не косметическая, и непонимание границы между NumPy и Pandas регулярно всплывает на собеседованиях аналитика данных как простой отсекающий вопрос.
Если коротко: NumPy отвечает за быстрые вычисления над однородными числовыми массивами, а Pandas — за работу с таблицами, в которых колонки бывают разных типов и у строк есть осмысленные метки. Pandas при этом построен поверх NumPy и хранит данные внутри в его массивах. То есть это не конкуренты, а два слоя одного стека: низкоуровневый движок и высокоуровневый интерфейс над ним.
Разобраться с этим стоит не ради собеседования, а чтобы перестать брать неподходящий инструмент. Когда вы крутите матрицы и считаете линейную алгебру через Pandas, вы теряете скорость на ровном месте. Когда вы чистите CSV с датами и пропусками через голый NumPy, вы вручную пишете то, что в Pandas делается одним методом. Ниже разберём обе библиотеки по отдельности, сравним их по структурам данных и производительности и покажем, как переходить между ними.
Что такое NumPy
NumPy (Numerical Python) — это фундамент научных вычислений в Python. Главная сущность библиотеки — ndarray (n-dimensional array), однородный многомерный массив фиксированного типа. «Однородный» здесь ключевое слово: все элементы массива имеют один и тот же тип данных, например int64 или float64, и лежат в памяти непрерывным блоком. Именно из-за этого NumPy работает на порядки быстрее обычных списков Python.
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
print(arr.dtype) # int64 (на 64-битных системах)
print(arr.mean()) # 3.0
print(arr * 2) # [ 2 4 6 8 10]
matrix = np.array([[1, 2], [3, 4]])
print(matrix.shape) # (2, 2)
print(matrix.sum(axis=0)) # [4 6] — сумма по столбцамОбратите внимание на выражение arr * 2. Мы не писали цикл — операция применилась сразу ко всему массиву. Это и есть векторизация: одно действие над целым массивом вместо поэлементного перебора в Python. Под капотом перебор всё равно происходит, но он выполняется в скомпилированном C-коде, без накладных расходов интерпретатора на каждый шаг.
import numpy as np
# Питоновский цикл — каждый шаг проходит через интерпретатор
nums = list(range(1_000_000))
squares_slow = [x ** 2 for x in nums]
# Векторизация NumPy — тот же результат, но цикл на C-уровне
arr = np.arange(1_000_000)
squares_fast = arr ** 2NumPy особенно силён там, где данные по своей природе числовые и однородные: линейная алгебра, статистика, обработка сигналов и изображений (картинка — это массив пикселей), генерация случайных чисел. Многие библиотеки машинного обучения, включая scikit-learn, принимают на вход именно NumPy-массивы.
import numpy as np
# Решение системы линейных уравнений Ax = b
A = np.array([[1, 2], [3, 4]])
b = np.array([5, 6])
x = np.linalg.solve(A, b)
print(x) # [-4. 4.5]Что такое Pandas
Pandas решает другую задачу — работу с табличными разнотипными данными, то есть с тем, что вы привыкли видеть в Excel или в результатах SQL-запроса. Две основные структуры здесь — Series (один столбец с метками) и DataFrame (таблица из столбцов). В отличие от NumPy, разные колонки DataFrame могут иметь разные типы: строка с именем, целое число с возрастом, дробное число с зарплатой — всё в одной таблице.
import pandas as pd
df = pd.DataFrame({
'name': ['Анна', 'Борис', 'Вика'],
'age': [25, 30, 28],
'salary': [80000, 120000, 95000],
})
print(df.dtypes)
# name object
# age int64
# salary int64
# dtype: object
print(df[df['salary'] > 90000])
# name age salary
# 1 Борис 30 120000
# 2 Вика 28 95000Кроме гетерогенности у Pandas есть две вещи, которых нет в NumPy: осмысленные метки и встроенная логика анализа данных. У каждой строки и колонки есть индекс (имя), а не только числовая позиция, поэтому к данным можно обращаться по названию. А методы вроде groupby, merge, pivot_table, fillna, read_csv закрывают типичный цикл работы аналитика — загрузил, почистил, сгруппировал, посчитал — без ручного кода.
import pandas as pd
orders = pd.DataFrame({
'user_id': [1, 1, 2, 2, 3],
'amount': [100, 250, 90, 310, 70],
})
by_user = orders.groupby('user_id')['amount'].agg(['sum', 'count', 'mean'])
print(by_user)
# sum count mean
# user_id
# 1 350 2 175.0
# 2 400 2 200.0
# 3 70 1 70.0По сути Pandas — это SQL и Excel внутри Python, плюс удобная работа с датами, текстом и пропусками. Если вы уже знаете SQL, многие операции переносятся почти один в один — об этом есть отдельный разбор в Pandas vs SQL.
Ключевые отличия
Главных линий различия три: структура данных, типы и работа с пропусками. NumPy оперирует однородным числовым массивом без меток, Pandas — таблицей с разнотипными именованными колонками. Из этого вытекает почти всё остальное.
| Критерий | NumPy | Pandas |
|---|---|---|
| Основная структура | ndarray (массив) | DataFrame и Series (таблица) |
| Типы данных | Однородные, один тип на массив | Разнородные, свой тип у каждой колонки |
| Индексация | По числовой позиции | По именованным меткам строк и колонок |
| Пропуски | Только NaN во float, без удобных методов | Встроенно: NaN, fillna, dropna, isna |
| Группировка и сводные | Нет из коробки | groupby, pivot_table, merge |
| Чтение файлов | Базовое (loadtxt, genfromtxt) | CSV, Excel, SQL, JSON, Parquet |
| Сильная сторона | Скорость на чистой математике | Удобство табличного анализа и ETL |
Отдельно стоит выделить пропуски, потому что здесь поведение библиотек прямо различается и это частый источник багов. Pandas по умолчанию игнорирует NaN в агрегатах, а голый NumPy — нет.
import numpy as np
import pandas as pd
s = pd.Series([10, np.nan, 30, np.nan, 50])
print(s.mean()) # 30.0 — NaN пропускается по умолчанию
print(s.fillna(0).tolist()) # [10.0, 0.0, 30.0, 0.0, 50.0]
print(s.dropna().tolist()) # [10.0, 30.0, 50.0]
arr = np.array([10, np.nan, 30, np.nan, 50])
print(arr.mean()) # nan — обычное среднее «заражается» NaN
print(np.nanmean(arr)) # 30.0 — нужен отдельный метод nanmeanПроизводительность: что быстрее
Прямой ответ — на чистых числовых вычислениях NumPy быстрее Pandas, потому что у него меньше накладных расходов. Pandas хранит данные в NumPy-массивах, но добавляет сверху индексы, метки, проверку типов и обработку пропусков. За удобство приходится платить, и на маленьких поэлементных операциях эта плата заметна.
При этом сравнивать «NumPy против Pandas» по скорости не всегда корректно — обычно их не выбирают для одной и той же задачи. Гораздо важнее другое сравнение: и NumPy, и Pandas в разы быстрее обычного питоновского цикла, потому что переносят вычисления на C-уровень и работают с непрерывной памятью. Векторизованная операция вместо for-цикла или iterrows — это типично порядок величины ускорения, а иногда и два.
import numpy as np
import pandas as pd
df = pd.DataFrame({'a': np.arange(1_000_000)})
# Медленно: построчный обход в Python
total = 0
for _, row in df.iterrows():
total += row['a'] ** 2
# Быстро: векторизация — операция уходит в NumPy под капотом
total_fast = (df['a'] ** 2).sum()Практический вывод простой. Не переписывайте Pandas на NumPy преждевременно: для большинства аналитических задач Pandas достаточно быстр, а читаемость кода важнее микросекунд. К NumPy спускайтесь точечно — когда узкое место действительно в тяжёлой математике над числовым массивом. Если же тормозит сам Pandas, чаще помогает не переход на NumPy, а правильные типы и векторизация — это разобрано в оптимизации производительности Pandas.
Как Pandas использует NumPy
Pandas не альтернатива NumPy, а надстройка над ним. Каждая числовая колонка DataFrame физически хранится как NumPy-массив, и многие операции Pandas просто делегируются движку NumPy. Поэтому знание NumPy не «дополнительная» тема для аналитика, а понимание того, как Pandas устроен изнутри.
import pandas as pd
df = pd.DataFrame({'a': [1, 2, 3]})
print(type(df['a'].to_numpy())) # <class 'numpy.ndarray'>
print(df['a'].to_numpy()) # [1 2 3]Эта связь объясняет и поведение, которое иначе кажется случайным. Например, почему Series с целыми числами превращается во float, как только в нём появляется NaN: у целочисленного NumPy-массива нет значения для «пусто», а NaN существует только во float, поэтому Pandas повышает тип всей колонки. Понимая нижний слой, такие вещи перестают удивлять.
Переход между NumPy и Pandas
На практике вы постоянно ходите между двумя мирами: загрузили таблицу в Pandas, почистили, а затем отдали числовой массив в модель машинного обучения, которая ждёт NumPy. Переход в обе стороны делается явными методами.
import numpy as np
import pandas as pd
df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
# Pandas → NumPy
arr = df['a'].to_numpy() # рекомендуемый способ
arr_legacy = df['a'].values # старый способ, тот же массив
mat = df.to_numpy() # весь DataFrame → 2D-массив
# NumPy → Pandas
data = np.array([[1, 2], [3, 4]])
back = pd.DataFrame(data, columns=['x', 'y'])
print(back)
# x y
# 0 1 2
# 1 3 4Два практических момента. Во-первых, для получения массива из колонки предпочитайте to_numpy(), а не атрибут .values — он считается современным и явным API, тогда как .values оставлен для обратной совместимости. Во-вторых, если применить to_numpy() к DataFrame с разнотипными колонками (строки и числа вместе), результат получит общий тип object — и вместе с ним потеряется скорость NumPy. Поэтому к массиву обычно приводят однородную числовую часть таблицы, а не всё подряд.
Когда использовать NumPy, а когда Pandas
Правило выбора простое и хорошо звучит на собеседовании: берите NumPy для численных вычислений над однородными данными и Pandas для табличного анализа и ETL. Если задача описывается как «матрицы, векторы, линейная алгебра, обработка изображений, тензоры под модель» — это NumPy. Если как «загрузить файл, почистить, сгруппировать, посчитать метрики, соединить таблицы, поработать с датами» — это Pandas.
NumPy уместен, когда данные числовые и однородные, а на первом месте стоит скорость: быстрые поэлементные операции, линейная алгебра, статистика, генерация случайных выборок через np.random, подготовка признаков в виде матрицы для scikit-learn. Pandas уместен, когда данные табличные и разнотипные: чтение и очистка CSV или выгрузок из базы, группировки и сводные таблицы, объединение источников, работа с временными рядами, разведочный анализ (EDA).
import pandas as pd
# Типичная задача аналитика: помесячная выручка из CSV — это Pandas
orders = pd.read_csv('orders.csv')
monthly = (orders
.assign(month=pd.to_datetime(orders['date']).dt.to_period('M'))
.groupby('month')['revenue']
.agg(['sum', 'count', 'mean']))Для аналитика данных рабочая пропорция смещена в сторону Pandas: 90% повседневных задач — это таблицы. NumPy подключается точечно. Лучший способ закрепить разницу — не читать про неё, а решать задачи руками: разобрать векторизацию, группировки и пропуски на живых примерах можно в тренажёре по Python для аналитика, где код пишется и проверяется сразу.
Частые ошибки
Считать, что Pandas «всегда медленный» и его надо переписывать на NumPy. Для подавляющего большинства аналитических задач Pandas достаточно быстр, а читаемый код важнее экономии микросекунд. Спускаться к NumPy стоит точечно и по замеру, а не из общего ощущения.
Обходить DataFrame циклом через iterrows или apply там, где работает векторизация. Построчный обход прогоняет каждую строку через интерпретатор Python и убивает главное преимущество обеих библиотек. Почти всегда есть векторизованная альтернатива, которая быстрее на порядок.
Забывать, что to_numpy() на разнотипном DataFrame возвращает массив типа object. Такой массив теряет скорость NumPy и ведёт себя непредсказуемо в числовых операциях. Приводите к массиву только однородную числовую часть таблицы.
Путать поведение с пропусками. В Pandas агрегаты по умолчанию игнорируют NaN, а в голом NumPy mean по массиву с NaN вернёт nan — для пропусков нужны отдельные методы np.nanmean, np.nansum. Перенос привычки из одной библиотеки в другую даёт тихие баги.
Класть в один ndarray разнородные данные. Как только в массиве смешиваются строки и числа, NumPy делает его типом object, и вся C-скорость исчезает. Для разнотипных данных существует Pandas — не нужно гнуть NumPy под чужую задачу.
Связанные темы
- Pandas vs SQL: что выбрать для анализа
- NumPy для аналитика данных
- Типы данных в Pandas
- Группировка в Pandas через groupby
- Оптимизация производительности Pandas
FAQ
В чём главная разница между NumPy и Pandas?
NumPy работает с однородными числовыми массивами (ndarray) и заточен под быстрые вычисления, а Pandas — с табличными разнотипными данными (DataFrame и Series), у которых есть именованные индексы и встроенные методы для группировки, объединения и пропусков. Pandas при этом построен поверх NumPy.
Что быстрее — NumPy или Pandas?
На чистых числовых операциях NumPy быстрее, потому что у Pandas есть накладные расходы на индексы, метки и проверку типов. Но обычно их не выбирают для одной задачи: и тот, и другой в разы быстрее питоновского цикла за счёт векторизации, а Pandas для табличного анализа достаточно быстр.
Нужно ли знать NumPy, если я работаю только с Pandas?
Базово да. Pandas хранит данные в NumPy-массивах и делегирует ему часть операций, поэтому знание NumPy объясняет поведение Pandas — например, почему колонка целых чисел становится float при появлении NaN. Плюс NumPy нужен при работе с scikit-learn и кастомными вычислениями.
Можно ли конвертировать DataFrame в NumPy-массив?
Да. Используйте df.to_numpy() для всей таблицы или df['col'].to_numpy() для одной колонки. Атрибут .values делает то же самое, но to_numpy() считается современным API. На разнотипной таблице результат получит тип object, поэтому к массиву обычно приводят однородную числовую часть.
.values или .to_numpy() — что использовать?
Предпочитайте to_numpy(): это явный и рекомендуемый метод. .values оставлен для обратной совместимости и возвращает тот же массив, но в новом коде лучше его не использовать.
Pandas действительно построен на NumPy?
Да. Числовые колонки DataFrame физически хранятся как NumPy-массивы, и многие операции Pandas под капотом вызывают NumPy. Это два слоя одного стека: NumPy — низкоуровневый движок вычислений, Pandas — высокоуровневый интерфейс для анализа данных над ним.