Как правильно добавить или создать массив из других массивов в NumPy?

NumPy является краеугольным камнем для научных вычислений в Python, предоставляя мощный объект ndarray для эффективной работы с числовыми данными. Часто в задачах анализа данных, машинного обучения или моделирования возникает необходимость не просто работать с одномерными или двумерными массивами, но и создавать более сложные структуры — массивы, состоящие из других массивов. Это может быть полезно для представления наборов данных с переменным количеством признаков, коллекций изображений, временных рядов или любых других иерархических данных.

Однако, создание и эффективное управление такими "вложенными" структурами в NumPy имеет свои особенности и требует понимания различных подходов. Существует несколько способов формирования массива из других массивов, каждый из которых подходит для определенных сценариев. В этой статье мы подробно рассмотрим, как правильно создавать, объединять и добавлять массивы к существующим многомерным структурам, используя различные функции NumPy, такие как np.array(), np.vstack(), np.hstack(), np.concatenate() и другие. Мы также обсудим нюансы работы с массивами разной длины и поможем выбрать наиболее подходящий метод для ваших задач.

Основы создания массива из массивов

После того как мы осознали важность многомерных структур данных, давайте рассмотрим базовые методы их создания в NumPy. Самый прямой способ сформировать массив, состоящий из других массивов, — это использовать функцию np.array().

Создание из списка списков

NumPy позволяет легко преобразовать стандартный Python список списков в многомерный массив ndarray. Если все вложенные списки имеют одинаковую длину, np.array() автоматически создаст двумерный массив.

import numpy as np

list_of_lists = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

array_from_lists = np.array(list_of_lists)
print(array_from_lists)
# Вывод:
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]
print(array_from_lists.shape) # (3, 3)

Создание из существующих одномерных массивов (np.array())

Аналогично, можно передать np.array() список уже существующих одномерных массивов NumPy. Это особенно удобно, когда у вас уже есть данные, представленные в виде ndarray объектов.

arr1 = np.array([10, 11, 12])
arr2 = np.array([13, 14, 15])
arr3 = np.array([16, 17, 18])

array_of_arrays = np.array([arr1, arr2, arr3])
print(array_of_arrays)
# Вывод:
# [[10 11 12]
#  [13 14 15]
#  [16 17 18]]
print(array_of_arrays.shape) # (3, 3)

Важно отметить, что для создания однородного многомерного массива (например, 2D-матрицы) все вложенные элементы (списки или массивы) должны иметь одинаковую длину. В противном случае NumPy может создать массив объектов (dtype=object), что имеет свои особенности и ограничения, которые мы рассмотрим позже.

Создание из списка списков

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

Рассмотрим пример:

import numpy as np

# Список списков, где каждый внутренний список представляет строку
list_of_rows = [
    [10, 20, 30],
    [40, 50, 60],
    [70, 80, 90]
]

# Создание массива NumPy из списка списков
matrix = np.array(list_of_rows)

print(matrix)
print(f"Форма массива: {matrix.shape}")
print(f"Тип данных элементов: {matrix.dtype}")

Результат:

[[10 20 30]
 [40 50 60]
 [70 80 90]]
Форма массива: (3, 3)
Тип данных элементов: int64

Как видно из примера, np.array() успешно преобразует list_of_rows в двумерный массив ndarray с формой (3, 3). Важно отметить, что для создания однородного двумерного массива все внутренние списки должны иметь одинаковую длину. Если длины различаются, NumPy может создать массив с типом данных object, где каждый элемент массива будет представлять собой отдельный список, а не числовое значение. Это значительно снижает производительность и возможности векторных операций.

Создание из существующих одномерных массивов (np.array())

Продолжая тему создания многомерных массивов, рассмотрим, как np.array() обрабатывает уже существующие одномерные массивы NumPy. Этот подход очень похож на создание из списка списков, но вместо обычных списков в качестве элементов выступают объекты ndarray.

Когда вы передаете список из нескольких одномерных массивов NumPy функции np.array(), она интерпретирует каждый внутренний массив как строку (или столбец, в зависимости от контекста) нового многомерного массива. Это удобный способ объединить несколько предварительно созданных векторов в единую матрицу.

import numpy as np

arr1 = np.array([10, 20, 30])
arr2 = np.array([40, 50, 60])
arr3 = np.array([70, 80, 90])

# Создание 2D массива из существующих 1D массивов
matrix = np.array([arr1, arr2, arr3])

print("Созданный массив:\n", matrix)
print("Форма массива:", matrix.shape)
print("Тип данных массива:", matrix.dtype)

В результате выполнения этого кода matrix будет двумерным массивом формы (3, 3), где каждый из arr1, arr2, arr3 стал отдельной строкой. Важно отметить, что для создания однородного ndarray все внутренние массивы должны иметь одинаковую длину. Если длины различаются, np.array() может создать массив объектов (dtype=object), где каждый элемент является отдельным одномерным массивом.

Объединение (конкатенация) массивов в многомерную структуру

Если np.array() позволяет создать массив из списка массивов, автоматически определяя их расположение, то для более явного контроля над тем, как массивы объединяются в многомерную структуру, NumPy предлагает специализированные функции конкатенации. Эти функции особенно полезны, когда требуется объединить массивы с одинаковой размерностью, но по определенной оси.

Вертикальное и горизонтальное объединение (np.vstack, np.hstack)

Для объединения массивов по вертикали, то есть добавления их как новых строк (или увеличения первой оси), используется функция np.vstack(). Она принимает кортеж или список массивов, которые должны иметь одинаковое количество столбцов (для 2D массивов) или одинаковую форму по всем осям, кроме первой.

import numpy as np

arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])

# Вертикальное объединение
vertical_stack = np.vstack((arr1, arr2))
# Результат:
# [[1 2 3]
#  [4 5 6]]

Аналогично, для горизонтального объединения массивов, то есть добавления их как новых столбцов (или увеличения последней оси), применяется функция np.hstack(). Объединяемые массивы должны иметь одинаковое количество строк (для 2D массивов) или одинаковую форму по всем осям, кроме последней.

import numpy as np

arr1 = np.array([[1], [2]])
arr2 = np.array([[3], [4]])

# Горизонтальное объединение
horizontal_stack = np.hstack((arr1, arr2))
# Результат:
# [[1 3]
#  [2 4]]

Использование np.concatenate() для гибкого объединения

np.concatenate() является более универсальным инструментом, чем np.vstack() и np.hstack(), поскольку позволяет объединять массивы вдоль любой указанной оси. Функции np.vstack() и np.hstack() по сути являются удобными обертками для np.concatenate() с предопределенными значениями оси.

Синтаксис np.concatenate((a1, a2, ...), axis=0) позволяет указать ось, по которой будет происходить объединение. Все массивы должны иметь одинаковую форму, за исключением размера по оси, вдоль которой происходит конкатенация.

import numpy as np

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

# Объединение по оси 0 (вертикально)
concat_axis0 = np.concatenate((arr1, arr2), axis=0)
# Результат:
# [[1 2]
#  [3 4]
#  [5 6]
#  [7 8]]

# Объединение по оси 1 (горизонтально)
concat_axis1 = np.concatenate((arr1, arr2), axis=1)
# Результат:
# [[1 2 5 6]
#  [3 4 7 8]]

Вертикальное и горизонтальное объединение (np.vstack, np.hstack)

Для часто встречающихся задач объединения массивов NumPy предлагает специализированные функции np.vstack() и np.hstack(). Они являются удобными обертками над np.concatenate(), упрощающими синтаксис для вертикального и горизонтального стекирования соответственно.

  • np.vstack() (vertical stack): Эта функция объединяет массивы по вертикали (построчно), увеличивая количество строк. Все входные массивы должны иметь одинаковое количество столбцов.

    import numpy as np
    
    arr1 = np.array([1, 2, 3])
    arr2 = np.array([4, 5, 6])
    vertical_stack = np.vstack((arr1, arr2))
    # Результат:
    # [[1 2 3]
    #  [4 5 6]]
    
    arr3 = np.array([[7, 8], [9, 10]])
    arr4 = np.array([[11, 12]])
    vertical_stack_2d = np.vstack((arr3, arr4))
    # Результат:
    # [[ 7  8]
    #  [ 9 10]
    #  [11 12]]
    
    Реклама
  • np.hstack() (horizontal stack): Эта функция объединяет массивы по горизонтали (постолбцово), увеличивая количество столбцов. Все входные массивы должны иметь одинаковое количество строк.

    import numpy as np
    
    arr1 = np.array([1, 2, 3])
    arr2 = np.array([4, 5, 6])
    horizontal_stack = np.hstack((arr1, arr2))
    # Результат: [1 2 3 4 5 6]
    
    arr3 = np.array([[1, 2], [3, 4]])
    arr4 = np.array([[5, 6], [7, 8]])
    horizontal_stack_2d = np.hstack((arr3, arr4))
    # Результат:
    # [[1 2 5 6]
    #  [3 4 7 8]]
    

Эти функции особенно полезны, когда вы хотите объединить одномерные массивы в двумерный массив или добавить строки/столбцы к существующим двумерным массивам, сохраняя при этом интуитивно понятный синтаксис.

Использование np.concatenate() для гибкого объединения

В то время как np.vstack() и np.hstack() являются специализированными функциями для объединения по определенным осям, np.concatenate() предлагает более универсальный подход, позволяя явно указать ось, вдоль которой будет происходить объединение. Это делает ее мощным инструментом для работы с массивами любой размерности.

Синтаксис np.concatenate((a1, a2, ...), axis=0) принимает кортеж массивов для объединения и параметр axis, который определяет ось. Если axis=0 (по умолчанию), массивы объединяются вертикально (как np.vstack()). Если axis=1, они объединяются горизонтально (как np.hstack()).

import numpy as np

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

# Вертикальное объединение (axis=0)
result_v = np.concatenate((arr1, arr2), axis=0)
# [[1, 2],
#  [3, 4],
#  [5, 6],
#  [7, 8]]

# Горизонтальное объединение (axis=1)
result_h = np.concatenate((arr1, arr2), axis=1)
# [[1, 2, 5, 6],
#  [3, 4, 7, 8]]

np.concatenate() требует, чтобы все массивы имели одинаковую форму по всем осям, кроме той, по которой происходит объединение.

Добавление новых массивов к существующему массиву массивов

Для расширения уже существующего массива массивов в NumPy можно использовать несколько подходов. Функции np.append() и np.insert() позволяют добавлять элементы или целые массивы, но важно помнить, что они всегда возвращают новый массив, а не изменяют исходный на месте. Это может быть неэффективно при частых операциях.

Пример использования np.append() для добавления строки:

import numpy as np
arr = np.array([[1, 2], [3, 4]])
new_row = np.array([5, 6])
arr_extended = np.append(arr, [new_row], axis=0)
# arr_extended теперь [[1, 2], [3, 4], [5, 6]]

np.insert() позволяет вставить массив в определенную позицию:

import numpy as np
arr = np.array([[1, 2], [3, 4]])
new_row = np.array([5, 6])
arr_inserted = np.insert(arr, 1, new_row, axis=0)
# arr_inserted теперь [[1, 2], [5, 6], [3, 4]]

Эффективное добавление через конкатенацию

Наиболее эффективным способом добавления одного или нескольких массивов к существующей структуре, особенно при работе с большими данными, является использование np.concatenate(). Этот метод позволяет объединить исходный массив с новым массивом (или списком массивов) вдоль указанной оси, создавая новый объединенный массив. Это предпочтительный подход для построения массивов и добавления к ним новых частей.

import numpy as np
arr = np.array([[1, 2], [3, 4]])
new_data = np.array([[5, 6], [7, 8]])
arr_combined = np.concatenate((arr, new_data), axis=0)
# arr_combined теперь [[1, 2], [3, 4], [5, 6], [7, 8]]

Расширение массива с помощью np.append() и np.insert()

Хотя np.concatenate() является предпочтительным методом для эффективного расширения массивов, np.append() и np.insert() также предоставляют функциональность для добавления новых массивов к существующим структурам, особенно когда речь идет о добавлении одного или нескольких элементов. Обе функции возвращают новый массив, а не изменяют исходный на месте.

  • np.append(arr, values, axis=None): Эта функция добавляет values в конец arr. Если arr представляет собой массив массивов (например, 2D массив), вы можете добавить новый одномерный массив как новую "строку", указав axis=0.

    import numpy as np
    arr_of_arrays = np.array([[1, 2], [3, 4]])
    new_array = np.array([5, 6])
    extended_arr = np.append(arr_of_arrays, [new_array], axis=0)
    # extended_arr будет [[1, 2], [3, 4], [5, 6]]
    
  • np.insert(arr, obj, values, axis=None): Позволяет вставить values перед указанным индексом obj вдоль заданной оси. Это полезно, когда требуется вставить новый массив не в конец, а в определенное место.

    # Продолжая пример
    another_new_array = np.array([7, 8])
    inserted_arr = np.insert(extended_arr, 1, [another_new_array], axis=0)
    # inserted_arr будет [[1, 2], [7, 8], [3, 4], [5, 6]]
    

Для частых операций добавления или с большими массивами, np.concatenate() остается более производительным выбором.

Эффективное добавление через конкатенацию (np.concatenate())

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

Предположим, у нас есть двумерный массив, и мы хотим добавить к нему новую "строку" (одномерный массив):

import numpy as np

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

# Добавление новой строки по вертикали (axis=0)
updated_array = np.concatenate((existing_array, [new_row]), axis=0)
print(updated_array)
# Вывод:
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]

Таким образом, np.concatenate() обеспечивает гибкий и производительный способ расширения массивов, позволяя контролировать ось объединения и минимизируя создание промежуточных копий.

Особенности и продвинутые аспекты работы

Работа с ‘рваными’ (ragged) массивами (массивы разной длины)

Стандартные ndarray в NumPy требуют, чтобы все вложенные массивы имели одинаковую форму. Если вы пытаетесь создать массив из массивов разной длины, NumPy по умолчанию создаст ndarray с dtype=object. В этом случае каждый элемент верхнего уровня будет ссылкой на отдельный массив Python, что может снизить производительность по сравнению с истинными многомерными массивами NumPy.

Сравнение методов: когда что использовать?

Выбор метода зависит от вашей задачи:

  • np.array([arr1, arr2]): Идеально для начального создания массива из существующих массивов или списков, особенно когда важна гибкость dtype=object для ‘рваных’ структур.

  • np.vstack() / np.hstack(): Используйте для специфического вертикального или горизонтального объединения массивов, когда вы точно знаете, по какой оси нужно выполнить конкатенацию.

  • np.concatenate(): Наиболее гибкий и эффективный метод для объединения и расширения массивов по любой заданной оси, особенно при работе с большими данными или когда требуется динамическое добавление.

Работа с ‘рваными’ (ragged) массивами (массивы разной длины)

Как уже упоминалось, для работы с ‘рваными’ массивами, где вложенные массивы имеют разную длину, NumPy требует использования dtype=object. Это превращает ndarray в массив Python-объектов, что позволяет хранить ссылки на массивы разной формы. Однако такой подход снижает производительность по сравнению с нативными числовыми массивами NumPy, так как операции выполняются на уровне объектов Python, а не оптимизированных C-функций. Для эффективной обработки данных часто рекомендуется приводить ‘рваные’ массивы к однородной структуре, например, путем дополнения (padding) более коротких массивов или использования списков Python для итерации.

Сравнение методов: когда что использовать?

Выбор метода зависит от структуры данных и требований к производительности.

  • Для создания массива из уже существующих массивов или списков, особенно если они ‘рваные’ (с dtype=object), используйте np.array().

  • Для объединения массивов одинаковой формы в многомерную структуру (2D, 3D и т.д.) предпочтительнее np.vstack(), np.hstack() или np.concatenate(). Эти функции оптимизированы для работы с однородными данными и обеспечивают лучшую производительность по сравнению с np.append() или np.insert() при частых изменениях.

Заключение

Итак, мы рассмотрели различные подходы к созданию и объединению массивов в NumPy, от простых списков списков до мощных функций np.vstack, np.hstack и np.concatenate. Выбор метода зависит от ваших конкретных задач: создания новой структуры, расширения существующей или работы с разнородными данными. Понимание этих инструментов позволяет эффективно управлять многомерными данными, что критически важно для анализа и машинного обучения.


Добавить комментарий