"Клонування" векторів рядків або стовпців


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]
]

У матлабі чи октаві це робиться досить легко:

 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]) не працював? Чи є спосіб досягти цього завдання в пітоні більш елегантним способом?


6
У Matlab зауважте, що набагато швидше використовувати repmat: repmat([1 2 3],3,1)абоrepmat([1 2 3].',1,3)
Луїс Мендо

Октава також є repmat.
ma11hew28

Для тих, хто хоче зробити подібне з пандами для оформлення даних, tile_df пов’язаних тут
zelusp

Відповіді:


80

Ось елегантний, пітонічний спосіб зробити це:

>>> 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як показано у відповіді пв .
Девід Геффернан

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
Оновлення! У моїй системі для вектора з 10000 елементами, повторених 1000 разів, tileметод в 19,5 разів швидший, ніж метод у прийнятій на даний момент відповідь (використовуючи метод множення-оператор).
Д-р Ян-Філіп Геркк

1
У другому розділі ("повторювані стовпці") ви можете пояснити, що робить другий набір квадратних дужок, тобто [[1,2,3]]
Ant Ant

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

Це має бути прийнятою відповіддю, оскільки ви можете передати будь-який вектор, уже ініціалізований, тоді як прийнятий може працювати, лише якщо ви додасте кому під час ініціалізації вектора. Дякую !
Йохан Обадія

Я не можу змусити це працювати над рішенням від 2d до 3d :(
Джон ktejik

42

Спочатку зауважте, що при операціях трансляції numpy зазвичай не потрібно дублювати рядки та стовпці. Дивіться це та це для описів.

Але для цього повторення та новеосі , мабуть, найкращий спосіб

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 має додаткову перевагу в тому, що він фактично не копіює дані, поки це не потрібно. Отже, якщо ви робите це для множення або додавання до іншого масиву 3x3, повторення не потрібне. Прочитайте про бездротове мовлення, щоб отримати ідею.
AFoglia

@AFoglia - Добрий момент. Я оновив свою відповідь, щоб вказати на це.
tom10

1
Які переваги використання np.repeatvs np.tile?
mrgloom

@mrgloom: В основному, для цього випадку немає. Для невеликого 1D масиву вони схожі, і немає суттєвої різниці / вигоди / переваги / тощо. Особисто я вважаю симетрію між клонуванням рядка та стовпця більш інтуїтивно зрозумілою, і мені не подобається транспонування, необхідне для плитки, але це лише питання смаку. Відповідь Матіна Ульхака також говорить, що повторення відбувається швидше, але це може залежати від конкретного випадку використання, який розглядається, хоча повторення набагато наближається до функціональності С, тому, ймовірно, залишиться дещо швидшим. У 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 ?!
дрімота

np.newaxis - псевдонім для None
John ktejik

повтор був швидшим: 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

Якщо у вас є фрейм даних панд і хочете зберегти типи, навіть категоричні, це швидкий спосіб зробити це:

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.