5 лучших способов преобразования DataFrame Pandas в NumPy массивы
Для кого эта статья:
- Аналитики данных и специалисты по обработке данных
- Программисты, использующие Python для анализа данных
Студенты и профессионалы, обучающиеся работе с Pandas и NumPy
Работа с большими наборами данных часто превращается в балансирование между удобством и скоростью — именно здесь преобразование DataFrame из Pandas в массивы NumPy становится критически важным навыком. Высокоуровневый API Pandas идеален для манипуляций с данными, но когда речь заходит о вычислительной эффективности, низкоуровневые массивы NumPy способны ускорить вашу работу в 10-100 раз! 🚀 Эта статья раскроет пять проверенных методов конвертации, которые не только сэкономят драгоценные вычислительные ресурсы, но и сделают ваш код элегантнее, производительнее и профессиональнее.
Хотите полностью раскрыть потенциал Python для анализа данных? Обучение Python-разработке от Skypro — ваш ключ к профессиональному владению инструментами вроде Pandas и NumPy. Вы не только научитесь эффективно конвертировать данные между различными форматами, но и освоите комплексный подход к обработке информации, который востребован у ведущих аналитических компаний. Инвестируйте в навыки, которые трансформируют данные в решения!
Основы преобразования DataFrame в NumPy: сравнение методов
Преобразование данных между DataFrame Pandas и массивами NumPy — это фундаментальная операция для каждого аналитика данных. Pandas построен поверх NumPy, что делает эту конвертацию не просто возможной, но и относительно бесшовной. Однако, разные методы преобразования имеют существенные различия в производительности, гибкости и обработке специфических типов данных.
Прежде чем погрузиться в детали каждого метода, давайте рассмотрим основные способы преобразования DataFrame в массив NumPy:
- DataFrame.to_numpy() — современный и рекомендуемый метод в Pandas
- DataFrame.values — традиционный атрибут, всё ещё широко используемый
- np.array(df) — прямой вызов функции NumPy
- df.to_records() — специализированное преобразование со сохранением структуры
- np.asarray(df) — оптимизированное преобразование, предпочтительное в некоторых случаях
Для демонстрации различий между методами, создадим простой DataFrame:
import pandas as pd
import numpy as np
# Создаем пример DataFrame
df = pd.DataFrame({
'A': [1, 2, 3, 4, 5],
'B': [5\.1, 4.2, 3.3, 2.4, 1.5],
'C': ['a', 'b', 'c', 'd', 'e']
})
Теперь сравним эффективность различных методов преобразования в контексте производительности и поведения:
| Метод | Синтаксис | Относительная скорость | Обработка смешанных типов |
|---|---|---|---|
| to_numpy() | df.to_numpy() | Быстрая | Приведение к общему типу |
| values | df.values | Быстрая | Приведение к общему типу |
| np.array() | np.array(df) | Медленнее (доп. проверки) | Приведение к общему типу |
| to_records() | df.to_records() | Самая медленная | Сохранение типов (структурированный массив) |
| np.asarray() | np.asarray(df) | Средняя | Приведение к общему типу |
Александр Петров, ведущий аналитик данных
В моей практике был показательный случай с проектом анализа клиентского поведения для крупного ритейлера. Мы обрабатывали датасет из 20 миллионов записей транзакций, и исходный пайплайн, полностью построенный на Pandas, занимал почти 3 часа.
Коллега предложил: "А давай-ка переведём основные вычисления на NumPy?" Я был скептичен, ведь нам пришлось бы переписать около 30% кода. Однако мы начали с самого узкого места — расчёта сегментационных метрик.
Заменив df.groupby().agg() на работу с предварительно сконвертированными в np.array() данными и векторизованные операции, мы ускорили этот этап в 47 раз! А полная конвертация критических участков с использованием df.to_numpy() сократила общее время выполнения до 12 минут.
Тот проект стал для меня поворотным — с тех пор я начинаю любой серьёзный анализ с вопроса: "Что можно перевести в NumPy?"
Важно понимать, что при преобразовании DataFrame в NumPy массив вы теряете некоторые преимущества Pandas, такие как именованные столбцы, индексы и встроенные методы для работы с пропущенными значениями. Однако вы получаете значительный прирост в скорости вычислений, особенно для больших объёмов данных и математических операций.

Метод DataFrame.to_numpy(): полный контроль над конвертацией
Метод to_numpy() — это самый современный и гибкий способ конвертации DataFrame в массив NumPy, введённый в Pandas 0.24.0. Он предлагает расширенные возможности контроля над процессом преобразования и рекомендован официальной документацией Pandas как предпочтительный метод. 📊
Основной синтаксис метода выглядит так:
# Базовое использование
numpy_array = df.to_numpy()
# С явным указанием типа данных
numpy_array_float = df.to_numpy(dtype=float)
# С обработкой NA/NaN значений
numpy_array_na = df.to_numpy(na_value=-999)
# С копированием данных
numpy_array_copy = df.to_numpy(copy=True)
Метод to_numpy() предоставляет несколько ключевых параметров для тонкой настройки процесса конвертации:
- dtype — позволяет явно задать тип данных для возвращаемого массива
- copy — если True, всегда создает копию данных; если False (по умолчанию), копирует только при необходимости
- na_value — значение для замены NA/NaN в возвращаемом массиве
Давайте рассмотрим практические примеры использования этих параметров:
import pandas as pd
import numpy as np
# Создаем DataFrame с разными типами данных и пропусками
df = pd.DataFrame({
'целые': [1, 2, None, 4],
'дробные': [1\.1, None, 3.3, 4.4],
'строки': ['a', 'b', 'c', None]
})
# Конвертация с автоматическим выбором типа
arr1 = df.to_numpy()
print(f"Автоматическая конвертация:\n{arr1}\nТип: {arr1.dtype}")
# Принудительное приведение к float
arr2 = df.to_numpy(dtype=float)
print(f"\nПриведение к float:\n{arr2}\nТип: {arr2.dtype}")
# Замена NA значениями
arr3 = df.to_numpy(na_value="ПРОПУСК")
print(f"\nЗамена NA:\n{arr3}\nТип: {arr3.dtype}")
Результат выполнения кода показывает, как to_numpy() обрабатывает разные ситуации:
- При автоматической конвертации смешанных типов данных метод выбирает наиболее общий тип (обычно object)
- С параметром dtype можно принудительно привести данные к нужному типу (но строки в числа не преобразуются)
- Параметр na_value позволяет заменить NA/NaN значения на указанную константу
| Сценарий использования | Рекомендуемые параметры to_numpy() | Преимущества |
|---|---|---|
| Математические вычисления | dtype=float, na_value=0 или np.nan | Обеспечивает числовой формат и контроль обработки пропусков |
| Интеграция с ML-библиотеками | dtype=float, copy=True | Предотвращает изменение исходных данных, совместимость с sklearn |
| Обработка категориальных данных | без параметров (auto) | Сохраняет строковые значения для дальнейшей обработки |
| Оптимизация памяти | dtype=np.float32 или np.int32 | Уменьшает потребление памяти при работе с большими данными |
| Экспорт в другие системы | na_value=специфическое значение | Обеспечивает совместимость с внешними системами |
Важно отметить, что to_numpy() имеет одно существенное отличие от атрибута values: он всегда возвращает двумерный массив для DataFrame (в отличие от Series, где возвращается одномерный массив). Это обеспечивает согласованность поведения при работе с данными.
Для больших датасетов стоит учитывать, что параметр copy=True может увеличить потребление памяти, так как создаёт новую копию данных. По умолчанию to_numpy() избегает копирования там, где это возможно, что делает его более эффективным.
Работа с DataFrame.values: наследие и особенности применения
Атрибут values — это исторически первый способ доступа к данным DataFrame как к массиву NumPy. Несмотря на появление более современного метода to_numpy(), атрибут values остаётся широко используемым в существующем коде и обладает своими особенностями. 🔄
Основное применение атрибута values чрезвычайно просто:
import pandas as pd
import numpy as np
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6]
})
# Получение NumPy массива через атрибут values
numpy_array = df.values
print(f"Тип результата: {type(numpy_array)}")
print(f"Форма массива: {numpy_array.shape}")
print(f"Содержимое массива:\n{numpy_array}")
Атрибут values возвращает внутреннее представление данных DataFrame в виде ndarray, что делает его очень быстрым методом преобразования. Однако у этого подхода есть несколько ключевых особенностей и потенциальных проблем.
Дмитрий Соколов, технический лид проектов по машинному обучению
Помню, как однажды мы столкнулись с загадочной проблемой в производственной системе прогнозирования спроса. Модель, которая раньше работала идеально, вдруг начала выдавать странные результаты.
Расследование привело нас к неожиданному виновнику — коду, использующему df.values с временными рядами. Недавнее обновление Pandas изменило внутреннее представление данных, и DataFrame с DatetimeIndex стал возвращать через .values не числовое представление дат (как раньше), а массив объектов datetime64.
Наш конвейер машинного обучения ожидал числовые значения времени, но получал объекты. Проблема решилась одной строкой — заменой df.values на df.to_numpy(dtype=float), но выявить её заняло два напряжённых дня.
Этот случай научил всю команду тщательно документировать предполагаемые типы данных в интерфейсах между компонентами и с осторожностью относиться к "магическим" атрибутам вроде .values, поведение которых может измениться в новых версиях библиотек.
Давайте рассмотрим основные различия между values и to_numpy():
- Контроль типов: values не позволяет указать выходной dtype, в то время как to_numpy() предоставляет эту возможность
- Индексы даты/времени: для DataFrame с DatetimeIndex, values может вернуть массив объектов datetime64, а не числовые значения
- Расширенная размерность: для некоторых типов данных values может вернуть массив с размерностью > 2, что может быть неожиданным
- Предсказуемость: поведение values может меняться в разных версиях Pandas, в то время как to_numpy() имеет стабильный контракт
Когда стоит использовать атрибут values:
- В существующем коде, где изменение на to_numpy() может потребовать дополнительного тестирования
- В случаях, когда требуется абсолютная максимальная производительность и гарантированно известны типы данных
- При быстром исследовательском анализе, где небольшие несоответствия в поведении не критичны
Примечательно, что в некоторых специфических случаях атрибут values может работать быстрее, чем to_numpy(), поскольку он просто возвращает внутренний буфер данных без дополнительных проверок. Однако эта разница в производительности обычно незначительна и не оправдывает потери в контроле и предсказуемости.
# Пример потенциальных проблем с values при работе с датами
import pandas as pd
import numpy as np
# Создаем DataFrame с временным индексом
dates = pd.date_range('20210101', periods=3)
df_dates = pd.DataFrame({'value': [10, 20, 30]}, index=dates)
# Сравниваем values и to_numpy()
print(f"Тип данных df.values: {df_dates.index.values.dtype}")
print(f"Значения df.values: {df_dates.index.values}")
print(f"\nТип данных df.index.to_numpy(): {df_dates.index.to_numpy().dtype}")
print(f"Значения df.index.to_numpy(): {df_dates.index.to_numpy()}")
# Явное преобразование в числа
print(f"\nЯвное преобразование в числа: {df_dates.index.to_numpy('int64')}")
Как видно из примера, атрибут values может вернуть массив с типом datetime64, который не всегда совместим с числовыми операциями, в то время как to_numpy() с указанным dtype позволяет получить именно числовое представление дат.
Использование np.array() для специфических задач преобразования
Функция np.array() предоставляет прямой способ преобразования DataFrame Pandas в массив NumPy. Хотя этот метод может показаться интуитивно понятным, особенно для тех, кто хорошо знаком с NumPy, он имеет ряд специфических особенностей и случаев применения, которые отличают его от встроенных методов Pandas. 🔍
Базовое использование np.array() выглядит следующим образом:
import pandas as pd
import numpy as np
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4\.5, 5.5, 6.5],
'C': ['x', 'y', 'z']
})
# Преобразование с помощью np.array()
numpy_array = np.array(df)
print(f"Тип результата: {type(numpy_array)}")
print(f"Форма массива: {numpy_array.shape}")
print(f"Тип данных: {numpy_array.dtype}")
print(f"Содержимое:\n{numpy_array}")
Функция np.array() обладает расширенными возможностями настройки и некоторыми отличительными характеристиками:
- Глубокое копирование: np.array() всегда создаёт новую копию данных, что может быть важно для изоляции изменений
- Контроль размерности: позволяет управлять выходной размерностью через параметр ndmin
- Расширенная обработка типов: предлагает более гибкий контроль над преобразованием типов через параметры dtype и casting
- Обработка вложенных структур: может работать со сложными вложенными структурами данных
Рассмотрим несколько специфических случаев использования np.array() для преобразования DataFrame:
# Управление размерностью выходного массива
arr_2d = np.array(df, ndmin=2) # Гарантированно двумерный массив
arr_3d = np.array(df, ndmin=3) # Трехмерный массив с дополнительной осью
print(f"Форма 2D массива: {arr_2d.shape}")
print(f"Форма 3D массива: {arr_3d.shape}")
# Контроль приведения типов
arr_float = np.array(df[['A', 'B']], dtype=float)
print(f"Массив float:\n{arr_float}")
# Копирование с порядком хранения в памяти
arr_fortran = np.array(df, order='F') # Fortran-порядок (столбцы смежные)
arr_c = np.array(df, order='C') # C-порядок (строки смежные)
Отличительные преимущества np.array() для специфических задач:
- Принудительное копирование данных: гарантирует полную независимость результирующего массива от исходного DataFrame
- Расширенные параметры формирования массива: позволяет настроить порядок хранения данных, контуры выходной размерности
- Обработка подмножеств и срезов: хорошо работает с выборками из DataFrame
- Интеграция с низкоуровневыми операциями NumPy: идеально подходит, когда требуются специфические возможности NumPy
Однако стоит помнить и о потенциальных недостатках этого метода:
- Производительность: np.array() часто работает медленнее, чем to_numpy() или values, особенно для больших DataFrame
- Избыточное копирование: всегда создаёт новую копию данных, даже когда это не требуется
- Потеря метаданных: теряются не только индексы и имена столбцов, но и другие метаданные DataFrame
В каких случаях np.array() может быть предпочтительнее других методов:
# 1. При работе с подмножествами данных
subset_array = np.array(df[df['A'] > 1][['B', 'C']])
# 2. Когда требуется явное приведение типов со специфическими правилами
arr_custom = np.array(df, dtype=[('col1', 'i4'), ('col2', 'f4'), ('col3', 'U10')])
# 3. При интеграции с другими функциями NumPy, ожидающими определённую структуру
stacked = np.column_stack((np.array(df['A']), np.array(df['B'])))
# 4. Для создания структурированных массивов с сохранением имён столбцов
dtype = [(name, df[name].dtype) for name in df.columns]
structured = np.array([tuple(x) for x in df.values], dtype=dtype)
Функция np.asarray() представляет собой более оптимизированную альтернативу np.array() и может быть предпочтительнее в некоторых ситуациях:
# np.asarray() не создаёт копию, если входные данные уже являются ndarray
# и имеют требуемый dtype
df_numeric = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
arr1 = np.asarray(df_numeric) # Может избежать копирования
arr2 = np.array(df_numeric) # Всегда создаёт копию
Обработка пропущенных значений при конвертации DataFrame в NumPy
Пропущенные значения (NaN, None, NaT) представляют собой особую проблему при конвертации данных из Pandas в NumPy. В то время как Pandas имеет развитую инфраструктуру для работы с пропусками, массивы NumPy обрабатывают их иначе, что может привести к неожиданным результатам или ошибкам. 📉
Рассмотрим основные сценарии и стратегии работы с пропущенными значениями:
import pandas as pd
import numpy as np
# Создадим DataFrame с разными типами пропущенных значений
df_missing = pd.DataFrame({
'целые': [1, None, 3, np.nan],
'дробные': [1\.1, 2.2, np.nan, 4.4],
'строки': ['a', None, 'c', np.nan],
'даты': pd.date_range('20230101', periods=2).tolist() + [None, pd.NaT]
})
print("Исходный DataFrame с пропущенными значениями:")
print(df_missing)
При прямой конвертации DataFrame с пропущенными значениями в массив NumPy происходят следующие преобразования:
- Числовые столбцы: NaN и None конвертируются в np.nan (числовое значение NaN)
- Строковые столбцы: NaN и None обычно конвертируются в строку 'nan' или объект None
- Столбцы с датами: NaT и None могут привести к различным результатам, включая NaT, np.nan или изменение типа всего столбца
Давайте сравним поведение разных методов конвертации:
# Конвертация с помощью to_numpy() без параметров
arr1 = df_missing.to_numpy()
print("\nto_numpy() без параметров:")
print(f"Тип данных: {arr1.dtype}")
print(arr1)
# Конвертация с заменой NA значений
arr2 = df_missing.to_numpy(na_value=-999)
print("\nto_numpy() с заменой NA на -999:")
print(arr2)
# Конвертация только числовых столбцов с явным приведением типа
arr3 = df_missing[['целые', 'дробные']].to_numpy(dtype=float)
print("\nto_numpy() числовых столбцов с dtype=float:")
print(f"Тип данных: {arr3.dtype}")
print(arr3)
При работе с пропущенными значениями следует учитывать ряд важных моментов:
- Типы данных: NumPy обрабатывает NaN по-разному в зависимости от типа данных. Только типы с плавающей точкой (float) могут естественно представлять NaN.
- Математические операции: Операции с np.nan дают результат np.nan, что может привести к распространению пропусков через расчеты.
- Сравнения: np.nan не равен ничему, включая сам себя (np.nan != np.nan) — для проверки используйте np.isnan().
- Агрегации: Функции вроде np.sum() по умолчанию включают np.nan, что может искажать результаты.
| Стратегия обработки NA | Реализация | Подходит для |
|---|---|---|
| Замена на константу | df.tonumpy(navalue=0) | Простые расчеты, когда пропуски можно безопасно заменить |
| Предварительная очистка | df.dropna().to_numpy() | Случаи, когда строки с пропусками можно исключить |
| Заполнение статистиками | df.fillna(df.mean()).to_numpy() | Статистический анализ и машинное обучение |
| Маскирование массива | np.ma.maskedarray(df.tonumpy(), mask=np.isnan(df.to_numpy())) | Сохранение информации о пропусках в NumPy |
| Отдельная обработка столбцов | применение разных стратегий к разным столбцам | Сложные наборы данных с разнородными столбцами |
Практические рекомендации для работы с пропущенными значениями:
# 1. Использование маскированных массивов NumPy
arr = df_missing.to_numpy()
masked_arr = np.ma.masked_array(arr, mask=pd.isna(df_missing))
print("\nМаскированный массив (np.ma):")
print(masked_arr)
# 2. Предварительная обработка перед конвертацией
# Заполнение средними значениями для числовых колонок
df_filled = df_missing.copy()
numeric_cols = df_filled.select_dtypes(include=[np.number]).columns
df_filled[numeric_cols] = df_filled[numeric_cols].fillna(df_filled[numeric_cols].mean())
arr_filled = df_filled.to_numpy()
print("\nМассив с предварительным заполнением числовых пропусков:")
print(arr_filled)
# 3. Использование специальных функций NumPy для обработки NaN
arr_numeric = df_missing[['целые', 'дробные']].to_numpy(dtype=float)
sum_ignoring_nan = np.nansum(arr_numeric, axis=0)
print(f"\nСумма по столбцам с игнорированием NaN: {sum_ignoring_nan}")
Важно помнить, что выбор стратегии обработки пропущенных значений должен соответствовать специфике задачи и характеру данных. Неправильная обработка пропусков может привести к систематическим ошибкам в анализе или модели.
Для критически важных приложений рекомендуется:
- Документировать стратегию обработки пропусков
- Проводить анализ чувствительности результатов к разным методам обработки пропусков
- Использовать более сложные методы, такие как множественное заполнение (multiple imputation), для статистически значимых исследований
- Рассмотреть возможность сохранения информации о пропусках на всех этапах анализа
Преобразование данных между Pandas и NumPy — это фундаментальный навык, который отличает профессионального аналитика данных от новичка. Выбрав правильный метод конвертации, вы не только оптимизируете производительность вашего кода, но и обеспечиваете корректность результатов. Помните: DataFrame.to_numpy() предлагает наилучший баланс между контролем и предсказуемостью, DataFrame.values обеспечивает максимальную скорость, а функции np.array() и специализированные методы дают гибкость для нестандартных сценариев. Обрабатывайте пропущенные значения осознанно, и ваши данные станут надежным фундаментом для любого анализа или модели машинного обучения.