06wk-2: numpy (1)

Author

최규빈

Published

April 12, 2024

1. 강의영상

2. Imports

import numpy as np

3. numpy

A. 선언

a=np.array([1,2,3]) # list를 만들고 ndarray화 시킴 
l=[1,2,3]

B. 기본연산 브로드캐스팅

a+1 ## [1,2,3] + 1 = [2,3,4]
array([2, 3, 4])
l+1
TypeError: can only concatenate list (not "int") to list
a*2
array([2, 4, 6])
l*2
[1, 2, 3, 1, 2, 3]
a/2
array([0.5, 1. , 1.5])
l/2
TypeError: unsupported operand type(s) for /: 'list' and 'int'
a**2
array([1, 4, 9])
l**2
TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'
a%2 # %2 = 2로 나눈 나머지를 리턴 a=[1,2,3] 
array([1, 0, 1])
l%2
TypeError: unsupported operand type(s) for %: 'list' and 'int'

C. 기타수학연산지원

np.sqrt(a), np.sqrt(l)
(array([1.        , 1.41421356, 1.73205081]),
 array([1.        , 1.41421356, 1.73205081]))
np.log(a), np.log(l)
(array([0.        , 0.69314718, 1.09861229]),
 array([0.        , 0.69314718, 1.09861229]))
np.exp(a), np.exp(l)
(array([ 2.71828183,  7.3890561 , 20.08553692]),
 array([ 2.71828183,  7.3890561 , 20.08553692]))
np.sin(a), np.sin(l)
(array([0.84147098, 0.90929743, 0.14112001]),
 array([0.84147098, 0.90929743, 0.14112001]))

D. 인덱싱 1차원

- 선언

l=[11,22,33,44,55,66] 
a=np.array(l) 

- 인덱스로 접근

l[0],l[1],l[2],l[3],l[-2],l[-1]
(11, 22, 33, 44, 55, 66)
a[0],a[1],a[2],a[3],a[-2],a[-1]
(11, 22, 33, 44, 55, 66)

- : 이용 (슬라이싱)

l[2:4] # index 2에서 시작, index 4는 포함하지 않음 
[33, 44]
a[2:4] 
array([33, 44])

- 정수배열에 의한 인덱싱

a
array([11, 22, 33, 44, 55, 66])
a[[0,2,4]] # index=0, index=2, index=4 에 해당하는 원소를 뽑고 싶다 
array([11, 33, 55])
l[[0,2,4]] # 리스트는 불가능 
TypeError: list indices must be integers or slices, not list

- 부울값에 의한 인덱싱

a
array([11, 22, 33, 44, 55, 66])
a[[True,False,True,False,True,False]] 
array([11, 33, 55])

응용하면?

a < 33 
array([ True,  True, False, False, False, False])
a[a<33]
array([11, 22])

리스트는 불가능

l<33 # 여기에서부터 불가능 
TypeError: '<' not supported between instances of 'list' and 'int'
l[[True,False,True,False,True,False]] # 이것도 불가능 
TypeError: list indices must be integers or slices, not list

E. 인덱싱 2차원

- 중첩리스트와 2차원 np.array 선언

A = [[1,2,3,4],[-1,-2,-3,-4],[5,6,7,8],[-5,-6,-7,-8]]
A2 = np.array(A)
A2
array([[ 1,  2,  3,  4],
       [-1, -2, -3, -4],
       [ 5,  6,  7,  8],
       [-5, -6, -7, -8]])
A
[[1, 2, 3, 4], [-1, -2, -3, -4], [5, 6, 7, 8], [-5, -6, -7, -8]]

- A의 원소 인덱싱

A[0][0] # (1,1)의 원소 
1
A[1][2] # (2,3)의 원소 
-3
A[-1][0] # (4,1)의 원소 
-5

- A2의 원소 인덱싱

A2[0][0] # (1,1)의 원소 
1
A2[1][2] # (2,3)의 원소 
-3
A2[-1][0] # (4,1)의 원소 
-5

- A2에서만 되는 기술 (넘파이에서 제시하는 신기술, R에서는 기본적으로 쓰던것, 이중list는 불가능)

A2[0,0] # (1,1)의 원소 
1
A2[1,2] # (2,3)의 원소 
-3
A2[-1,0] # (4,1)의 원소 
-5

- 정수배열에 의한 인덱싱 & 슬라이싱!

A2
array([[ 1,  2,  3,  4],
       [-1, -2, -3, -4],
       [ 5,  6,  7,  8],
       [-5, -6, -7, -8]])
A2[0,0:2] # 1행1열, 1행2열 
array([1, 2])
A2[0,:] # 1행 
array([1, 2, 3, 4])
A2[0] # 1행
array([1, 2, 3, 4])
A2[[0,2],:] # 1행, 3행 
array([[1, 2, 3, 4],
       [5, 6, 7, 8]])
A2[[0,2]] # 1행, 3행 
array([[1, 2, 3, 4],
       [5, 6, 7, 8]])
A2[:,0] # 1열 
array([ 1, -1,  5, -5])
A2[:,[0]] # 1열 
array([[ 1],
       [-1],
       [ 5],
       [-5]])
A2[:,[0,2]] # 1열, 3열
array([[ 1,  3],
       [-1, -3],
       [ 5,  7],
       [-5, -7]])
A2[0:2,[0,2]] # 1행~2행 //  1열,3열 
array([[ 1,  3],
       [-1, -3]])

F. 1차원 배열의 선언

- 리스트나 튜플을 선언하고 형변환

np.array((1,2,3)) # 튜플->넘파이어레이 
array([1, 2, 3])
np.array([1,2,3]) # 리스트 ->넘파이어레이 
array([1, 2, 3])

- range()를 이용해서 선언하고 형변환

np.array(range(10)) # range(10) -> 넘파이어레이 
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

- np.zeros, np.ones

np.zeros(3) 
array([0., 0., 0.])
np.ones(4)
array([1., 1., 1., 1.])

- np.linspace

np.linspace(0,1,12) # 0에서 시작하고 1에서 끝남 (양끝점 모두 포함)
array([0.        , 0.09090909, 0.18181818, 0.27272727, 0.36363636,
       0.45454545, 0.54545455, 0.63636364, 0.72727273, 0.81818182,
       0.90909091, 1.        ])
len(np.linspace(0,1,12)) # 길이는 12
12

- np.arange

np.arange(5) # np.array(range(5))
array([0, 1, 2, 3, 4])
np.arange(1,6) # np.array(range(1,6))
array([1, 2, 3, 4, 5])

G. reshape

- reshape: ndarray의 특수한 기능

a=np.array([11,22,33,44,55,66])
a ## 길이가 6인 벡터 
array([11, 22, 33, 44, 55, 66])
a.reshape(2,3) ## (2,3) matrix 라고 생각해도 무방 
array([[11, 22, 33],
       [44, 55, 66]])

note: reshape은 a자체를 변화시키는것은 아님

a # a는 그대로 있음 
array([11, 22, 33, 44, 55, 66])
b= a.reshape(2,3) # a를 reshape한 결과를 b에 저장 
b
array([[11, 22, 33],
       [44, 55, 66]])
a # a는 여전히 그대로 있음
array([11, 22, 33, 44, 55, 66])

- 다시 b를 a처럼 바꾸고 싶다

b
array([[11, 22, 33],
       [44, 55, 66]])
b.reshape(6) # b는 (2,3) matrix , 그런데 이것을 길이가 6인 벡터로 만들고 싶다. 
array([11, 22, 33, 44, 55, 66])

- reshape with -1

a=np.arange(24) # np.array(range(24))
a
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])
a.reshape(2,-1)
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]])
a.reshape(3,-1)
array([[ 0,  1,  2,  3,  4,  5,  6,  7],
       [ 8,  9, 10, 11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20, 21, 22, 23]])
a.reshape(4,-1)
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])
a.reshape(5,-1)
ValueError: cannot reshape array of size 24 into shape (5,newaxis)
a.reshape(6,-1)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])
a.reshape(7,-1)
ValueError: cannot reshape array of size 24 into shape (7,newaxis)
a.reshape(8,-1)
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11],
       [12, 13, 14],
       [15, 16, 17],
       [18, 19, 20],
       [21, 22, 23]])
a.reshape(12,-1)
array([[ 0,  1],
       [ 2,  3],
       [ 4,  5],
       [ 6,  7],
       [ 8,  9],
       [10, 11],
       [12, 13],
       [14, 15],
       [16, 17],
       [18, 19],
       [20, 21],
       [22, 23]])
b= a.reshape(12,-1)
b
array([[ 0,  1],
       [ 2,  3],
       [ 4,  5],
       [ 6,  7],
       [ 8,  9],
       [10, 11],
       [12, 13],
       [14, 15],
       [16, 17],
       [18, 19],
       [20, 21],
       [22, 23]])
b.reshape(-1) # b를 다시 길이가 24인 벡터로!
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])

H. 2차원 배열의 선언

np.zeros((3,3))
array([[0., 0., 0.],
       [0., 0., 0.],
       [0., 0., 0.]])
np.ones((3,3))
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])
np.eye(3)
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])
np.diag([1,2,3,-1])
array([[ 1,  0,  0,  0],
       [ 0,  2,  0,  0],
       [ 0,  0,  3,  0],
       [ 0,  0,  0, -1]])

I. 랜덤으로 배열 생성

np.random.randn(10) # 표쥰정규분포에서 10개를 뽑음 
array([-0.33138513,  1.42650878,  1.22837316,  0.08113144, -1.0972836 ,
        1.66641222,  0.18244459,  0.45246865, -1.22156378,  0.18756565])
np.random.rand(10) # 0~1사이에서 10개를 뽑음
array([0.67642264, 0.06040223, 0.48991224, 0.32202819, 0.22195857,
       0.31407011, 0.82567895, 0.14494499, 0.74293485, 0.37775136])
np.random.randn(4).reshape(2,2) # 표준정규분포에서 4개를 뽑고 (2,2) ndarray로 형태변환 
array([[-1.1514733 , -0.82454175],
       [-0.07976586,  0.0863252 ]])
np.random.rand(4).reshape(2,2) # 0~1 4개를 뽑고 (2,2) ndarray로 형태변환 
array([[0.06084905, 0.54804885],
       [0.64428734, 0.76590051]])

J. 행렬관련기능

A=np.arange(4).reshape(2,2) 
A
array([[0, 1],
       [2, 3]])
A.T # .T는 전치행렬을 구해줌 
array([[0, 2],
       [1, 3]])
np.linalg.inv(A) # np.linalg.inv는 역행렬을 구해주는 함수 
array([[-1.5,  0.5],
       [ 1. ,  0. ]])
A @ np.linalg.inv(A) # @는 행렬곱을 수행 
array([[1., 0.],
       [0., 1.]])

K. 2차원 배열과 연립 1차 방정식

- 아래의 연립방정식 고려

\(\begin{cases} y+z+w = 3 \\ x+z+w = 3 \\ x+y+w = 3 \\ x+y+z = 3 \end{cases}\)

- 행렬표현?

\(\begin{bmatrix} 0 & 1 & 1 & 1 \\ 1 & 0 & 1 & 1 \\ 1 & 1 & 0 & 1 \\ 1 & 1 & 1 & 0 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ w \end{bmatrix} = \begin{bmatrix} 3 \\ 3 \\ 3 \\ 3 \end{bmatrix}\)

- 풀이

A = np.array([[0,1,1,1],[1,0,1,1],[1,1,0,1],[1,1,1,0]])
A
array([[0, 1, 1, 1],
       [1, 0, 1, 1],
       [1, 1, 0, 1],
       [1, 1, 1, 0]])
b= np.array([3,3,3,3]).reshape(4,1)
b
array([[3],
       [3],
       [3],
       [3]])
np.linalg.inv(A) @ b 
array([[1.],
       [1.],
       [1.],
       [1.]])

- 다른풀이

b를 아래와 같이 만들어도 된다.

b=np.array([3,3,3,3])
b
array([3, 3, 3, 3])
b.shape # b.shape은 길이가 1인 튜플로 나온다. 
(4,)
np.linalg.inv(A) @ b 
array([1., 1., 1., 1.])

@의 유연성

- 엄밀하게는 아래의 행렬곱이 가능하다. - (2,2) @ (2,1) => (2,1) - (1,2) @ (2,2) => (1,2)

A = np.array([1,2,3,4]).reshape(2,2) 
b = np.array([1,2]).reshape(2,1) 
A@b
array([[ 5],
       [11]])
A.shape, b.shape, (A@b).shape
((2, 2), (2, 1), (2, 1))
A = np.array([1,2,3,4]).reshape(2,2) 
b = np.array([1,2]).reshape(1,2) 
b@A 
array([[ 7, 10]])
A.shape, b.shape, (b@A).shape
((2, 2), (1, 2), (1, 2))

- 당연히 아래는 성립안한다.

A = np.array([1,2,3,4]).reshape(2,2) 
b = np.array([1,2]).reshape(2,1) 
b@A
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 1)
A = np.array([1,2,3,4]).reshape(2,2) 
b = np.array([1,2]).reshape(1,2) 
A@b
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 1 is different from 2)

- 아래는 어떨까? 계산가능할까? \(\to\) 모두 계산가능! - (2,) @ (2,2) = (2,) - (2,2) @ (2,) = (2,)

A = np.array([1,2,3,4]).reshape(2,2)
b = np.array([1,2]) 
A@b
array([ 5, 11])
A.shape, b.shape, (A@b).shape 
((2, 2), (2,), (2,))
  • b를 마치 (2,1)처럼 해석하여 행렬곱하고 결과는 다시 (2,) 로 만든것 같다.
b@A
array([ 7, 10])
A.shape, b.shape, (b@A).shape 
((2, 2), (2,), (2,))
  • 이때는 \(b\)를 마치 (1,2)처럼 해석하여 행렬곱하고 결과는 다시 (2,)로 만든것 같다.

- 아래는 어떠할까?

b1 = np.array([1,2,3,4]) 
b2 = np.array([1,2,3,4]) 
b1@b2 
30
b1.shape, b2.shape, (b1@b2).shape 
((4,), (4,), ())
  • (1,4) @ (4,1) = (1,1) 로 생각

- 즉 위는 아래와 같이 해석하고 행렬곱한것과 결과가 같다.

b1 = np.array([1,2,3,4]).reshape(1,4) 
b2 = np.array([1,2,3,4]).reshape(4,1) 
b1@b2 
array([[30]])
b1.shape, b2.shape, (b1@b2).shape 
((1, 4), (4, 1), (1, 1))

- 때로는 (4,1) @ (1,4)와 같은 계산결과를 얻고 싶을 수 있는데 이때는 차원을 명시해야함

b1 = np.array([1,2,3,4]).reshape(4,1) 
b2 = np.array([1,2,3,4]).reshape(1,4) 
b1@b2 
array([[ 1,  2,  3,  4],
       [ 2,  4,  6,  8],
       [ 3,  6,  9, 12],
       [ 4,  8, 12, 16]])

L. 차원

- 넘파이배열의 차원은 .shape 으로 확인가능

- 아래는 모두 미묘하게 다르다.

a=np.array(3.14) # 스칼라, 0d array 
a, a.shape
(array(3.14), ())
a=np.array([3.14]) # 벡터, 1d array 
a, a.shape
(array([3.14]), (1,))
a=np.array([[3.14]]) # 매트릭스, 2d array 
a, a.shape
(array([[3.14]]), (1, 1))
a=np.array([[[3.14]]]) # 텐서, 3d array 
a, a.shape
(array([[[3.14]]]), (1, 1, 1))

4. numpy와 축(axis)

축(axis)은 차원과 비슷하게 생각하면 됨

A. np.concatenate

- 기본예제

a = np.array([1,2])
b = -a
np.concatenate([a,b])
array([ 1,  2, -1, -2])
  • 딱히 인상적인건 아님.
  • 왜냐하면 리스트에서 있는 기능임

- 2D인 경우

a = np.array([1,2,3,4]).reshape(2,2)
b = -a
np.concatenate([a,b])
array([[ 1,  2],
       [ 3,  4],
       [-1, -2],
       [-3, -4]])

a,b를 위아래가 아니라 좌우로 붙이고 싶다면?

np.concatenate([a,b],axis=1)
array([[ 1,  2, -1, -2],
       [ 3,  4, -3, -4]])

- 도데체 axis=0, 혹은 axis=1의 의미가 무엇인가?

np.concatenate([a,b],axis=0) # 이건 아까 np.concatenate([a,b])랑 같네?
array([[ 1,  2],
       [ 3,  4],
       [-1, -2],
       [-3, -4]])
np.concatenate([a,b],axis=1) # 이건 아까 np.concatenate([a,b])랑 같네?
array([[ 1,  2, -1, -2],
       [ 3,  4, -3, -4]])
  • 관찰에 의한 정리: a,b가 2차원일때 axis=0 이라고 쓰면 위아래로, axis=1이라고 하면 좌우로 합쳐진다.
  • axis=0은 생략할 수 있다.

- 2D일 경우에 활용

a = np.array([1,2,3,4]).reshape(2,2)
b = np.array([10,20]).reshape(2,1)
c = -b
a,b,c
(array([[1, 2],
        [3, 4]]),
 array([[10],
        [20]]),
 array([[-10],
        [-20]]))
np.concatenate([a,b.T],axis=0) 
array([[ 1,  2],
       [ 3,  4],
       [10, 20]])
np.concatenate([a,b.T,c.T],axis=0) 
array([[  1,   2],
       [  3,   4],
       [ 10,  20],
       [-10, -20]])
np.concatenate([a,b],axis=1) 
array([[ 1,  2, 10],
       [ 3,  4, 20]])
np.concatenate([a,b,c],axis=1) 
array([[  1,   2,  10, -10],
       [  3,   4,  20, -20]])
np.concatenate([a.reshape(4,1), np.concatenate([b,c])],axis=1)
array([[  1,  10],
       [  2,  20],
       [  3, -10],
       [  4, -20]])

- axis의 의미가 뭔지 궁금함. 좀 더 예제를 살펴보자.

a = np.arange(2*3*4).reshape(2,3,4)
b = -a
a.shape, b.shape
((2, 3, 4), (2, 3, 4))
np.concatenate([a,b],axis=0)
array([[[  0,   1,   2,   3],
        [  4,   5,   6,   7],
        [  8,   9,  10,  11]],

       [[ 12,  13,  14,  15],
        [ 16,  17,  18,  19],
        [ 20,  21,  22,  23]],

       [[  0,  -1,  -2,  -3],
        [ -4,  -5,  -6,  -7],
        [ -8,  -9, -10, -11]],

       [[-12, -13, -14, -15],
        [-16, -17, -18, -19],
        [-20, -21, -22, -23]]])
np.concatenate([a,b],axis=1)
array([[[  0,   1,   2,   3],
        [  4,   5,   6,   7],
        [  8,   9,  10,  11],
        [  0,  -1,  -2,  -3],
        [ -4,  -5,  -6,  -7],
        [ -8,  -9, -10, -11]],

       [[ 12,  13,  14,  15],
        [ 16,  17,  18,  19],
        [ 20,  21,  22,  23],
        [-12, -13, -14, -15],
        [-16, -17, -18, -19],
        [-20, -21, -22, -23]]])
np.concatenate([a,b],axis=2)
array([[[  0,   1,   2,   3,   0,  -1,  -2,  -3],
        [  4,   5,   6,   7,  -4,  -5,  -6,  -7],
        [  8,   9,  10,  11,  -8,  -9, -10, -11]],

       [[ 12,  13,  14,  15, -12, -13, -14, -15],
        [ 16,  17,  18,  19, -16, -17, -18, -19],
        [ 20,  21,  22,  23, -20, -21, -22, -23]]])
  • 이번에는 axis=2 까지 계산이 가능함
np.concatenate([a,b],axis=3)
AxisError: axis 3 is out of bounds for array of dimension 3
  • axis=3까지는 불가능

- 뭔가 나름의 방식으로 합쳐지는것 같은데, 원리를 잘 모르겠음

(분석) np.concatenate([a,b],axis=??)에서 ?? 의 숫자를 바꿔가면서 결과의 차원만 관찰해보자.

print(f'입력: {a.shape} concat {b.shape}')
print(f'출력: {np.concatenate([a,b],axis=0).shape} -- axis=0')
입력: (2, 3, 4) concat (2, 3, 4)
출력: (4, 3, 4) -- axis=0
print(f'입력: {a.shape} concat {b.shape}')
print(f'출력: {np.concatenate([a,b],axis=1).shape} -- axis=1')
입력: (2, 3, 4) concat (2, 3, 4)
출력: (2, 6, 4) -- axis=1
print(f'입력: {a.shape} concat {b.shape}')
print(f'출력: {np.concatenate([a,b],axis=2).shape} -- axis=2')
입력: (2, 3, 4) concat (2, 3, 4)
출력: (2, 3, 8) -- axis=2

- 2D의 경우도 재해석

a = np.array([1,2,3,4,5,6]).reshape(3,2)
b = -a                         

좌우로 합치고 싶다면?? (3,2) concat (3,2) = (3,4) 가 되어야함 –> 그러면 차원의 두번째 숫자가 바뀌어야함.

np.concatenate([a,b],axis=1)
array([[ 1,  2, -1, -2],
       [ 3,  4, -3, -4],
       [ 5,  6, -5, -6]])

위아래로 합치고 싶다면? (3,2) concat (3,2) = (6,2) 가 되어야함 –> 그러면 차원의 첫번째 숫자가 바뀌어야함.

np.concatenate([a,b],axis=0)
array([[ 1,  2],
       [ 3,  4],
       [ 5,  6],
       [-1, -2],
       [-3, -4],
       [-5, -6]])

- axis=-1로 넣는 경우도 있음..

a = np.arange(2*3*4).reshape(2,3,4)
b = -a
a.shape, b.shape
((2, 3, 4), (2, 3, 4))

(2,3,4) concat (2,3,4) = (2,3,8) 을 만들고 싶다면?? –> 세번째(axis=2)축이 바뀌어야함.

np.concatenate([a,b],axis=2).shape
(2, 3, 8)

(2,3,4) concat (2,3,4) = (2,3,8) 을 만들고 싶다면?? –> 마지막(axis=-1)축이 바껴야함.

np.concatenate([a,b],axis=-1).shape
(2, 3, 8)

(2,3,4) concat (2,3,4) = (2,6,4) 을 만들고 싶다면?? –> 마지막에서 두번째 (axis=-2)축이 바껴야함.

np.concatenate([a,b],axis=-2).shape
(2, 6, 4)

(2,3,4) concat (2,3,4) = (4,3,4) 을 만들고 싶다면?? –> 마지막에서 세번째 (axis=-3)축이 바껴야함.

np.concatenate([a,b],axis=-3).shape
(4, 3, 4)

- 2D의 경우에도 axis=-1, axis=-2를 적용가능

a = np.array([1,2,3,4]).reshape(2,2)
b = np.array([10,20]).reshape(2,1)
a,b
(array([[1, 2],
        [3, 4]]),
 array([[10],
        [20]]))
np.concatenate([a,b],axis=-1)
array([[ 1,  2, 10],
       [ 3,  4, 20]])
np.concatenate([b.T,a],axis=-2)
array([[10, 20],
       [ 1,  2],
       [ 3,  4]])

B. np.stack

- 혹시 아래가 가능할까?

  • (3,) concat (3,) = (3,2)
a = np.array([1,2,3])
b = -a 
a,b
(array([1, 2, 3]), array([-1, -2, -3]))
np.concatenate([a,b],axis=1)
AxisError: axis 1 is out of bounds for array of dimension 1
  • 불가능

- 아래와 같은 방식은 가능

np.concatenate([a.reshape(3,1), b.reshape(3,1)],axis=1)
array([[ 1, -1],
       [ 2, -2],
       [ 3, -3]])

- 위의 과정을 줄여서 아래와 같이 할 수 있음.

np.stack([a,b],axis=1)
array([[ 1, -1],
       [ 2, -2],
       [ 3, -3]])

- 아래와 같은 결합도 가능

np.stack([a,b],axis=0)
array([[ 1,  2,  3],
       [-1, -2, -3]])

- stack에서 axis의 역할에 대한 분석

a = np.arange(5*3*4).reshape(5,3,4)
b = -a
print(f'입력: {a.shape} stack {b.shape}')
print(f'출력: {np.stack([a,b],axis=0).shape} -- axis=0')
print(f'출력: {np.stack([a,b],axis=1).shape} -- axis=1')
print(f'출력: {np.stack([a,b],axis=2).shape} -- axis=2')
print(f'출력: {np.stack([a,b],axis=3).shape} -- axis=3')
#print(f'출력: {np.stack([a,b],axis=4).shape} -- axis=4')
입력: (5, 3, 4) stack (5, 3, 4)
출력: (2, 5, 3, 4) -- axis=0
출력: (5, 2, 3, 4) -- axis=1
출력: (5, 3, 2, 4) -- axis=2
출력: (5, 3, 4, 2) -- axis=3

- 다시 (3,) stack (3,) 상황을 이해하여보면

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

a,b 모두 1차원이지만 이를 위아래로 붙여서 2차원으로 만들고 싶어. 즉 (3,) stack (3,) = (2,3) 을 만들고 싶음. -> 첫번째 위치에(axis=0)에 축을 추가해야겠음.

np.stack([a,b],axis=0)
array([[ 1,  2,  3],
       [-1, -2, -3]])

a,b 모두 1차원이지만 이를 좌우로 붙여서 2차원으로 만들고 싶어. 즉 (3,) stack (3,) = (3,2) 을 만들고 싶음. -> 두번째 위치에(axis=1)에 축을 추가해야겠음.

np.stack([a,b],axis=1)
array([[ 1, -1],
       [ 2, -2],
       [ 3, -3]])

note: np.concatenate은 축의 총 갯수를 유지하면서 결합, np.stack은 축의 갯수를 하나 증가시키면서 결합

C. sum

- 1차원

a = np.array([1,2,3])
a
array([1, 2, 3])
np.sum(a)
6
a.sum() 
6
a.sum(axis=0)
6

- 2차원

a = np.array([1,2,3,4,5,6]).reshape(3,2)
a
array([[1, 2],
       [3, 4],
       [5, 6]])
a.sum() # 전체합: 1+2+3+4+5+6
21
a.sum(axis=0) # 1열의합, 2열의합
array([ 9, 12])
a.sum(axis=1) # 1행의합, 2행의합, 3행의합
array([ 3,  7, 11])

a를 2차원 array모양으로 만들고 axis을 잘 써주면 row-wise로 합을 구하거나 column-wise로 합을 구하기 좋음.

- 넘파이 특수기능 .sum()에서 axis의 의미를 알아보자.

a.shape, a.sum(axis=0).shape, a.sum(axis=1).shape
((3, 2), (2,), (3,))

- 연습

a = np.arange(10).reshape(5,2)
a
array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7],
       [8, 9]])

행별로 합을 구하고 싶음 -> (5,2)의 차원이 (5,) 와 같이 되어야함. -> 두번째축이 사라져야함

a.sum(axis=1)
array([ 1,  5,  9, 13, 17])

열별로 합을 구하고 싶음 -> (5,2)의 차원이 (2,) 와 같이 되어야함. -> 첫번째축이 사라져야함

a.sum(axis=0)
array([20, 25])

a의 모든 원소의 합을 구하고 싶다면? -> (5,2)차원이 ()와 같이 되어야함 -> 첫번째축과 두번째축이 다 사라져야함

a.sum(axis=(0,1))
45
a.sum() # 아 이것은 사실 a.sum(axis=(0,1)) 의 생략된 표현이었군!
45

D. mean, std, max, min, prod

- 모두 sum과 비슷한 논리로 mean, std, max, min, prod 을 구할 수 있음

a = np.array([1,2,3,4,5,6,7,8]).reshape(4,2)
a
array([[1, 2],
       [3, 4],
       [5, 6],
       [7, 8]])
a.mean(axis=0), a.std(axis=0), a.max(axis=0), a.min(axis=0), a.prod(axis=0)
(array([4., 5.]),
 array([2.23606798, 2.23606798]),
 array([7, 8]),
 array([1, 2]),
 array([105, 384]))
a.mean(axis=1), a.std(axis=1), a.max(axis=1), a.min(axis=1), a.prod(axis=1)
(array([1.5, 3.5, 5.5, 7.5]),
 array([0.5, 0.5, 0.5, 0.5]),
 array([2, 4, 6, 8]),
 array([1, 3, 5, 7]),
 array([ 2, 12, 30, 56]))

E. argmax, argmin

- 1차원

a = np.array([22,-2,3,10,4])
a
array([22, -2,  3, 10,  4])
a.argmax(),a.argmin()
(0, 1)

- 2차원

np.random.seed(43052)
a = np.random.rand(10).reshape(5,2)
a
array([[0.81768226, 0.04953212],
       [0.83868626, 0.61977707],
       [0.12254052, 0.11712779],
       [0.8795562 , 0.97941543],
       [0.90986893, 0.96667407]])
a.argmax(axis=0),a.argmin(axis=0)
(array([4, 3]), array([2, 0]))
a.argmax(axis=1),a.argmin(axis=1)
(array([0, 0, 0, 1, 1]), array([1, 1, 1, 0, 0]))

F. cumsum, cumprod

- 1차원

a = np.array([1,2,3,4])
a
array([1, 2, 3, 4])
np.cumsum(a), a.cumsum()
(array([ 1,  3,  6, 10]), array([ 1,  3,  6, 10]))
np.cumprod(a), a.cumprod()
(array([ 1,  2,  6, 24]), array([ 1,  2,  6, 24]))

- 2차원

a = np.array([1,2,3,4,5,6,7,8,9,10,11,12]).reshape(4,3)
a
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])
a.cumsum(axis=0),a.cumsum(axis=1),a.cumprod(axis=0),a.cumprod(axis=1)
(array([[ 1,  2,  3],
        [ 5,  7,  9],
        [12, 15, 18],
        [22, 26, 30]]),
 array([[ 1,  3,  6],
        [ 4,  9, 15],
        [ 7, 15, 24],
        [10, 21, 33]]),
 array([[   1,    2,    3],
        [   4,   10,   18],
        [  28,   80,  162],
        [ 280,  880, 1944]]),
 array([[   1,    2,    6],
        [   4,   20,  120],
        [   7,   56,  504],
        [  10,  110, 1320]]))

G. diff

- 1차원 차분

a = np.array([1,2,4,7,15])
a
array([ 1,  2,  4,  7, 15])
np.diff(a)
array([1, 2, 3, 8])
np.diff(a,prepend=100)
array([-99,   1,   2,   3,   8])
np.diff(a,prepend=a[0])
array([0, 1, 2, 3, 8])
np.diff(a,append=100)
array([ 1,  2,  3,  8, 85])
np.diff(a,append=a[-1])
array([1, 2, 3, 8, 0])

- 2차원 array의 미분

a = np.arange(24).reshape(4,6)
a
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])
np.diff(a,axis=0) # 열별로 연산이 적용
array([[6, 6, 6, 6, 6, 6],
       [6, 6, 6, 6, 6, 6],
       [6, 6, 6, 6, 6, 6]])
np.diff(a,axis=1) # 행별로 연산이 적용
array([[1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1]])