Updated:

ndarray의 차원을 변경하거나 합칠 때 shape을 먼저 생각하면 계산하기 편하다.

import numpy as np

7.1. ndarray ndims 변경

reshape을 활용한 차원 변경

reshape을 그대로 이용하는 것이다. 단, 변경된 shape이 원래 size를 수용할 수 있어야한다.

  • np.reshape(a,newshape), a.reshape(newshape): reshape으로 ndims를 ‘증가/감소’할 수 있다. / reshape시 변경된 shape이 원래 size를 수용할 수 있어야한다.
a = np.arange(12)
print(f"a.shape: {a.shape}")

b = np.reshape(a,(3,4))
print(f"b.shape: {b.shape}")

c = np.reshape(b, (-1,))
print(f"c.shape: {c.shape}")
a.shape: (12,)
b.shape: (3, 4)
c.shape: (12,)
a = np.arange(12).reshape((3,4))
print(f"a.shape: {a.shape}")

b = np.reshape(a,(1,*a.shape))
print(f"b.shape: {b.shape}")
a.shape: (3, 4)
b.shape: (1, 3, 4)

slicing을 활용한 ndims 추가

맨 앞, 맨 뒤에만 추가할 수 있다.

  • np.newaxis: 새로운 axis를 추가하기위해 사용되는 변수, None 값을 지닌다.
  • a[np.newaxis, …], a[… ,np.newaxis] : slicing을 이용해 a의 ndims를 1개 더 증가시킬 수 있다.
print(np.newaxis)

a = np.arange(12)
print(f"a.shape: {a.shape}")

b = a[np.newaxis,...]
print(f"b.shape: {b.shape}")

c = a[...,np.newaxis,np.newaxis]
print(f"c.shape: {c.shape}")

d = a[np.newaxis, ... , np.newaxis]
print(f"d.shape: {d.shape}")
None
a.shape: (12,)
b.shape: (1, 12)
c.shape: (12, 1, 1)
d.shape: (1, 12, 1)

np.expand_dims()를 활용한 ndims 추가

  • np.expand_dims(a,axis=(n0,n1,…)): axis parameter를 이용하여 shape에 원하는 위치의 axis를 추가할 수 있다. / axis parameter에 원하는 위치를 튜플로 묶으면 동시에 추가할 수 있다.
a = np.arange(12)
print(f"a.shape: {a.shape}")

b = np.expand_dims(a,axis=0)
print(f"b.shape: {b.shape}")

c = np.expand_dims(a,axis=1)
print(f"c.shape: {c.shape}")

d = np.expand_dims(a,axis=(0,2))
print(f"d.shape: {d.shape}")
a.shape: (12,)
b.shape: (1, 12)
c.shape: (12, 1)
d.shape: (1, 12, 1)

np.squeeze()를 활용한 차원 감소

  • np.squeeze(a), a.squeeze(): shape에서 axis가 1인 곳을 모두 없애고 나머지 shape만 남겨둔다.
  • np.squeeze(a,axis=()): parameter에 axis를 추가하면 원하는 위치에 있는 값이 1인 axis를 없앨 수 있다. (그 위치의 axis값이 1이 아니면 안된다.)
a = np.arange(20).reshape((1,1,2,1,5,2))
print(f"a.shape: {a.shape}")

b = np.squeeze(a)
print(f"b.shape: {b.shape}")

c = np.squeeze(a,axis=(0,1))
print(f"c.shape: {c.shape")
  File "<ipython-input-6-70a0d6ece571>", line 8
    print(f"c.shape: {c.shape")
          ^
SyntaxError: f-string: expecting '}'

7.2. axis swap, move, transpose

swap

  • np.swapaxes(a,axis1,axis2): axis1과 axis2를 swap한다.
a = np.random.randint(0,5,(1,3,4))
print(f"a.shape: {a.shape}")

b = np.swapaxes(a,0,1)
print(f"b.shape: {b.shape}")

c = np.swapaxes(a,0,-1)
print(f"c.shape: {c.shape}")
a.shape: (1, 3, 4)
b.shape: (3, 1, 4)
c.shape: (4, 3, 1)

move

  • np.moveaxis(a,source,destination): source 위치의 axis를 destination 위치로 이동시킨다.
a = np.random.randint(0,5,(1,3,4,5))
print(f"a.shape: {a.shape}")

b = np.moveaxis(a,0,1)
print(f"b.shape: {b.shape}")

c = np.moveaxis(a,1,-1)
print(f"c.shape: {c.shape}")
a.shape: (1, 3, 4, 5)
b.shape: (3, 1, 4, 5)
c.shape: (1, 4, 5, 3)

transpose

  • np.transpose(a), a.transpose(), a.T : 행렬의 transpose와 같은 역할을 한다. / axis 순서를 반대로 뒤바꾼다.
  • np.transpose(a, axes=()): 전체 axis를 원하는 위치로 바꾼다. (paramter가 axes에 주의한다.)
a = np.arange(12).reshape((3,4))
print(f"a.shape: {a.shape}")

b = a.transpose()
c = a.T

print(f"b.shape: {b.shape}, c.shape: {c.shape}")
a.shape: (3, 4)
b.shape: (4, 3), c.shape: (4, 3)
a = np.random.randint(1,10,(2,3,4,5))
print(f"a.shape: {a.shape}")

b = np.transpose(a,axes=(2,1,3,0))
print(f"b.shape: {b.shape}")

c = a.transpose()
print(f"c.shape: {c.shape}")
a.shape: (2, 3, 4, 5)
b.shape: (4, 3, 5, 2)
c.shape: (5, 4, 3, 2)

7.3. vstack, hstack, dstack, stack

vstack

axis=0 위치에 ndarray를 합친다.

헹을 하나 더 추가한다고 생각하면 편하다.(vertical-wise)

1차원 ndarray의 경우 shape을 (N,) -> (1,N)으로 바꾼뒤 합친다.

  • np.vstack([a,b,c,…]): axis=0 위치를 기준으로 리스트, 튜플로 묶여있는 ndarray를 합친 ndarray를 반환한다.
a = np.random.randint(1,10,(4,))
b = np.random.randint(1,10,(4,))

print(f"a: {a.shape}\n{a}\nb: {b.shape}\n{b}")
print()

c = np.vstack([a,b])
print(f"c: {c.shape}\n {c}")
a: (4,)
[8 6 8 1]
b: (4,)
[5 3 4 5]

c: (2, 4)
 [[8 6 8 1]
 [5 3 4 5]]
ndarrays = []

for i in range(10):
    a = np.random.randint(1,10,(5,))
    ndarrays.append(a)

b = np.vstack(ndarrays)
print(f"b.shape: {b.shape}")
b.shape: (10, 5)

hstack

axis=1을 기준으로 ndarray를 합친다.

열을 하나 더 추가한다고 생각하면 편하다.(horizontal-wise)

주의할 점은 합칠 때 dimension을 고려하여 합칠 수 있어야한다. ( e.g. 행렬에 column을 추가할 때)

1차원 ndarray의 경우 shape을 (N,) -> (N,1)으로 바꾼뒤 합친다.

  • np.hstack([a,b,c,…]): axis=1 위치를 기준으로 리스트, 튜플로 묶여있는 ndarray를 모두 합친 ndarray를 반환한다.
a = np.random.randint(1,10,(4,))
b = np.random.randint(1,10,(4,))
print(f"a: {a.shape}\n{a}")
print(f"b: {b.shape}\n{b}")

c = np.hstack([a,b])
print(f"c: {c.shape}\n{c}")
a: (4,)
[4 2 9 8]
b: (4,)
[3 1 5 8]
c: (8,)
[4 2 9 8 3 1 5 8]
a = np.random.randint(1,10,(3,4))
b = np.random.randint(1,10,(3,))
print(f"a: {a.shape}\n {a}")
print(f"b: {b.shape}\n {b}")

# b를 a에 그냥 hstack하면 차원이 맞지 않아 Error가 발생한다.
# b를 1차원 증가시켜서 hstack을 해야한다.
b = np.reshape(b,(-1,1))
print(f"b: {b.shape}\n {b}\n")

c = np.hstack([a,b])
print(f"c: {c.shape}\n {c}")
a: (3, 4)
 [[6 3 5 7]
 [1 7 3 2]
 [7 1 7 9]]
b: (3,)
 [3 7 5]
b: (3, 1)
 [[3]
 [7]
 [5]]

c: (3, 5)
 [[6 3 5 7 3]
 [1 7 3 2 7]
 [7 1 7 9 5]]

dstack

axis=2를 기준으로 ndarray를 합친다. (depth-wise)

합칠 때 3차원보다 작은 ndarray는 3차원으로 만들고 합친다. 1차원 :(N,) -> (1,N,1), 2차원: (M,N) -> (M,N,1)

  • np.vstack([a,b,c,…]): axis=2 위치를 기준으로 리스트, 튜플로 묶여있는 ndarray를 모두 합친 ndarray를 반환한다.
a = np.array([1,2,3])
b = np.array([4,5,6])

print(f"a: {a.shape}\n{a}")
print(f"b: {b.shape}\n{b}\n")

c = np.dstack([a,b])
print(f"c: {c.shape}\n{c}")
a: (3,)
[1 2 3]
b: (3,)
[4 5 6]
c: (1, 3, 2)
[[[1 4]
  [2 5]
  [3 6]]]
a = np.array([[1],[2],[3]])
b = np.array([[4],[5],[6]])

print(f"a: {a.shape}\n{a}")
print(f"b: {b.shape}\n{b}\n")

c = np.dstack([a,b])
print(f"c: {c.shape}\n{c}")
a: (3, 1)
[[1]
 [2]
 [3]]
b: (3, 1)
[[4]
 [5]
 [6]]

c: (3, 1, 2)
[[[1 4]]

 [[2 5]]

 [[3 6]]]

stack

새로운 axis를 만들고 그 axis를 기준으로 ndarray를 합친다.

  • np.vstack([a,b,c,…],axis=n0): axis=n0 위치에 새로운 axis를 추가한 후 그 axis를기준으로 리스트, 튜플로 묶여있는 ndarray를 모두 합친 ndarray를 반환한다.
a = np.random.randint(1,10,(4,5,6))
b = np.random.randint(1,10,(4,5,6))
c = np.random.randint(1,10,(4,5,6))

s1 = np.stack([a,b,c],axis=0)
s2 = np.stack([a,b,c],axis=1)
s3 = np.stack([a,b,c],axis=2)
s4 = np.stack([a,b,c],axis=3)

print(f"s1.shape: {s1.shape}")
print(f"s2.shape: {s2.shape}")
print(f"s3.shape: {s3.shape}")
print(f"s4.shape: {s4.shape}")
s1.shape: (3, 4, 5, 6)
s2.shape: (4, 3, 5, 6)
s3.shape: (4, 5, 3, 6)
s4.shape: (4, 5, 6, 3)

7.4 concatenate

주어진 axis를 기준으로 ndarray를 합친다. 기준이 되는 axis를 제외한 나머지 axis 값은 모두 일치해야한다.

차원이 다른 ndarray를 합치는 경우 차원을 주의해서 합쳐야한다.

  • np.concatenate([a,b,c],axis=n0) : 주어진 axis를 기준으로 리스트 또는 튜플로 묶여있는 ndarray를 모두 합친 ndarray를 새로 반환한다.
a = np.random.randint(1,10,(4,))
b = np.random.randint(1,10,(4,))
print(f"a: {a.shape}\n{a}")
print(f"b: {b.shape}\n{b}")

c = np.concatenate([a,b])
print(f"c: {c.shape}\n{c}")

d = np.concatenate([a,b],axis=0)
print(f"d: {d.shape}\n{d}")
a: (4,)
[7 2 7 2]
b: (4,)
[7 9 5 3]
c: (8,)
[7 2 7 2 7 9 5 3]
d: (8,)
[7 2 7 2 7 9 5 3]
a = np.random.randint(1,10,(3,4))
b = np.random.randint(1,10,(1,4))
c = np.random.randint(1,10,(3,1))

print(f"a: {a.shape}\n{a}")
print(f"b: {b.shape}\n{b}")
print(f"c: {c.shape}\n{c}\n")

d = np.concatenate([a,b],axis=0)
print(f"d: {d.shape}\n{d}\n")

e = np.concatenate([a,c],axis=1)
print(f"e: {e.shape}\n{e}")
a: (3, 4)
[[2 7 9 3]
 [7 8 5 1]
 [7 1 8 1]]
b: (1, 4)
[[8 4 4 3]]
c: (3, 1)
[[1]
 [6]
 [1]]

d: (4, 4)
[[2 7 9 3]
 [7 8 5 1]
 [7 1 8 1]
 [8 4 4 3]]

e: (3, 5)
[[2 7 9 3 1]
 [7 8 5 1 6]
 [7 1 8 1 1]]
a = np.random.randint(1,10,(2,3,4))

b = np.random.randint(1,10,(1,3,4))
b1 = np.concatenate([a,b],axis=0)
print(f"b1.shape: {b1.shape}")

c = np.random.randint(1,10,(2,1,4))
c1 = np.concatenate([a,c],axis=1)
print(f"c1.shape: {c1.shape}")

d = np.random.randint(1,10,(2,3,1))
d1 = np.concatenate([a,d],axis=2)
print(f"d1.shape: {d1.shape}")
b1.shape: (3, 3, 4)
c1.shape: (2, 4, 4)
d1.shape: (2, 3, 5)

Leave a comment