DATA MSE week 05

Week 5 (NumPy 02 - 배열 연산(산술, 내적, 외적), 브로드캐스팅, 그 외 기타 함수)

  • 목표
    • 벡터/행렬 연산을 할 수 있다.
    • 브로드 캐스팅을 이해한다.
    • Determination, Eigen value 등을 계산할 수 있다.

      수업 05-1

  • 벡터의 크기 벡터 \(\boldsymbol a\) 의 크기는 \(|\boldsymbol a|=\sqrt{a_1^2+a_2^2+a_3^2}=\sqrt{\sum_i^3a_i^2}\)
  • 벡터의 내적 \(\boldsymbol a \cdot \boldsymbol b=a_1b_1+a_2b_2+a_3b_3=\sum_i^3a_ib_i\) 혹은 아래와 같이 두 벡터 사이의 끼인 각 $\theta$ 를 활용해 표현할 수 있다. \(\boldsymbol a \cdot \boldsymbol b=|\boldsymbol a||\boldsymbol b|\cos\theta\) List, len, range, enumerate를 활용하여 아래와 같이 표현 가능
    a=[1,2,3]
    b=[4,5,6]
    dotprod=0.
    for i in range(3):
      dotprod=dotprod+a[i]*b[i]
      ## 위를 `dotprod+=a[i]*b[i]`로 줄여서 표현 가능
    

    NumPy의 배열을 활용한다면?

    a=np.array([1,2,3])
    b=np.array([4,5,6])
    dotprod=0.
    for i in range(3):
      dotprod+=a[i]*b[i]
    
  • 행렬간의 dot product

    \(\begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} \cdot \begin{bmatrix} 2 & 0 \\ 1 & 3 \end{bmatrix} = ?\)

    A = np.array([[1, 2], [3, 4]])
    B = np.array([[2, 0], [1, 3]])
    
    print(A @ B)          # 행렬 곱
    print(np.dot(A, B))   # 동일
    

    두 2x2 행렬 \(\boldsymbol A\) 와 \(\boldsymbol B\) 의 곱 결과가 또 다른 2x2행렬 \(\boldsymbol C\) 이라면 \(\boldsymbol A\cdot\boldsymbol B = \boldsymbol C\) 와 같이 표현할 수 있다. 이를 index를 활용한 방식으로 아래와 같이 표기 가능하다. \(\sum_k^3A_{ik}B_{kj}=C_{ij}, \text{ for } i=1,2,3.\)

  • 외적 \(\boldsymbol c = \boldsymbol a \times \boldsymbol b\) \(c_i=\epsilon_{ijk}a_jb_k\)

    import numpy as np
    
    a = np.array([1, 2, 3])
    b = np.array([4, 5, 6])
    
    c = np.cross(a, b)
    print(c)  # [-3  6 -3]
    

    예시 - tetrahedral site & octaheral site 크기 구하기

  • Broadcasting
    • 브로드캐스팅은 서로 다른 shape의 배열끼리 연산할 때 NumPy가 자동으로 차원을 맞춰주는 기능
    • 예시 (1차원+0차원(스칼라))
      arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
      print(arr + 10)  # [11 12 13 ... 19]
      
    • 예시 (2차원 + 1차원)
      mat = np.array([[1, 2, 3],
                      [4, 5, 6]])
      vec = np.array([10, 20, 30])
      
      print(mat + vec)
      # [[11 22 33]
      #  [14 25 36]]
      
    • 주의 뒤에서부터 비교하며 차원이 같거나 1이면 확장 가능 하나라도 불가능하면 에러 발생
  • Other various features
    arr = np.array([1, 4, 9, 16])
    
    print(np.sqrt(arr))   # 제곱근 → [1. 2. 3. 4.]
    print(np.exp(arr))    # e^x
    print(np.log(arr))    # 자연로그
    print(np.sin(arr))    # 사인 함수
    print(np.mean(arr))   # 평균
    print(np.sum(arr))    # 합
    print(np.min(arr))    # 최소값
    print(np.max(arr))    # 최대값
    print(np.std(arr))    # 표준편차
    
    arr = np.array([3, 1, 2])
    
    print(np.sort(arr))        # 정렬된 배열
    print(np.argsort(arr))     # 정렬 인덱스
    print(np.argmax(arr))      # 최대값 인덱스
    print(np.argmin(arr))      # 최소값 인덱스
    
    ind=np.argsort(arr)
    arr[ind] ## sorting 이 된 배열
    
    # 추가 예제
    names=['Michael','Jim','Pam','Dwight','Kevin','Creed']
    scores=[5, 30, 20, 40, 10, 25]
    
    inds=np.argsort(scores)
    print(names[inds]) ## score에 따라 정렬된 배열
    

    수업 05-2

  • Einstein summation
    • 벡터 스케일링 (스칼라 곱) \(c\boldsymbol a=\boldsymbol b\) 이를 index 표기법으로 나타내면 \(b_i=ca_i \text{ with } i=1,2,3\)
      ## List로 구현
      c=0.3
      a=[1,2,3]
      b=[] # empty list
      for i in range(3): ## iteration
        b.append(c*a[i])
      
      ## Numpy로 구현
      c=0.3
      a=np.array([1,2,3])
      b=c*a ## broadcasting
      
    • 단위 벡터 (unit) 벡터 \(\boldsymbol a\) 의 크기가 1 이라면, 벡터 \(\boldsymbol a\) 를 단위 벡터라 부른다.
      • 주어진 한 벡터 \(\boldsymbol a\) 의 단위 벡터를 \(\bar{\boldsymbol a}\) 라 할 때 아래와 같이 그 관계가 표현될 수 있다. \(\bar{\boldsymbol a}=\frac{\boldsymbol a}{|\boldsymbol a|}\) 혹은 인덱스를 활용해 아래와 같이 표현된다. \(\bar{a}_i=\frac{a_i}{\sqrt{a_1^2+a_2^2+a_3^2}}\)
      • 예시: 주어진 벡터 \(\boldsymbol a\) 와 방향은 같으나 크기가 1인 단위 벡터를 구하시오.
        a=[3,4,5]
        magnitude=0. ## 벡터 크기
        for i in range(len(a)):
          magnitude+=a[i]**2
        magnitude=magnitude**0.5 ## sqrt(a)
        for i in range(len(a)):
          a[i]=a[i]/mag
        print(bar_a)
        

        위를 Numpy를 활용하면

        import numpy as np
        old_a=np.array([3,4,5])
        new_a=old_a**2
        mag=np.sqrt(new_a.sum())
        bar_a=old_a/mag
        print(bar_a)
        

        혹은 더욱 축약한다면

        import nump as np
        old_a=np.array([3,4,5])
        bar_a=old_a/np.sqrt((old_a**2).sum())
        print(bar_a)
        
    • 벡터 내적 \(\boldsymbol a \cdot \boldsymbol b = \sum_i^3 a_ib_i=c\) 위를 Einstein summation convention으로 표기하면 \(\boldsymbol a \cdot \boldsymbol b = a_ib_i=c\) summation 기호 \(\sum\) 가 생략되어 있음에 주목하시오.

      ## Numpy없이 구현
      a=[1,2,3]
      b=[4,5,6]
      c=0.
      for i in range(3):
        c+=a[i]*b[i]
      print(c)
      ## Numpy로 구현
      a=np.array([1,2,3])
      b=np.array([4,5,6])
      c=a*b  ## element-wise operation되는 것을 유념하라.
             ## 즉 c=np.array([a[0]*b[0],a[1]*b[1],a[2]*b[2]])
      c=c.sum()
      print(c)
      
      #혹은 마지막 두 줄을 줄여서 아래와 같은 한줄의 명령어로 바꿀 수 있겠다.
      c=(a*b).sum()
      print(c)
      
    • 행렬 벡터 곱 \(\boldsymbol c = \boldsymbol A \cdot \boldsymbol v\) \(c_i = \sum_j^3A_{ij}v_j \ \text{ for } i=1,2,3\) 위를 Einstein summation convention으로 표기하면 \(c_i=A_{ij}v_j\)
    • 행렬 곱 (single dot) \(\boldsymbol C = \boldsymbol A\cdot \boldsymbol B\) \(C_{ij} = \sum_k^3 A_{ik}B_{kj} \text{ for } (i,j) \text{ of } (1,1), (1,2), ... , (3,2), (3,3)\)

      NumPy를 사용하지 않는다면 아래와 같은 예시로 표현될 수 있겠다.

      A=[[1,2,3],[4,5,6],[7,8,9]]
      B=[[3,2,1],[6,5,4],[9,8,7]]
      C=[]
      for i in range(3):
        C.append([])
        for j in range(3):
          C[i].append(0)
          for k in range(3):
            C[i][j]+=A[i][k]*B[k][j]
      

      numpy를 활용한다면?

      import numpy as np
      A=np.array([[1,2,3],[4,5,6],[7,8,9]])
      B=np.array([[3,2,1],[6,5,4],[9,8,7]])
      C=np.zeros((3,3))
      for i in range(3):
          for j in range(3):
              for k in range(3):
                  C[i,j]+=A[i,k]*B[k,j] ## Nested-list와 달리 ',' 콤마 기호로 각 축의 index를 활용한다.
      print(C)
      # 혹은 dot 활용하여
      C=np.dot(A,B)
      print(C)
      # 혹은 더 줄여서 (python 3.5이상)
      C=A@B # dtype 이 float로 바뀜
      print(C)
      
    • 행렬 곱 (double dot) \(c=\boldsymbol A : \boldsymbol B\) \(c=\sum_i\sum_jA_{ij}B_{ij}=\sum_j\sum_iA_{ij}B_{ij}=\sum_j\sum_iB_{ij}A_{ij}=\sum_i\sum_jB_{ij}A_{ij}\)

      파이썬 코드로 바꾸면…

      A=[[1,2,3],[4,5,6],[7,8,9]]
      B=[[3,2,1],[6,5,4],[9,8,7]]
      ## 1
      c=0.
      for i in range(3): # i is outer
        for j in range(3): # j is inner
          c+=A[i][j]*B[i][j]
      print(c)
      ## 2, 안/바깥 for-loop 바뀜.
      c=0.
      for j in range(3): # j is outer
        for i in range(3): # i is inner
          c+=A[i][j]*B[i][j]
      print(c)
      ## 3. 안/바깥 for-loop 바뀜, 그리고 A와 B의 순서 바뀜
      c=0.
      for j in range(3): # j is outer
        for i in range(3): # i is inner
          c+=B[i][j]*A[i][j] # A[i][j] x B[i][j] 혹은 B[i][j] x A[i][j]
      print(c)
      ## 4. A와 B의 순서 바뀜
      c=0.
      for i in range(3): # i is outer
        for j in range(3): # j is inner
          c+=B[i][j]*A[i][j] # A[i][j] x B[i][j] 혹은 B[i][j] x A[i][j]
      print(c)
      

      numpy를 활용해서 표현해보자.

      A=np.array([[1,2,3],[4,5,6],[7,8,9]])
      B=np.array([[3,2,1],[6,5,4],[9,8,7]])
      ##
      c=0.
      for i in range(3): # i is outer
        for j in range(3): # j is inner
          c+=A[i,j]*B[i,j]