[NumPy] 배열과 행렬의 연산

kkiyou·2021년 6월 11일
0

Data Science

목록 보기
6/11
  • Vector는 힘의 크기(magnitude)와 방향(direction)을 갖는 물리량을 의미한다. 예컨대 속도, 가속도, 힘, 응력 등이 해당한다.

  • Scalar는 힘의 크기만 가지는 물리량(only magnitude, but not direction)을 의미한다. 예컨대 질량, 시간, 면적 등이 해당한다.


    참고자료 1 참고자료 2


1. Array 연산

numpy 연산은 같은 위치의 원소끼리(element-wise) 연산하므로, 축의 shape가 동일해야 한다.

>>> arr = np.arange(24).reshape(3, 2, 4)
>>> arr
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]]])

>>> arr_2 = (np.arange(24) * 100).reshape(3, 2, 4)
>>> arr_2
array([[[   0,  100,  200,  300],
        [ 400,  500,  600,  700]],

       [[ 800,  900, 1000, 1100],
        [1200, 1300, 1400, 1500]],

       [[1600, 1700, 1800, 1900],
        [2000, 2100, 2200, 2300]]])

>>> arr + arr_2
array([[[   0,  101,  202,  303],
        [ 404,  505,  606,  707]],

       [[ 808,  909, 1010, 1111],
        [1212, 1313, 1414, 1515]],

       [[1616, 1717, 1818, 1919],
        [2020, 2121, 2222, 2323]]])

# x축의 shape가 동일할 때.
>>> arr_3 = np.array([100, 200, 300, 400])
>>> arr_3
array([100, 200, 300, 400])
>>> arr + arr_3
array([[[100, 201, 302, 403],
        [104, 205, 306, 407]],

       [[108, 209, 310, 411],
        [112, 213, 314, 415]],

       [[116, 217, 318, 419],
        [120, 221, 322, 423]]])


# y축의 shape가 동일할 때.
>>> arr_4 = np.array([-1000, -2000]).reshape(2, 1)
>>> arr_4
array([[-1000],
       [-2000]])
>>> arr - arr_4
array([[[1000, 1001, 1002, 1003],
        [2004, 2005, 2006, 2007]],

       [[1008, 1009, 1010, 1011],
        [2012, 2013, 2014, 2015]],

       [[1016, 1017, 1018, 1019],
        [2020, 2021, 2022, 2023]]])
       

# z축의 shape가 동일할 때.
>>> arr_5 = np.array([10, 20, 30]).reshape(3, 1, 1)
>>> arr_5
array([[[10]],

       [[20]],

       [[30]]])
>>> arr * arr_5 
array([[[  0,  10,  20,  30],
        [ 40,  50,  60,  70]],

       [[160, 180, 200, 220],
        [240, 260, 280, 300]],

       [[480, 510, 540, 570],
        [600, 630, 660, 690]]])

list의 연산과는 차이가 존재한다.

# list 연산
>>> a = [0, 1, 2, 3]
>>> b = [100, 110, 220, 330]
>>> a + b
[0, 1, 2, 3, 100, 110, 220, 330]

>>> a * b
TypeError: can't multiply sequence by non-int of type 'list'

>>> a - b
TypeError: unsupported operand type(s) for -: 'list' and 'list'

>>> a / b
TypeError: unsupported operand type(s) for /: 'list' and 'list'

# property를 활용한 vectorize 
@np.vectorize
def plus(x, y):
    return (x + y)
>>> plus(a, b)
array([100, 111, 222, 333])



2. Matrix 연산

Matrix(행렬)의 연산은 array의 연산과 다른 점이 있어 주의가 필요하다. 수학 시간에 행렬의 연산을 배운 기억이 있을 것이다. 덧셈과 뺄셈 그리고 나눗셈은 array 연산처럼 같은 위치의 원소끼리 연산한다.

그러나, 행렬의 곱셈과 그 외의 행렬 연산은 배열의 연산과 다르다. 행렬 사이의 곱셈 연산은 아래와 같다. 따라서 곱셈의 순서가 중요하다.(a×bb×aa \times b \ne b \times a)

[a0,0a0,1a0,2a1,0a1,1a1,2][b0,0b0,1b1,0b1,1b2,0b2,1]=\left[ \begin{matrix} a_{0, 0} & a_{0, 1} & a_{0, 2}\\ a_{1, 0} & a_{1, 1} & a_{1, 2} \\ \end{matrix} \right] \left[ \begin{matrix} b_{0, 0} & b_{0, 1}\\ b_{1, 0} & b_{1, 1} \\ b_{2, 0} & b_{2, 1} \\ \end{matrix} \right] =
[a0,0b0,0+a0,1b1,0+a0,2b2,0a0,0b0,1+a0,1b1,1+a0,2b2,1a1,0b0,0+a1,1b1,0+a1,2b2,0a1,0b0,1+a1,1b1,1+a1,2b2,1]\left[ \begin{matrix} a_{0, 0} b_{0, 0} + a_{0, 1}b_{1, 0} + a_{0, 2}b_{2, 0}& a_{0, 0} b_{0, 1} + a_{0, 1}b_{1, 1} + a_{0, 2}b_{2, 1}\\ a_{1, 0} b_{0, 0} + a_{1, 1}b_{1, 0} + a_{1, 2}b_{2, 0}& a_{1, 0} b_{0, 1} + a_{1, 1}b_{1, 1} + a_{1, 2}b_{2, 1}\\ \end{matrix} \right]
matrix 곱셈
>>> mat = np.matrix(np.arange(6).reshape(2, 3))
>>> mat
matrix([[0, 1, 2],
        [3, 4, 5]])
>>> print(type(mat))
<class 'numpy.matrix'>

# matrix의 덧셈은 shape가 완전히 동일해야 함.
>>> mat2 = np.matrix([[10, 20],
                  [100, 200]])
>>> mat + mat2
ValueError: operands could not be broadcast together with shapes (2,3) (2,2) 

# matrix의 곱셈은 [a, b] * [x, y]일 때 b와 x가 동일해야 한다.
# (2,3) * (2,2)
>>> mat * mat2
ValueError: shapes (2,3) and (2,2) not aligned: 3 (dim 1) != 2 (dim 0)

# matrix 곱셈은 순서가 중요하다.
# (2,2) * (2,3)
>>> mat2 * mat
matrix([[  60,   90,  120],
        [ 600,  900, 1200]])

>>> mat3 = np.matrix([[1, 2],
                  [10, 20],
                  [100, 200]])

# (2,3) * (3,2)
>>> mat * mat3
matrix([[ 210,  420],
        [ 543, 1086]])

# (3,2) * (2,3)
>>> mat3 * mat
matrix([[   6,    9,   12],
        [  60,   90,  120],
        [ 600,  900, 1200]])

참고자료 1


2.1. @

numpy는 @를 사용해 array 또한 행렬 곱을 할 수 있다.

>>> arr = np.arange(6).reshape(2, 3)
>>> arr2 = np.array([[1, 2],
                     [10, 20],
                     [100, 200]])

>>> arr * arr2
ValueError: operands could not be broadcast together with shapes (2,3) (3,2) 

>>> arr @ arr2
array([[ 210,  420],
       [ 543, 1086]])

2.2. np.dot()

a와 b array를 행렬 곱셈 한다.

>>> arr = np.arange(4).reshape(2, 2)
>>> arr
array([[0, 1],
       [2, 3]])

>>> arr2 = np.arange(10, 18).reshape(2, 4)
>>> arr2
array([[10, 11, 12, 13],
       [14, 15, 16, 17]])

>>> np.dot(arr, arr2)
array([[14, 15, 16, 17],
       [62, 67, 72, 77]])



3. Broadcasting

연산이 불가한 서로 다른 shape의 array를, 특정 조건이 만족할 때 연산이 가능하게 해주는 기능을 의미한다.

  1. 계산하려는 array의 shape 중 차원(axis)가 동일거나, 1일 때.
    >>> a = np.arange(24).reshape(2, 3, 4)
    >>> 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]]])
    
    #  (2, 3, 4) + (4,)
    >>> a + np.array([0, 10, 100, 1000])
    array([[[   0,   11,  102, 1003],
            [   4,   15,  106, 1007],
            [   8,   19,  110, 1011]],
    
           [[  12,   23,  114, 1015],
            [  16,   27,  118, 1019],
            [  20,   31,  122, 1023]]])
    
    # (2, 3, 4) + (3, 1)
    >>> a + np.array([[10],
                      [100],
                      [1000]])
    array([[[  10,   11,   12,   13],
            [ 104,  105,  106,  107],
            [1008, 1009, 1010, 1011]],
    
           [[  22,   23,   24,   25],
            [ 116,  117,  118,  119],
            [1020, 1021, 1022, 1023]]])
    
    # (2, 3, 4) + (2, 1, 1)
    >>> a + np.array([[[10]],
                      [[100]]])
    array([[[ 10,  11,  12,  13],
            [ 14,  15,  16,  17],
            [ 18,  19,  20,  21]],
    
           [[112, 113, 114, 115],
            [116, 117, 118, 119],
            [120, 121, 122, 123]]])
    
    # (2, 3, 4) + (3, 4)
    >>> a + np.array([[1, 10, 100, 1000],
                      [2, 20, 200, 2000],
                      [3, 30, 300, 3000]])
    array([[[   1,   11,  102, 1003],
            [   6,   25,  206, 2007],
            [  11,   39,  310, 3011]],
    
           [[  13,   23,  114, 1015],
            [  18,   37,  218, 2019],
            [  23,   51,  322, 3023]]])
    
    # (2, 3, 4) + (2, 1, 4)
    >>> a + np.array([[[1, 10, 100, 1000]],
                      [[2, 20, 200, 2000]]])
    array([[[   1,   11,  102, 1003],
            [   5,   15,  106, 1007],
            [   9,   19,  110, 1011]],
    
           [[  14,   33,  214, 2015],
            [  18,   37,  218, 2019],
            [  22,   41,  222, 2023]]])
  1. 하나의 원소로 이루졌거나, 숫자를 연산할 때.
    >>> a = np.array([1, 2, 3])
    >>> a + 1
    array([2, 3, 4])
    
    >>> a + np.array(10)
    array([11, 12, 13])



4. 선형대수 참고자료

KOCW 2013년 2학기 선형대수 한양대학교 이상화
Khan Academy

0개의 댓글