2025, Nov 19 12:02
Добавляем столбец единиц в двумерный массив NumPy
Как корректно добавить константный столбец (столбец единиц) в двумерный массив NumPy: почему ломается list comprehension и решение через broadcast_to и hstack.
Добавить константный столбец к двумерному массиву NumPy кажется пустяком, но на практике это часто вызывает сложности, когда смешивают логику списков Python с семантикой NumPy. Ниже — краткое объяснение, почему наивный подход ломается и как аккуратно решить задачу штатными средствами NumPy.
Обзор задачи
У нас есть двумерный массив с одним столбцом, и нужно добавить справа столбец из единиц. Проще говоря, превратить столбец признаков в матрицу с дополнительным столбцом смещения (bias).
Неподходящий пример
Данные выглядят так:
import numpy as np
vals2d = np.array([[6.575],
[6.421],
[7.185],
[6.998],
[6.43 ],
[6.012],
[6.172],
[5.631],
[6.004],
[6.377],
[6.03 ]])
Естественная первая попытка — собрать новый массив через list comprehension:
augmented = np.array([[item, 1] for item in vals2d])
Это приводит к ошибке вида:
ValueError: попытка присвоить элементу массива последовательность. Запрошенный массив имеет неоднородную форму после 2 измерений. Обнаруженная форма: (506, 2) + неоднородная часть.
Почему это не работает
Итерация по двумерному массиву NumPy возвращает одномерные массивы, а не скаляры. Каждая итерация здесь даёт что-то вроде array([6.575]). В итоге list comprehension строит элементы вида [array([6.575]), 1] вместо [6.575, 1]. Когда NumPy пытается собрать из этого единый числовой массив, он замечает неоднородное содержимое и отказывается его приводить. Схожая ловушка: массивы не предназначены для хранения списков Python внутри; это ведёт к типу object, который почти наверняка не подходит для этой задачи.
Правильное решение на NumPy
Оставайтесь в мире NumPy. Расширьте скаляр с помощью broadcast_to и склейте столбцы через hstack:
result = np.hstack([vals2d, np.broadcast_to(1, vals2d.shape)])
Получится:
array([[6.575, 1. ],
[6.421, 1. ],
[7.185, 1. ],
[6.998, 1. ],
[6.43 , 1. ],
[6.012, 1. ],
[6.172, 1. ],
[5.631, 1. ],
[6.004, 1. ],
[6.377, 1. ],
[6.03 , 1. ]])
Если всё же хотите поправить list comprehension
Проблема в том, что каждый элемент — это одномерный массив. Извлеките скаляр или распакуйте строку перед формированием нового списка. Эти варианты убирают неоднородность, хотя циклы с массивами NumPy по возможности лучше не использовать:
np.array([[val, 1] for val in vals2d[:, 0]])
# or
np.array([[*row, 1] for row in vals2d])
Почему это важно
Операции, родные для NumPy, вроде broadcast_to и hstack, предотвращают несоответствие форм и не дают скатиться к типу object. Это сохраняет корректную структуру массива и обеспечивает предсказуемое поведение последующих вычислений.
Выводы
Работая с массивами NumPy, предпочитайте векторизованные операции вместо циклов Python. Если нужно добавить константный столбец, разверните константу до целевой формы и склейте по последней оси. Если видите ошибки о «неоднородной форме», проверьте, что именно возвращает ваша итерация, и убедитесь, что вы комбинируете однородные числовые структуры, а не списки или вложенные массивы.