เวกเตอร์แถวหรือคอลัมน์แบบโคลนนิ่ง


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 หรือระดับแปดเสียงทำได้ง่ายมาก:

 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)
หลุยส์ Mendo

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
(ไขว้ทำงานสำหรับอาร์เรย์ 2 มิติเช่นสำหรับสี่เหลี่ยมจัตุรัสในตัวอย่างหรือเมื่อเปลี่ยนเป็น(N,1)อาร์เรย์โดยใช้รูปทรง.reshape(-1, 1))
Mark

34
สิ่งนี้ไม่มีประสิทธิภาพสูง ใช้numpy.tileตามที่แสดงในคำตอบของ pv .
David Heffernan

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! ในระบบของฉันสำหรับเวกเตอร์ที่มี 10,000 องค์ประกอบซ้ำ 1,000 ครั้งtileวิธีนี้เร็วกว่าวิธี 19.5 เท่าในคำตอบที่ยอมรับในปัจจุบัน (ใช้วิธีการคูณตัวดำเนินการ)
ดร. Jan-Philip Gehrcke

1
ในส่วนที่สอง ("คอลัมน์ซ้ำ") คุณช่วยอธิบายว่าชุดวงเล็บเหลี่ยมชุดที่สองทำอะไรได้บ้าง [[1,2,3]]
Ant

@ ไม่รวมเข้าไปในอาร์เรย์ 2D ที่มีความยาว 1 ในแกนแรก (แนวตั้งบนหน้าจอของคุณ) และความยาว 3 ในแกนที่สอง (แนวนอนบนหน้าจอของคุณ) การเคลื่อนย้ายทำให้มีความยาว 3 ในแกนแรกและยาว 1 ในแกนที่สอง รูปร่างของการ(1, 3)คัดลอกคอลัมน์นี้ในสามครั้งซึ่งเป็นสาเหตุที่แถวของผลลัพธ์มีองค์ประกอบที่แตกต่างกันแต่ละรายการ
BallpointBen

นี่ควรเป็นคำตอบที่ได้รับการยอมรับเนื่องจากคุณสามารถส่งเวกเตอร์ใด ๆ ที่กำหนดค่าเริ่มต้นแล้วในขณะที่หนึ่งเวกเตอร์ที่ยอมรับสามารถใช้ได้เฉพาะเมื่อคุณเพิ่มเครื่องหมายจุลภาคในขณะที่คุณเริ่มต้นเวกเตอร์ ขอบคุณมาก!
Yohan Obadia

ฉันไม่สามารถทำงานนี้เพื่อแก้ปัญหา 2d to 3d :(
john ktejik

42

โปรดทราบว่าด้วยการใช้งานการกระจายสัญญาณของนัมมี่มักไม่จำเป็นต้องทำซ้ำแถวและคอลัมน์ ดูสิ่งนี้และนี่เพื่อดูคำอธิบาย

แต่การทำเช่นนี้การทำซ้ำและ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 มีประโยชน์เพิ่มเติมที่จะไม่คัดลอกข้อมูลจนกว่าจะต้องการ ดังนั้นหากคุณทำเช่นนี้เพื่อทวีคูณหรือเพิ่มไปยังอาร์เรย์ 3x3 อื่นการทำซ้ำนั้นไม่จำเป็น อ่านข้อมูลเกี่ยวกับการกระจายเสียงเพื่อรับแนวคิด
AFoglia

@AFoglia - จุดดี ฉันอัปเดตคำตอบเพื่อชี้ให้เห็น
tom10

1
ประโยชน์ของการใช้np.repeatvs np.tile?
mrgloom

@mrgloom: ไม่มีส่วนใหญ่สำหรับกรณีนี้ สำหรับอาร์เรย์ 1D ขนาดเล็กพวกมันคล้ายกันและไม่มีความแตกต่างอย่างมีนัยสำคัญ / ผลประโยชน์ / ความได้เปรียบ / ฯลฯ โดยส่วนตัวแล้วฉันพบความสมมาตรระหว่างการโคลนนิ่งแถวและคอลัมน์เพื่อให้เข้าใจได้ง่ายขึ้นและฉันไม่ชอบทรานสเลชันที่จำเป็นสำหรับกระเบื้อง แต่มันเป็นเรื่องของรสนิยม คำตอบของ Mateen Ulhaq ยังกล่าวว่าการทำซ้ำเร็วขึ้น แต่อาจขึ้นอยู่กับกรณีการใช้งานที่ถูกพิจารณาถึงแม้ว่าการทำซ้ำนั้นจะใกล้เคียงกับฟังก์ชัน 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 เป็นนามแฝงสำหรับไม่มี
john ktejik

ทำซ้ำเร็วกว่า: 5.56 ms = 5560 --s
Augusto Fadel

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

หากคุณมีดาต้าดาต้าแพนด้าและต้องการรักษา 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.]]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.