Nhân bản vô tính vectơ hàng hoặc cột vectơ


154

Đôi khi nó rất hữu ích để "sao chép" một vectơ hàng hoặc cột vào một ma trận. Bằng cách nhân bản, tôi có nghĩa là chuyển đổi một vectơ hàng như

[1,2,3]

Vào một ma trận

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

hoặc một vectơ cột như

[1
 2
 3
]

vào

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

Trong matlab hoặc octave, điều này được thực hiện khá dễ dàng:

 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

Tôi muốn lặp lại điều này trong numpy, nhưng không thành công

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

Tại sao phương thức đầu tiên ( In [16]) hoạt động? Có cách nào để đạt được nhiệm vụ này ở trăn một cách tao nhã hơn không?


6
Trong Matlab, lưu ý rằng việc sử dụng nhanh hơn nhiều repmat: repmat([1 2 3],3,1)hoặcrepmat([1 2 3].',1,3)
Luis Mendo

Octave cũng có repmat.
ma11hew28

Đối với những người muốn làm tương tự với kiểm tra khung dữ liệu gấu trúc, tile_df liên kết ở đây
zelusp

Câu trả lời:


79

Đây là một cách trang nhã, Pythonic để làm điều đó:

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

vấn đề với [16]dường như là chuyển vị không có tác dụng đối với một mảng. có lẽ bạn muốn một ma trận thay thế:

>>> 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
(chuyển đổi hoạt động cho các mảng 2D, ví dụ cho hình vuông trong ví dụ hoặc khi chuyển thành (N,1)mảng hình dạng bằng cách sử dụng .reshape(-1, 1))
Đánh dấu

34
Điều này là không hiệu quả cao. Sử dụng numpy.tilenhư thể hiện trong câu trả lời của pv .
David Heffernan

302

Sử dụng numpy.tile:

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

hoặc để lặp lại các cột:

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

16
Upvote! Trên hệ thống của tôi, đối với một vectơ có 10000 phần tử được lặp lại 1000 lần, tilephương thức này nhanh hơn 19,5 lần so với phương thức trong câu trả lời hiện được chấp nhận (sử dụng phương pháp nhân-toán tử-toán tử).
Tiến sĩ Jan-Philip Gehrcke

1
Trong phần thứ hai ("các cột lặp lại"), bạn có thể giải thích tập hợp dấu ngoặc vuông thứ hai làm gì không, tức là [[1,2,3]]
Ant

@Ant nó tạo thành một mảng 2D có chiều dài 1 ở trục thứ nhất (dọc trên màn hình của bạn) và chiều dài 3 ở trục thứ hai (nằm ngang trên màn hình của bạn). Chuyển vị sau đó làm cho nó có chiều dài 3 ở trục thứ nhất và chiều dài 1 ở trục thứ hai. Một hình dạng gạch của các (1, 3)bản sao cột này trong ba lần, đó là lý do tại sao các hàng của kết quả chứa một phần tử riêng biệt mỗi phần.
BallpointBen

Đây phải là câu trả lời được chấp nhận vì bạn có thể vượt qua bất kỳ vectơ nào đã được khởi tạo trong khi vectơ được chấp nhận chỉ có thể hoạt động nếu bạn thêm dấu phẩy trong khi bạn khởi tạo vectơ. Cảm ơn !
Yohan Obadia

Tôi không thể làm điều này để làm việc cho giải pháp 2d đến 3d :(
john ktejik

41

Đầu tiên lưu ý rằng với các hoạt động phát sóng của numpy thường không cần thiết phải sao chép các hàng và cột. Xem cái nàycái này để mô tả.

Nhưng để làm điều này, lặp lại newaxis có lẽ là cách tốt nhất

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

Ví dụ này là cho một vectơ hàng, nhưng áp dụng điều này cho một vectơ cột là hy vọng rõ ràng. Lặp lại dường như đánh vần điều này tốt, nhưng bạn cũng có thể làm điều đó thông qua phép nhân như trong ví dụ của bạn

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 có lợi ích bổ sung mà nó không thực sự sao chép dữ liệu cho đến khi cần. Vì vậy, nếu bạn đang làm điều này để nhân hoặc thêm vào một mảng 3x3 khác, việc lặp lại là không cần thiết. Đọc lên phát sóng numpy để có được ý tưởng.
AFoglia

@AFoglia - Điểm tốt. Tôi cập nhật câu trả lời của tôi để chỉ ra điều này.
tom10

1
Lợi ích của việc sử dụng np.repeatvs np.tile?
mrgloom

@mrgloom: Không, chủ yếu, cho trường hợp này. Đối với một mảng 1D nhỏ, chúng tương tự nhau và không có sự khác biệt đáng kể / lợi ích / lợi thế / v.v. Cá nhân, tôi thấy sự đối xứng giữa nhân bản hàng và cột trở nên trực quan hơn và tôi không thích sự chuyển đổi cần thiết cho gạch, nhưng đó chỉ là vấn đề của hương vị. Câu trả lời của Mateen Ulhaq cũng cho biết việc lặp lại nhanh hơn, nhưng điều này có thể phụ thuộc vào trường hợp sử dụng chính xác được xem xét, mặc dù việc lặp lại gần với chức năng C hơn, do đó có thể sẽ vẫn nhanh hơn một chút. Trong 2D họ có những hành vi khác nhau nên nó quan trọng ở đó.
tom10

12

Để cho:

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

Phân bổ không chi phí

Một khung nhìn không mất thêm bộ nhớ. Do đó, những tuyên bố này là tức thời:

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

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

Phân bổ cưỡng bức

Nếu bạn muốn buộc các nội dung nằm trong bộ nhớ:

>>> %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)

Tất cả ba phương pháp đều có cùng tốc độ.

Tính toán

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

Tất cả ba phương pháp đều có cùng tốc độ.


Phần kết luận

Nếu bạn muốn sao chép trước khi tính toán, hãy xem xét sử dụng một trong các phương pháp "phân bổ chi phí bằng không". Bạn sẽ không phải chịu hình phạt hiệu suất của "phân bổ bắt buộc".


8

Tôi nghĩ rằng sử dụng phát sóng trong numpy là tốt nhất và nhanh hơn

Tôi đã làm một so sánh như sau

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

sử dụng phát sóng nhanh hơn khoảng 15 lần


Bạn có thể lập chỉ mục Noneđể làm điều tương tự.
DanielSank

Newaxis là gì?!
dreab

np.newaxis là bí danh cho Không ai
john ktejik

lặp lại nhanh hơn: 5,56 ms = 5560 lượt
Augusto Fadel

4

Một giải pháp rõ ràng là sử dụng chức năng sản phẩm bên ngoài của NumPy với một vectơ:

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

cho ncác hàng lặp lại. Chuyển thứ tự đối số để có được các cột lặp lại. Để có được số lượng hàng và cột bằng nhau, bạn có thể làm

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

3

Bạn có thể dùng

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

gạch sẽ tạo ra các đại diện của vector

và định hình lại sẽ cho nó hình dạng bạn muốn


1

Nếu bạn có một khung dữ liệu gấu trúc và muốn bảo tồn các kiểu chữ, thậm chí là phân loại, đây là một cách nhanh chóng để làm điều đó:

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)

sản lượng:

[[ 1.  1.  1.]
 [ 2.  2.  2.]
 [ 3.  3.  3.]]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.