คัดลอกอาร์เรย์ 2D ไปยังมิติที่ 3 N ครั้ง (Python)


116

ฉันต้องการคัดลอกอาร์เรย์ 2D ที่เป็นตัวเลขเป็นมิติที่สาม ตัวอย่างเช่นกำหนดอาร์เรย์ numpy (2D):

import numpy as np
arr = np.array([[1,2],[1,2]])
# arr.shape = (2, 2)

แปลงเป็นเมทริกซ์ 3 มิติด้วย N สำเนาดังกล่าวในมิติใหม่ ดำเนินการarrกับ N = 3 ผลลัพธ์ควรเป็น:

new_arr = np.array([[[1,2],[1,2]],[[1,2],[1,2]],[[1,2],[1,2]]])
# new_arr.shape = (3, 2, 2)

คำตอบ:


156

อาจเป็นวิธีที่สะอาดที่สุดคือใช้np.repeat:

a = np.array([[1, 2], [1, 2]])
print(a.shape)
# (2,  2)

# indexing with np.newaxis inserts a new 3rd dimension, which we then repeat the
# array along, (you can achieve the same effect by indexing with None, see below)
b = np.repeat(a[:, :, np.newaxis], 3, axis=2)

print(b.shape)
# (2, 2, 3)

print(b[:, :, 0])
# [[1 2]
#  [1 2]]

print(b[:, :, 1])
# [[1 2]
#  [1 2]]

print(b[:, :, 2])
# [[1 2]
#  [1 2]]

ต้องบอกว่าคุณมักจะสามารถหลีกเลี่ยงการทำซ้ำอาร์เรย์ของคุณทั้งหมดโดยใช้การกระจายเสียง ตัวอย่างเช่นสมมติว่าฉันต้องการเพิ่ม(3,)เวกเตอร์:

c = np.array([1, 2, 3])

ถึงa. ฉันสามารถคัดลอกเนื้อหาa3 ครั้งในมิติที่สามจากนั้นคัดลอกเนื้อหาของcสองครั้งทั้งในมิติแรกและมิติที่สองเพื่อให้ทั้งสองอาร์เรย์ของฉันเป็น(2, 2, 3)แล้วคำนวณผลรวมของมัน อย่างไรก็ตามการทำสิ่งนี้ทำได้ง่ายและรวดเร็วกว่ามาก:

d = a[..., None] + c[None, None, :]

ที่นี่a[..., None]มีรูปร่าง(2, 2, 1)และc[None, None, :]มีรูปร่าง(1, 1, 3)*. เมื่อฉันคำนวณผลรวมผลลัพธ์จะได้รับการ 'ออกอากาศ' ตามขนาดของขนาด 1 ทำให้ฉันได้ผลลัพธ์ของรูปร่าง(2, 2, 3):

print(d.shape)
# (2,  2, 3)

print(d[..., 0])    # a + c[0]
# [[2 3]
#  [2 3]]

print(d[..., 1])    # a + c[1]
# [[3 4]
#  [3 4]]

print(d[..., 2])    # a + c[2]
# [[4 5]
#  [4 5]]

การแพร่ภาพเป็นเทคนิคที่มีประสิทธิภาพมากเนื่องจากหลีกเลี่ยงค่าใช้จ่ายเพิ่มเติมที่เกี่ยวข้องกับการสร้างสำเนาอาร์เรย์อินพุตของคุณซ้ำ ๆ ในหน่วยความจำ


* แม้ว่าฉันจะรวมไว้เพื่อความชัดเจน แต่Noneดัชนีcก็ไม่จำเป็นจริงๆ - คุณสามารถทำได้a[..., None] + cเช่นออกอากาศ(2, 2, 1)อาร์เรย์กับ(3,)อาร์เรย์ เนื่องจากถ้าหนึ่งในอาร์เรย์มีขนาดน้อยกว่าอีกอันหนึ่งจะต้องเข้ากันได้เฉพาะมิติต่อท้ายของอาร์เรย์ทั้งสองเท่านั้น เพื่อให้ตัวอย่างที่ซับซ้อนมากขึ้น:

a = np.ones((6, 1, 4, 3, 1))  # 6 x 1 x 4 x 3 x 1
b = np.ones((5, 1, 3, 2))     #     5 x 1 x 3 x 2
result = a + b                # 6 x 5 x 4 x 3 x 2

เพื่อตรวจสอบว่าจริงให้ผลที่ถูกต้องคุณยังสามารถพิมพ์ออกb[:,:,0], และb[:,:,1] b[:,:,2]ชิ้นส่วนมิติที่สามแต่ละชิ้นเป็นสำเนาของอาร์เรย์ 2D ดั้งเดิม print(b)นี้ไม่เป็นที่เห็นได้ชัดเพียงแค่มองไปที่
ely

ความแตกต่างระหว่าง None และ np.newaxis คืออะไร? เมื่อฉันทดสอบมันก็ให้ผลลัพธ์เหมือนกัน
เสาหิน

1
@wedran เหมือนกันทุกประการ - np.newaxisเป็นเพียงนามแฝงของNone
ali_m

27

numpy.dstackอีกวิธีหนึ่งคือการใช้งาน สมมติว่าคุณต้องการทำซ้ำเมทริกซ์a num_repeatsครั้ง:

import numpy as np
b = np.dstack([a]*num_repeats)

เคล็ดลับคือการรวมเมทริกซ์aไว้ในรายการขององค์ประกอบเดียวจากนั้นใช้ตัว*ดำเนินการเพื่อทำซ้ำองค์ประกอบในรายการnum_repeatsครั้งนี้

ตัวอย่างเช่นถ้า:

a = np.array([[1, 2], [1, 2]])
num_repeats = 5

สิ่งนี้จะทำซ้ำอาร์เรย์[1 2; 1 2]5 ครั้งในมิติที่สาม ในการตรวจสอบ (ใน IPython):

In [110]: import numpy as np

In [111]: num_repeats = 5

In [112]: a = np.array([[1, 2], [1, 2]])

In [113]: b = np.dstack([a]*num_repeats)

In [114]: b[:,:,0]
Out[114]: 
array([[1, 2],
       [1, 2]])

In [115]: b[:,:,1]
Out[115]: 
array([[1, 2],
       [1, 2]])

In [116]: b[:,:,2]
Out[116]: 
array([[1, 2],
       [1, 2]])

In [117]: b[:,:,3]
Out[117]: 
array([[1, 2],
       [1, 2]])

In [118]: b[:,:,4]
Out[118]: 
array([[1, 2],
       [1, 2]])

In [119]: b.shape
Out[119]: (2, 2, 5)

ในตอนท้ายเราจะเห็นว่ารูปร่างของเมทริกซ์2 x 2มี 5 ชิ้นในมิติที่สาม


สิ่งนี้เปรียบเทียบกับreshapeอย่างไร? เร็วขึ้น? ให้โครงสร้างเดียวกัน? มันสวยกว่าแน่นอน
Ander Biguri

@AnderBiguri ฉันไม่เคยเปรียบเทียบ ... ฉันใส่ไว้ที่นี่เพื่อความสมบูรณ์เป็นหลัก มันจะน่าสนใจเมื่อเวลาผ่านไปและดูความแตกต่าง
rayryeng

1
ฉันเพิ่งทำ img = np.dstack ([arr] * 3) และทำงานได้ดี! ขอบคุณ
thanos.a

1
คิดว่าฉันสามารถเสนอผลลัพธ์ที่ดูแล้วเพื่อประสิทธิภาพ เป็นโพสต์เก่า ๆ อาจมีคนพลาดไปแล้ว เพิ่มวิธีแก้ปัญหาในการถามตอบนี้
Divakar

1
โซลูชันที่อ่านง่ายที่สุดของ IMHO แต่จะเป็นการดีที่จะเปรียบเทียบกับวิธีการอื่น ๆ เพื่อเปรียบเทียบ
mrgloom

21

ใช้มุมมองและรับรันไทม์ฟรี! ขยายn-dimอาร์เรย์ทั่วไปเป็นn+1-dim

แนะนำในNumPy1.10.0เราสามารถใช้ประโยชน์จากnumpy.broadcast_toการสร้าง3Dมุมมองใน2Dอาร์เรย์อินพุต ประโยชน์ที่ได้คือไม่มีค่าใช้จ่ายหน่วยความจำเพิ่มเติมและรันไทม์ฟรี สิ่งนี้จำเป็นอย่างยิ่งในกรณีที่อาร์เรย์มีขนาดใหญ่และเราสามารถทำงานกับมุมมองได้ นอกจากนี้ยังสามารถใช้ได้กับn-dimกรณีทั่วไป

ฉันจะใช้คำนี้stackแทนcopyเนื่องจากผู้อ่านอาจสับสนกับการคัดลอกอาร์เรย์ที่สร้างสำเนาหน่วยความจำ

กองตามแกนแรก

หากเราต้องการซ้อนอินพุตarrตามแกนแรกวิธีแก้ปัญหาnp.broadcast_toในการสร้าง3Dมุมมองจะเป็น -

np.broadcast_to(arr,(3,)+arr.shape) # N = 3 here

กองตามแกนที่สาม / แกนสุดท้าย

ในการจัดเรียงอินพุตarrตามแกนที่สามวิธีการสร้าง3Dมุมมองจะเป็น -

np.broadcast_to(arr[...,None],arr.shape+(3,))

ถ้าเราต้องการจริงสำเนาหน่วยความจำที่เราสามารถเสมอผนวก.copy()มี ดังนั้นการแก้ปัญหาจะเป็น -

np.broadcast_to(arr,(3,)+arr.shape).copy()
np.broadcast_to(arr[...,None],arr.shape+(3,)).copy()

ต่อไปนี้เป็นวิธีการทำงานของการเรียงซ้อนสำหรับทั้งสองกรณีซึ่งแสดงพร้อมกับข้อมูลรูปร่างของเคสตัวอย่าง -

# Create a sample input array of shape (4,5)
In [55]: arr = np.random.rand(4,5)

# Stack along first axis
In [56]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[56]: (3, 4, 5)

# Stack along third axis
In [57]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[57]: (4, 5, 3)

โซลูชันเดียวกันจะทำงานเพื่อขยายn-dimอินพุตเพื่อn+1-dimดูเอาต์พุตตามแกนแรกและแกนสุดท้าย มาสำรวจกรณีสลัวที่สูงกว่ากัน -

กรณีอินพุต 3D:

In [58]: arr = np.random.rand(4,5,6)

# Stack along first axis
In [59]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[59]: (3, 4, 5, 6)

# Stack along last axis
In [60]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[60]: (4, 5, 6, 3)

กรณีอินพุต 4D:

In [61]: arr = np.random.rand(4,5,6,7)

# Stack along first axis
In [62]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[62]: (3, 4, 5, 6, 7)

# Stack along last axis
In [63]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[63]: (4, 5, 6, 7, 3)

และอื่น ๆ

การกำหนดเวลา

ลองใช้2Dกรณีตัวอย่างขนาดใหญ่และรับการกำหนดเวลาและตรวจสอบผลลัพธ์ว่าเป็นviewไฟล์.

# Sample input array
In [19]: arr = np.random.rand(1000,1000)

มาพิสูจน์กันว่าวิธีแก้ปัญหาที่เสนอนั้นเป็นมุมมองที่แท้จริง เราจะใช้การเรียงซ้อนตามแกนแรก (ผลลัพธ์จะคล้ายกันมากสำหรับการเรียงซ้อนตามแกนที่สาม) -

In [22]: np.shares_memory(arr, np.broadcast_to(arr,(3,)+arr.shape))
Out[22]: True

มาดูการกำหนดเวลาเพื่อแสดงว่ามันฟรี -

In [20]: %timeit np.broadcast_to(arr,(3,)+arr.shape)
100000 loops, best of 3: 3.56 µs per loop

In [21]: %timeit np.broadcast_to(arr,(3000,)+arr.shape)
100000 loops, best of 3: 3.51 µs per loop

เป็นมุมมองที่เพิ่มขึ้นNจาก3การ3000เปลี่ยนแปลงอะไรในการกำหนดเวลาและทั้งสองมีเล็กน้อยต่อหน่วยระยะเวลา ดังนั้นจึงมีประสิทธิภาพทั้งในหน่วยความจำและประสิทธิภาพ!


3
A=np.array([[1,2],[3,4]])
B=np.asarray([A]*N)

แก้ไข @ Mr.F เพื่อรักษาลำดับมิติ:

B=B.T

ผลนี้ในการ N x 2 x 2 อาร์เรย์สำหรับฉันเช่นB.shapeพิมพ์สำหรับสิ่งที่คุ้มค่าของ(N, 2, 2) Nหากคุณเปลี่ยนBไปB.Tแล้วจะตรงกับผลลัพธ์ที่คาดไว้
ely

@ Mr.F - คุณถูกต้อง สิ่งนี้จะออกอากาศไปตามมิติแรกและการทำเช่นB[0], B[1],...นั้นจะทำให้คุณมีชิ้นส่วนที่ถูกต้องซึ่งฉันจะเถียงและบอกว่ามันง่ายกว่าที่จะพิมพ์มากกว่าการใช้B[:,:,0], B[:,:,1]ฯลฯ
rayryeng

อาจจะง่ายกว่าในการพิมพ์ แต่ตัวอย่างเช่นหากคุณทำสิ่งนี้กับข้อมูลรูปภาพส่วนใหญ่จะไม่ถูกต้องเนื่องจากอัลกอริทึมเกือบทั้งหมดคาดว่าจะใช้รูปแบบของพีชคณิตเชิงเส้นสำหรับช่องพิกเซล 2 มิติ เป็นการยากที่จะจินตนาการถึงแอปพลิเคชั่นที่คุณเริ่มต้นด้วยอาร์เรย์ 2 มิติการปฏิบัติต่อแถวและคอลัมน์ด้วยรูปแบบหนึ่ง ๆ จากนั้นคุณต้องการสำเนาหลายชุดของสิ่งเดียวกันนั้นขยายไปสู่แกนใหม่ แต่ทันใดนั้นคุณต้องการให้แกนแรกเปลี่ยนความหมายเป็น เป็นแกนใหม่ ...
ely

@ Mr.F - โอ้แน่นอน ฉันเดาไม่ออกว่าคุณจะต้องการใช้เมทริกซ์ 3 มิติอะไรในอนาคต ที่กล่าวมาทั้งหมดขึ้นอยู่กับการใช้งาน FWIW ฉันชอบB[:,:,i]และนั่นคือสิ่งที่ฉันคุ้นเคย
rayryeng

3

ตอนนี้สามารถทำได้โดยใช้np.tileดังนี้:

import numpy as np

a = np.array([[1,2],[1,2]])
b = np.tile(a,(3, 1,1))

b.shape
(3,2,2)

b
array([[[1, 2],
        [1, 2]],

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

       [[1, 2],
        [1, 2]]])

2

นี่คือตัวอย่างการแพร่ภาพที่ทำตามที่ร้องขอทุกประการ

a = np.array([[1, 2], [1, 2]])
a=a[:,:,None]
b=np.array([1]*5)[None,None,:]

จากนั้นb*aเป็นผลลัพธ์ที่ต้องการและ(b*a)[:,:,0]ก่อให้เกิดarray([[1, 2],[1, 2]])ซึ่งเป็นต้นฉบับaเช่นเดียวกับ(b*a)[:,:,1]ฯลฯ

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