«Клонирование» векторов строк или столбцов


155

Иногда полезно «клонировать» вектор строки или столбца в матрицу. Под клонированием я подразумеваю преобразование вектора строки, такого как

[1,2,3]

В матрицу

[[1,2,3]
 [1,2,3]
 [1,2,3]
]

или вектор столбца, такой как

[1
 2
 3
]

в

[[1,1,1]
 [2,2,2]
 [3,3,3]
]

В matlab или octave это делается довольно легко:

 x = [1,2,3]
 a = ones(3,1) * x
 a =

    1   2   3
    1   2   3
    1   2   3

 b = (x') * ones(1,3)
 b =

    1   1   1
    2   2   2
    3   3   3

Я хочу повторить это по-тупому, но безуспешно

In [14]: x = array([1,2,3])
In [14]: ones((3,1)) * x
Out[14]:
array([[ 1.,  2.,  3.],
       [ 1.,  2.,  3.],
       [ 1.,  2.,  3.]])
# so far so good
In [16]: x.transpose() * ones((1,3))
Out[16]: array([[ 1.,  2.,  3.]])
# DAMN
# I end up with 
In [17]: (ones((3,1)) * x).transpose()
Out[17]:
array([[ 1.,  1.,  1.],
       [ 2.,  2.,  2.],
       [ 3.,  3.,  3.]])

Почему не первый метод ( In [16]) не работает? Есть ли способ выполнить эту задачу в Python более элегантным способом?


6
В Matlab обратите внимание, что его использовать гораздо быстрее repmat: repmat([1 2 3],3,1)илиrepmat([1 2 3].',1,3)
Luis Mendo

Октава тоже имеет repmat.
ma11hew28

Для тех, кто хочет сделать то же самое с извлечением данных из панд, приведенным tile_df здесь
zelusp

Ответы:


80

Вот элегантный Pythonic способ сделать это:

>>> array([[1,2,3],]*3)
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

>>> array([[1,2,3],]*3).transpose()
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

[16]похоже, проблема в том, что транспонирование не влияет на массив. вы, вероятно, хотите вместо матрицы:

>>> x = array([1,2,3])
>>> x
array([1, 2, 3])
>>> x.transpose()
array([1, 2, 3])
>>> matrix([1,2,3])
matrix([[1, 2, 3]])
>>> matrix([1,2,3]).transpose()
matrix([[1],
        [2],
        [3]])

1
(транспонировать произведения для двумерных массивов, например, для квадратного в примере, или при превращении в (N,1)массив -образной формы с помощью .reshape(-1, 1))
Марк

34
Это крайне неэффективно. Используйте, numpy.tileкак показано в ответе Pv .
Дэвид Хеффернан

304

Используйте numpy.tile:

>>> tile(array([1,2,3]), (3, 1))
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

или для повторяющихся столбцов:

>>> tile(array([[1,2,3]]).transpose(), (1, 3))
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

16
Upvote! В моей системе для вектора с 10000 элементов, повторяемых 1000 раз, tileметод в 19,5 раз быстрее, чем метод из принятого в настоящее время ответа (с использованием метода умножения-оператора).
Доктор Ян-Филипп Герке

1
Во втором разделе («повторяющиеся столбцы»), вы можете объяснить, что делает второй набор квадратных скобок, то есть [[1,2,3]]
Муравей

@ Он превращается в двумерный массив с длиной 1 на первой оси (вертикальной на вашем экране) и длиной 3 на второй оси (горизонтальной на вашем экране). Затем транспонирование делает его имеющим длину 3 на первой оси и длину 1 на второй оси. Форма мозаики (1, 3)копирует этот столбец более трех раз, поэтому строки результата содержат каждый отдельный элемент.
BallpointBen

Это должен быть принятый ответ, поскольку вы можете передать любой вектор, который уже инициализирован, в то время как принятый может работать, только если вы добавите запятую во время инициализации вектора. Спасибо !
Йохан Обадия

Я не могу заставить это работать для решения 2d в 3d :(
Джон Ктеджик

42

Во-первых, обратите внимание, что при вещании Numpy обычно нет необходимости дублировать строки и столбцы. Смотрите это и это для описания.

Но сделать это, повторить и newaxis , вероятно, лучший способ

In [12]: x = array([1,2,3])

In [13]: repeat(x[:,newaxis], 3, 1)
Out[13]: 
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

In [14]: repeat(x[newaxis,:], 3, 0)
Out[14]: 
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

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

In [15]: x = array([[1, 2, 3]])  # note the double brackets

In [16]: (ones((3,1))*x).transpose()
Out[16]: 
array([[ 1.,  1.,  1.],
       [ 2.,  2.,  2.],
       [ 3.,  3.,  3.]])

5
newaxis имеет дополнительное преимущество, заключающееся в том, что он на самом деле не копирует данные, пока в этом нет необходимости. Поэтому, если вы делаете это для умножения или добавления в другой массив 3х3, повторение не требуется. Читайте о вещах, чтобы получить представление.
AFoglia

@AFoglia - Хороший вопрос. Я обновил свой ответ, чтобы указать на это.
Tom10

1
Какие преимущества использования np.repeatпротив np.tile?
Mrgloom

@mrgloom: Нет, в основном, для этого случая. Для небольшого 1D массива они похожи, и нет существенной разницы / выгоды / преимущества / и т.д. Лично я нахожу симметрию между клонированием строк и столбцов более интуитивной, и мне не нравится транспонирование, необходимое для плитки, но это всего лишь вопрос вкуса. В ответе Матина Ульгака также говорится, что повторение происходит быстрее, но это может зависеть от конкретного рассматриваемого варианта использования, хотя повторение гораздо ближе к функциональности C, поэтому, вероятно, останется несколько быстрее. В 2D они ведут себя по-разному, поэтому это важно.
Tom10

12

Позволять:

>>> n = 1000
>>> x = np.arange(n)
>>> reps = 10000

Распределение по нулевой стоимости

Вид не несет никакой дополнительной памяти. Таким образом, эти объявления являются мгновенными:

# New axis
x[np.newaxis, ...]

# Broadcast to specific shape
np.broadcast_to(x, (reps, n))

Принудительное распределение

Если вы хотите, чтобы содержимое находилось в памяти:

>>> %timeit np.array(np.broadcast_to(x, (reps, n)))
10.2 ms ± 62.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit np.repeat(x[np.newaxis, :], reps, axis=0)
9.88 ms ± 52.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit np.tile(x, (reps, 1))
9.97 ms ± 77.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Все три метода примерно одинаковой скорости.

вычисление

>>> a = np.arange(reps * n).reshape(reps, n)
>>> x_tiled = np.tile(x, (reps, 1))

>>> %timeit np.broadcast_to(x, (reps, n)) * a
17.1 ms ± 284 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit x[np.newaxis, :] * a
17.5 ms ± 300 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit x_tiled * a
17.6 ms ± 240 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Все три метода примерно одинаковой скорости.


Вывод

Если вы хотите выполнить репликацию перед вычислением, рассмотрите возможность использования одного из методов «нулевого распределения». Вы не будете страдать от производительности "принудительного распределения".


8

Я думаю, что использование трансляции в NumPy является лучшим и быстрее

Я сделал сравнение следующим образом

import numpy as np
b = np.random.randn(1000)
In [105]: %timeit c = np.tile(b[:, newaxis], (1,100))
1000 loops, best of 3: 354 µs per loop

In [106]: %timeit c = np.repeat(b[:, newaxis], 100, axis=1)
1000 loops, best of 3: 347 µs per loop

In [107]: %timeit c = np.array([b,]*100).transpose()
100 loops, best of 3: 5.56 ms per loop

примерно в 15 раз быстрее, используя трансляцию


Вы можете индексировать, Noneчтобы сделать то же самое.
DanielSank

что такое newaxis ?!
dreab

np.newaxis - псевдоним для None
Джон Ктеджик,

повтор был быстрее: 5,56 мс = 5560 мкс
Аугусто Фадель

4

Одно чистое решение - использовать функцию внешнего продукта NumPy с вектором единиц:

np.outer(np.ones(n), x)

дает nповторяющиеся строки. Переключите порядок аргументов, чтобы получить повторяющиеся столбцы. Чтобы получить одинаковое количество строк и столбцов, вы можете сделать

np.outer(np.ones_like(x), x)

3

Ты можешь использовать

np.tile(x,3).reshape((4,3))

плитка будет генерировать повторения вектора

и изменить форму придаст ей желаемую форму


1

Если у вас есть датафрейм Pandas и вы хотите сохранить dtypes, даже категориальные, это быстрый способ сделать это:

import numpy as np
import pandas as pd
df = pd.DataFrame({1: [1, 2, 3], 2: [4, 5, 6]})
number_repeats = 50
new_df = df.reindex(np.tile(df.index, number_repeats))

-1
import numpy as np
x=np.array([1,2,3])
y=np.multiply(np.ones((len(x),len(x))),x).T
print(y)

выходы:

[[ 1.  1.  1.]
 [ 2.  2.  2.]
 [ 3.  3.  3.]]
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.