별의 공부 블로그 🧑🏻‍💻
728x90
728x170

배열 객체 관리와 연산

  • 넘파이는 수백만 개의 수치 데이터를 빠르게 처리함으로써 파이썬의 과학 계산을 빠르게 처리하는 라이브러리이다.
  • 넘파이의 N차원 배열은 데이터를 빠르게 처리할 수 있는 구조이다.
  • 이러한 구조는 요소의 데이터 타입과 크기가 정해져 있으며, 인덱싱으로 빠르게 필드에 접근하고 변경할 수 있는 장점이 있다.
  • 넘파이는 메모리 버퍼에 있는 같은 타입의 매트릭스나 벡터 같은 배열 데이터를 하드웨어 레벨인 저수준 형태로 메모리에 저장하고 처리한다.
  • 또한 넘파이는 같은 크기의 메모리를 할당받고, 연속된 메모리 공간에 존재하는 벡터 연산을 지원한다.
  • 효율적인 인터페이스와 최적화된 관련 함수들, 그리고 최적화된 C 코드를 통해 CPU를 관리하는 벡터화 기능을 사용한 빠른 연산도 지원한다.

 

뷰와 복사

뷰(View)

  • 배열 데이터를 보는 하나의 방법
  • 기술적으로 두 객체의 데이터가 공유된다는 것을 의미한다.
  • 원래 배열에서 슬라이싱하는 슬라이스 뷰 dtype을 변경하는 dtype 뷰로 생성할 수 있다.
  • 배열의 변화를 반영하므로 새로운 데이터 타입의 배열이 위치하는 메모리를 다시 해석한다.
  • 슬라이스 뷰는 넘파이에서 뷰를 생성하는 가장 일반적인 방법으로, 다음과 같이 사용한다.
>>> arr = np.arange(10)
>>> arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

>>> v1 = arr[1:2]
>>> v1
array([1])

>>> arr[1] = 2
>>> v1
array([2])

>>> v2 = arr[1::3]
>>> v2
array([2, 4, 7])

>>> arr[7] = 10
>>> v2
array([ 2,  4, 10])

 

  • dtype 뷰로 같은 데이터 영역에 또 하나의 데이터 타입을 할당할 수 있다.
>>> arr = np.zeros(2, dtype=np.uint16)
>>> arr
array([0, 0], dtype=uint16)

>>> arr.view(np.uint8)
array([0, 0, 0, 0], dtype=uint8)

>>> arr.view(np.uint32)
array([0], dtype=uint32)

 

  • astype을 이용하면 새로운 데이터 타입을 가지는 배열을 복사할 수 있는데 그 결과는 뷰와 다르다.
>>> arr = np.zeros(2, dtype=np.uint16)
>>> arr
array([0, 0], dtype=uint16)

>>> arr.astype(np.uint8)
array([0, 0], dtype=uint8)

>>> arr.astype(np.uint32)
array([0, 0], dtype=uint32)

 

  • 2차원 배열의 뷰를 실행하는 경우
>>> arr = np.arange(4, dtype=np.uint16).reshape(2, 2)
>>> arr
array([[0, 1],
       [2, 3]], dtype=uint16)
       
>>> arr.view(np.uint8)
array([[0, 0, 1, 0],
       [2, 0, 3, 0]], dtype=uint8)
       
>>> arr * 100
array([[  0, 100],
       [200, 300]], dtype=uint16)
       
>>> (arr * 100).view(np.uint8)
array([[  0,   0, 100,   0],
       [200,   0,  44,   1]], dtype=uint8)
  • dtype이 uint16인 arr에 dtype이 uint8인 뷰를 실행하면 마지막 축인 열을 따라서 shape가 (2, 4)로 변경된다.
    • 이 결과는 데이터 타입 uint16인 배열 요소 2는 2개의 uint8과 같으므로, 배열 요소가 2와 0으로 변경되는 것과 같다.
  • 그런데 배열 요소의 수가 255를 넘어가면 256은 0으로 세팅되어 300은 44가 된다.
  • uint8이 8비트 이므로 255의 수까지 표현할 수 있기 때문에 그 이상의 수인 256부터는 0이 된다.
  • 따라서 데이터 타입 uint16인 300을 uint8로 변경하면 300과 0이 아니라 44와 1이 된다.
    • 1은 255를 한 번 넘었다는 뜻이다.

 

  • 뷰는 데이터를 복사하지 않고 여러 방법으로 메모리 내에서 넘파이가 효율적으로 수행되도록 한다.
  • 뷰는 정확한 strides와 shape로 배열을 생성한다.
    • 여기서 strides는 특정 축에서 한 단계 이동하기 위해 메모리에서 이동할 바이트 수들을 알려준다.
    • strides가 (4, 2)라는 것은 다른 행으로 이동하려면 4바이트, 열을 이동하려면 2바이트가 소요된다는 것을 의미한다.
>>> arr
array([[0, 1],
       [2, 3]], dtype=uint16)
       
>>> arr.strides
(4, 2)

 

복사

  • 배열 객체 arr을 불리언으로 인덱싱하면 arr 중 해당하는 부분의 복사를 반환한다.
    • 복사는 arr1의 요소를 변경하더라도 원본 배열인 arr의 요소에는 변화가 없다.
>>> arr = np.random.randn(3, 4)
>>> arr
array([[-0.15138   , -0.37173983, -1.96645828,  0.30214658],
       [ 0.76395484,  0.33710357, -1.37829904,  1.5614794 ],
       [-0.04186889, -0.73269011,  0.37541069, -1.00037301]])
       
>>> arr1 = arr[arr>0]
>>> arr1
array([0.30214658, 0.76395484, 0.33710357, 1.5614794 , 0.37541069])

>>> arr1[:] = 0
>>> arr
array([[-0.15138   , -0.37173983, -1.96645828,  0.30214658],
       [ 0.76395484,  0.33710357, -1.37829904,  1.5614794 ],
       [-0.04186889, -0.73269011,  0.37541069, -1.00037301]])

 

  • 그러나 같은 원본 배열에 슬라이싱을 실행해 다른 값을 동적 할당하면 원본 배열인 arr의 요소가 변경되었음을 확인할 수 있다.
>>> arr1 = arr[0, :]
>>> arr1
array([-0.15138   , -0.37173983, -1.96645828,  0.30214658])

>>> arr1[:] = 0
>>> arr
array([[ 0.        ,  0.        ,  0.        ,  0.        ],
       [ 0.76395484,  0.33710357, -1.37829904,  1.5614794 ],
       [-0.04186889, -0.73269011,  0.37541069, -1.00037301]])

 

  • 를 사용하는 경우와 copy() 함수를 사용하는 경우의 비교
>>> arr = np.random.randn(3, 4)
>>> arr
array([[ 0.50856536,  0.30074826, -1.97176039,  0.34067095],
       [ 0.03926801,  0.60489558,  0.06880529,  0.44914903],
       [ 0.2767649 , -0.45157678,  1.58307681,  0.56481563]])
       
>>> arr1 = arr[0, :]; arr1
array([ 0.50856536,  0.30074826, -1.97176039,  0.34067095])

>>> arr2 = arr1[:].copy()
>>> arr2[:] = 0
>>> arr1
array([ 0.50856536,  0.30074826, -1.97176039,  0.34067095])
  • arr1을 복사한 배열 arr2의 요소에 새로운 값을 동적 할당해도 원래 배열인 arr1의 요소는 변경되지 않는다.
    • 따라서 배열 arr의 어떤 요소도 변경되지 않는다.

 

  • 넘파이는 빅데이터를 처리하는 연산에서 복사보다 뷰를 사용함으로써 메모리에 주는 부담을 줄여 처리 속도를 높인다.

 

 

브로드캐스팅

  • 일반적으로 크기가 다른 배열을 더하거나 빼는 등의 산술 연산은 할 수 없다.
  • 브로드캐스팅 다른 shape를 가진 배열들을 산술 연산할 수 있도록 방법을 제공한다.
  • 또한 브로드캐스팅을 이용하면 코드를 단순화할 수 있다.
>>> a = np.array([1.0, 2.0, 3.0])
>>> b = np.array([2.0, 2.0, 2.0])
>>> arr = a * b
>>> arr
array([2., 4., 6.])

 

  • 가장 간단한 브로드캐스팅은 배열과 스칼라 값이 결합되어 연산될 때 발생한다.
>>> a = np.array([1.0, 2.0, 3.0])
>>> b = 2.0
>>> a * b
array([2., 4., 6.])
  • 산술 연산을 수행하는 동안, 스칼라 b가 a와 같은 shape를 가지는 배열로 확장되는 개념으로 이해할 수 있다.
  • 이러한 브로드캐스팅은 곱셈 연산 작업 동안 메모리를 최적화하고 효율적으로 사용한다.

1차원 배열과 스칼라의 브로드캐스팅

 

  • shape (4, 3)인 배열 a와 shape (3, )인 배열 b를 더하면 브로드캐스팅 규칙에 따라 다음 그림과 같은 확장 개념으로 호환성을 확보한다.

 

브로드캐스팅 규칙

  • 넘파이에서 두 배열을 연산할 때, 요소 단위로 두 배열의 shape를 비교한 차원이 동일하거나 둘 중 하나가 1일 때 호환할 수 있다.
  • 두 배열을 연산한 결과의 크기는 입력 배열들 중 최대 크기를 가진 차원을 따라 정해진다.

 

예제 : 빛의 3원색 RGB 값인 256x256x3 배열을 가진 이미지의 색을 조정(scale)할 경우
  • 이때 이미지를 값이 3개 있는 1차원 배열로 곱한다고 할 수 있다.
  • 브로드캐스팅 규칙에 의해 이 배열들의 후행 축을 따라 정렬하면 호환성이 있음을 알 수 있다.
A (4차원 배열)     : 256 x 256 x 3 
B (3차원 배열)     :             3
결과 (4차원 배열)  : 256 x 256 x 3
  • 3개의 값이 있는 1차원 배열은 3차원 배열과 연산할 수 있다.
  • 비교 차원들 중, 크기가 1인 차원이 있다면 다른 차원과 일치되기 위해 차원이 늘려지거나 복사된다.

 

  • 길이가 1인 축들을 가진 경우
    • A는 4차원 배열, B는 3차원 배열로써 브로드캐스팅 연산을 하는 동안 길이가 1인 축은 더 큰 크기로 확장된다.
A (4차원 배열)    : 8 x 1 x 6 x 1
B (3차원 배열)    : 7 x 1 x 5
결과 (4차원 배열)  : 8 x 7 x 6 x 5

 

  • A가 3차원 배열, B가 3차원 배열일 때 축의 크기가 1인 연산은 다음과 같이 수행된다.
A (3차원 배열) : 15 x 3 x 5
B (3차원 배열) : 15 x 1 x 5
결과 (3차원 배열) : 15 x 3 x 5

 

  • A가 3차원 배열, B가 2차원 배열일 때 shape가 다르거나 축의 크기가 1인 연산은 다음과 같이 수행된다.
A (3차원 배열)    : 15 x 3 x 5
B (2차원 배열)    :      3 x 5
결과 (3차원 배열) : 15 x 3 x 5

A (3차원 배열)     : 15 x 3 x 5
B (2차원 배열)     :      3 x 1
결과 (3차원 배열)  : 15 x 3 x 5

 

  • 브로드캐스팅 규칙이 적용된 예제
>>> arr1 = np.arange(4)
>>> arr2 = arr1.reshape(4, 1)
>>> arr3 = np.ones(5)
>>> arr4 = np.ones((3, 4))
>>> arr1.shape
(4,)

>>> arr3.shape
(5,)

>>> arr1 + arr3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: operands could not be broadcast together with shapes (4,) (5,)

>>> arr2.shape
(4, 1)

>>> (arr2 + arr3).shape
(4, 5)

>>> arr2 + arr3
array([[1., 1., 1., 1., 1.],
       [2., 2., 2., 2., 2.],
       [3., 3., 3., 3., 3.],
       [4., 4., 4., 4., 4.]])
       
>>> arr4.shape
(3, 4)

>>> (arr1 + arr4).shape
(3, 4)

>>> arr1 + arr4
array([[1., 2., 3., 4.],
       [1., 2., 3., 4.],
       [1., 2., 3., 4.]])

 

numpy.newaxis 적용

  • 상수인 numpy.newaxis를 적용하면 1차원 배열을 2차원 열 벡터나 행 벡터로 변환할 수 있다.
>>> arr = np.array([0, 1, 2, 3])
>>> arr.shape
(4,)

>>> arr[np.newaxis, :]    # 행 벡터로 변환
array([[0, 1, 2, 3]])

>>> arr[:, np.newaxis]    # 열 벡터로 변환
array([[0],
       [1],
       [2],
       [3]])

 

  • shape (4, 1)과 shape (3,)의 연산
    • 넘파이 인덱스 연산자인 newaxis를 사용해 1차원 shape (4,)인 배열 arr1을 2차원 shape (4, 1)로 만들어 shape (3,)인 arr2와 덧셈 연산을 할 수 있도록 한다.
>>> arr1 = np.array([0, 1, 2, 3])
>>> arr2 = np.array([10, 20, 30])
>>> arr11 = arr1[:, np.newaxis]
>>> arr11 + arr2
array([[10, 20, 30],
    [11, 21, 31],
    [12, 22, 32],
    [13, 23, 33]])

1차원 배열과 1차원 배열의 브로드캐스팅

 

broadcast 클래스

  • 브로드캐스팅을 모방하는 객체를 생성한다.
  • 입력 매개 변수들을 차례로 브로드캐스팅하고 결과를 감싸는(Encapsulation) 객체를 반환한다.
클래스 numpy.broadcast
매개 변수 in1, in2, ...:유사 배열, 입력 매개 변수
반환 값 b: broadcast 객체

 

  • broadcast 클래스의 속성과 메소드
구분 종류 기능
속성 index 브로드캐스팅된 결과에서 현재 인덱스
iters self의 요소에 따른 이터레이터(iterators)의 튜플
nd 브로드캐스팅된 결과의 차원 수
ndim 브로드캐스팅된 결과의 차원 수(넘파이 1.12.0 이후 버전)
numiter 브로드캐스트 된 결과에 의해 소유된 반복자의 수
shape 브로드캐스트 된 결과의 shape
size 브로드캐스트 된 결과의 총 크기
메소드 reset() 브로드캐스트 된 결과의 iterators를 리셋

 

사용 예
  • 속성 numiter
>>> arr1 = np.array([1, 2, 3])
>>> arr2 = np.array([[4], [5], [6]])
>>> arr = np.broadcast(arr1, arr2)
>>> arr.numiter
2

 

  • shape (3, 1)과 shape(3,)인 배열의 연산
    • 브로드캐스팅을 이용해 2개의 벡터를 더한다.
    • 브로드캐스팅 실행 결과는 broadcast 객체이며, arr1의 shape는 (3, 1)이고, arr2의 shape가 (3,)이 되어 브로드캐스팅할 수 있는 조건이 된다.
    • arr2과 arr2가 브로드캐스팅되면 arr1의 shape는 (3, 1)로 확장되고, arr2의 shape도 (3, 3)으로 확장되어 연산할 수 있는 상태가 된다.
>>> arr1 = np.array([[1], [2], [3]])
>>> arr2 = np.array([4, 5, 6])
>>> arr = np.broadcast(arr1, arr2)
>>> arr
<numpy.broadcast at 0x6bb60f8>

>>> arr.shape
(3, 3)

 

  • flatiter 객체 생성, arr1 + arr2 연산
    • out.flat을 실행하면 반복 처리할 수 있는 상태의 객체인 numpy.flatiter가 된다.
      • for 반복문이나 next() 메소드를 호출함으로써 마치 1차원 배열인 것처럼 배열의 반복 처리(Iteration)를 허용한다.
    • [u+v for (u, v) in arr]를 반복 처리한 값들을 out.flat flatiter 객체에 동적 할당하므로 배열 객체인 out이 생성된다.
>>> out = np.empty(arr.shape)
>>> out.flat = [u+v for (u, v) in arr]
>>> type(out.flat)
<class 'numpy.flatiter'>

>>> out
array([[5., 6., 7.],
       [6., 7., 8.],
       [7., 8., 9.]])
       
>>> arr1 + arr2
array([[5, 6, 7],
       [6, 7, 8],
       [7, 8, 9]])
  • flatiter 클래스의 속성과 메소드
구분 종류 기능
속성 base 반복되는 대상인 배열을 참조(Reference)
coords 현재 좌표(Coordinate)의 N차원 튜플
index flat 인덱스를 배열로 나타냄.
메소드 copy() 1차원 배열로써 이터레이터의 복사본을 얻음.
  • flatiter 이터레이터 flatiter 생성자를 호출하여 동작하고, 파이썬 코드로 직접 생성할 수는 없다.
>>> arr = np.arange(6).reshape(2, 3)
>>> f1 = arr.flat
>>> for item in f1:
...     print(item)
...
0
1
2
3
4
5

>>> type(f1)
<class 'numpy.flatiter'>

>>> f1[2:4]
array([2, 3])

 

  • base index 속성의 사용
>>> arr1 = np.arange(5)
>>> f1 = arr1.flat
>>> f1.base is arr1
True

>>> arr2 = np.arange(6).reshape(2, 3)
>>> f2 = arr2.flat
>>> f2.index
0

 

  • flatiter의 속성 중 하나인 numpy.flatiter.coords는 현재 좌표를 N차원의 튜플로 표기한다.
>>> arr1 = np.arange(6).reshape(2, 3)
>>> f1 = arr1.flat
>>> f1.coords
(0, 0)

>>> next(f1)
0

>>> f1.coords
(0, 1)

>>> next(f1)
1

>>> f1.coords
(0, 2)

 

  • flat 속성
    • ndarray 클래스
    • 1차원 배열을 반복하는 이터레이터
    • numpy.flatiter 인스턴스
  • 다음과 같이 arr.flat을 실행하면 2차원 배열인 arr이 반복 처리할 수 있는 1차원 배열이 되어 arr.flat[3]과 같은 값을 구할 수 있다.
>>> arr = np.arange(1, 7).reshape(2, 3)
>>> arr
array([[1, 2, 3],
       [4, 5, 6]])
       
>>> arr.flat[3]
4

>>> arr.T
array([[1, 4],
       [2, 5],
       [3, 6]])
       
>>> arr.T.flat[3]
5

>>> type(arr.flat)
<class 'numpy.flatiter'>

 

  • 다음과 같이 flat 속성을 적용하고 동적 할당을 할 수 있다.
>>> arr.flat[[1, 4]]
array([2, 5])

>>> arr.flat = 3; arr
array([[3, 3, 3],
       [3, 3, 3]])
       
>>> arr.flat[[1, 4]] = 1; arr
array([[3, 1, 3],
       [3, 1, 3]])

 

브로드캐스팅 연산

  • 크기가 서로 다른 배열을 크기가 같아지도록 확장 개념을 사용해 브로드캐스팅으로 변환하면 크기가 다른 배열 간 연산을 실행할 수 있다.
    • 입력 데이터보다 더 많은 출력을 얻는 문제를 해결할 때 이 방법을 사용할 수 있다.
>>> arr = np.arange(10)
>>> arr_br = arr - arr[:, np.newaxis]
>>> arr_br
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [-1,  0,  1,  2,  3,  4,  5,  6,  7,  8],
       [-2, -1,  0,  1,  2,  3,  4,  5,  6,  7],
       [-3, -2, -1,  0,  1,  2,  3,  4,  5,  6],
       [-4, -3, -2, -1,  0,  1,  2,  3,  4,  5],
       [-5, -4, -3, -2, -1,  0,  1,  2,  3,  4],
       [-6, -5, -4, -3, -2, -1,  0,  1,  2,  3],
       [-7, -6, -5, -4, -3, -2, -1,  0,  1,  2],
       [-8, -7, -6, -5, -4, -3, -2, -1,  0,  1],
       [-9, -8, -7, -6, -5, -4, -3, -2, -1,  0]])

 

  • 브로드캐스팅은 이미지 처리 및 관리 분야에도 적용할 수 있다.
import numpy as np
import matplotlib.pyplot as plt

arr1 = np.arange(10)
arr2 = np.arange(7)
arr_img = np.sqrt(arr1[:, np.newaxis]**2 + arr2**2)
plt.pcolor(arr_img)
plt.colorbar()
plt.axis('equal')
plt.show()

 

예제 : 수능 성적 분석
  • 수능에서 국어, 영어, 수학 점수가 정규 분포를 따른다고 할 때, 평균과 편차를 알고 있고, 수능의 난이도가 같다면 특정 학교의 학력을 측정하는 데이터로 사용할 수 있다.
  • 전국 학생들의 국어, 영어, 수학 평균 점수가 각각 70, 67, 59점이며 표준 편차는 각각 5, 7, 9일 때 샘플 15개를 활용한 정규 분포 그리고 브로드캐스팅을 적용한 예제는 다음과 같다.
>>> arr = np.random.normal(loc=[70., 67., 59.], scale = [5., 7., 9.], size = (5, 3))
>>> arr
array([[65.55729619, 70.75992918, 48.7683003 ],
       [78.76417195, 62.58442299, 63.39660319],
       [69.94811844, 54.83676312, 68.23035151],
       [71.4668727 , 60.38309697, 60.72215292],
       [67.83736681, 74.23596354, 55.02305071]])

 

  • 다음으로 열 단위 평균을 구한 후, arr에서 arr_vec를 뺀다.
  • arr의 shape는 (5, 3), arr_avc의 shape는 (3,)으로써 브로드캐스팅 조건에 적합하므로 뺄셈을 할 수 있다.
>>> arr_avc = arr.mean(axis = 0)
>>> arr_avc
array([70.71476522, 64.56003516, 59.22809172])

>>> arr - arr_avc
array([[ -5.15746903,   6.19989402, -10.45979142],
       [  8.04940673,  -1.97561217,   4.16851146],
       [ -0.76664678,  -9.72327204,   9.00225979],
       [  0.75210749,  -4.17693819,   1.49406119],
       [ -2.87739841,   9.67592838,  -4.20504101]])
       
>>> arr_avr = arr.mean(axis = 1)
>>> arr_avr
array([61.69517522, 68.24839937, 64.33841102, 64.19070753, 65.69879369])

 

  • 행 단위로 평균을 구할 때 arr_avr의 shape는 (5,)이고, arr의 shape가 (5, 3)이므로 브로드캐스팅 조건에 적합하지 않아 연산할 수 없다.
  • 따라서 브로드캐스팅 조건에 적합하도록 np.newaxis 상수를 사용해 arr_avr의 차원을 2차원인 (5, 1)로 확장하여 연산한다.
>>> arr_avr[:, np.newaxis]
array([[61.69517522],
       [68.24839937],
       [64.33841102],
       [64.19070753],
       [65.69879369]])
       
>>> arr - arr_avr[:, np.newaxis]
array([[  3.86212097,   9.06475395, -12.92687492],
       [ 10.51577257,  -5.66397639,  -4.85179619],
       [  5.60970742,  -9.5016479 ,   3.89194049],
       [  7.27616517,  -3.80761056,  -3.46855461],
       [  2.13857312,   8.53716985, -10.67574297]])

 

 

배열 조작과 정렬

C 우선 배치와 F 우선 배치

  • 넘파이 배열은 행 우선순위로 처리된다.
    • 배열의 행에 위치한 데이터들이 서로 인접해 로딩된다는 것을 의미한다.
    • 옵션에 F, C를 정하지 않으면 기본값 C 우선 배치로 처리된다.
      • F : Fortran 언어
      • C : C 언어
>>> arr = np.arange(12).reshape(3, 4)
>>> arr
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
       
>>> arr.ravel(order='F')
array([ 0,  4,  8,  1,  5,  9,  2,  6, 10,  3,  7, 11])

>>> arr.ravel(order='C')
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

 

  • ravel() 함수 numpy.ndarray.ravel numpy.ravel로 구분하여 사용하지만, 1차원 배열을 반환한다는 점에서 기능은 같다.

 

>>> arr.reshape(2, 6)
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11]])
       
>>> arr.ravel(order='F').reshape(2, 6)
array([[ 0,  4,  8,  1,  5,  9],
       [ 2,  6, 10,  3,  7, 11]])
  • 배열 arr을 F순으로 인덱싱하고, shape (2, 6)으로 변형한다.

 

  • order 키워드
    • 배열 arr에서 값들을 가져오고 값들을 출력 배열에 배치하기 위해 인덱스 순서를 제어한다.
  • C 우선 배치와 F 우선 배치는 인접(Contigunous) 배열과 비인접(Uncontiguous) 배열 개념으로 설명할 수 있다.
  • 배열의 요소는 메모리에 저장되는데 인접 배열은 연속된 메모리 블록에 저장되며, 배열에서 다음 요솟값으로 접근할 때는 메모리 내에서 다음 주소로 이동하게 된다.
  • 옵션이 설정되지 않을 때 배열 요소는 다음 처럼 C 우선으로 배치되어 arr이 shape (3, 2)로 표시된다.
    • 그리고 메모리는 인접 배치되어 접근한다.

 

  • 행이 연속된 메모리 블록으로 저장되므로 C 인접 배열이 된다.
  • 여기에서 다음과 같이 배열 arr arr.T로 전치하면 열이 메모리 인접 블록으로 저장되므로 F 인접 배열이 된다.

 

  • 메모리 인접 블록으로 저장된 배열을 연산하면 매우 빠르게 처리할 수 있다.
  • C 인접 배열 F 인접 배열보다 빠른 연산을 할 수 있으며, 넘파이는 C 인접 배열을 기본으로 한다.
>>> arr = np.arange(6)
>>> np.reshape(arr, (2, 3))
array([[0, 1, 2],
       [3, 4, 5]])
       
>>> np.reshape(np.ravel(arr), (2, 3))
array([[0, 1, 2],
       [3, 4, 5]])
       
>>> np.reshape(arr, (2, 3), order='F')
array([[0, 2, 4],
       [1, 3, 5]])
       
>>> np.reshape(np.ravel(arr, order='F'), (2, 3), order='F')
array([[0, 2, 4],
       [1, 3, 5]])

 

배열을 이어 붙이고 스택으로 배치하기

  • 배열을 이어 붙일 때 numpy.concatenate() 함수를 사용한다.
    • 이 함수는 설정한 축을 따라 배열을 이어 붙인다.
>>> arr1 = np.array([[1, 2], [3, 4]])
>>> arr2 = np.array([[5, 6]])
>>> np.concatenate((arr1, arr2), axis=0)
array([[1, 2],
       [3, 4],
       [5, 6]])
       
>>> np.concatenate((arr1, arr2.T), axis=1)
array([[1, 2, 5],
       [3, 4, 6]])
       
>>> np.concatenate((arr1, arr2), axis=None)
array([1, 2, 3, 4, 5, 6])

 

  • numpy.vstack() 수직으로 배열을 쌓는다. (행 단위)
    • axis=0을 설정하여 이어 붙이는 것과 동일
  • numpy.hstack() 수평으로 배열을 쌓는다. (열 단위)
    • axis=1을 설정하여 이어 붙이는 것과 동일
>>> np.hstack((arr1, arr2))
ValueError: all the input array dimensions for the concatenation axis must match exactly, but along dimension 0, the array at index 0 has size 2 and the array at index 1 has size 1

>>> np.vstack((arr1, arr2))
array([[1, 2],
       [3, 4],
       [5, 6]])

 

  • 다음 예의 arrays는 shape (3, 4)의 2차원 배열 10개 요소로 구성되는 리스트이다.
  • 이것을 수직으로 쌓기 위해 axis=0 stack() 함수를 실행하면 shape (10, 3, 4)인 3차원 배열이 된다.
  • 수평으로 쌓기 위해 axis=1로 설정하면 shape (3, 10, 4)가 된다.
  • axis=2로 설정하면 shape (3, 4, 10)이 된다.
>>> arrays = [np.random.randn(3, 4) for _ in range(10)]
>>> np.stack(arrays, axis=0).shape
(10, 3, 4)

>>> np.stack(arrays, axis=1).shape
(3, 10, 4)

>>> np.stack(arrays, axis=2).shape
(3, 4, 10)

>>> arr1 = np.array([1, 2, 3])
>>> arr2 = np.array([2, 3, 4])
>>> np.stack((arr1, arr2))
array([[1, 2, 3],
       [2, 3, 4]])
       
>>> np.stack((arr1, arr2), axis=-1)
array([[1, 2],
       [2, 3],
       [3, 4]])

 

  • numpy.vstack() 행 단위로 수직 순서대로 배열을 쌓는다.
  • 이것은 shape가 (N,)인 1차원 배열 (1, N) shape로 변형한 후 첫 번째 축을 따라 이어 붙이는 것과 동일하다.
>>> arr1 = np.array([1, 2, 3])
>>> arr2 = np.array([2, 3, 4])
>>> np.vstack((arr1, arr2))
array([[1, 2, 3],
       [2, 3, 4]])

>>> arr3 = np.array([[1], [2], [3]])
>>> arr4 = np.array([[2], [3], [4]])
>>> np.vstack((arr3, arr4))
array([[1],
       [2],
       [3],
       [2],
       [3],
       [4]])

 

  • numpy.hstack() 열 단위로 수평 순서대로 배열을 쌓는다.
  • 이것은 첫 번째 축을 따라 이어 붙이는 1차원 배열을 제외하고 두 번째 축을 따라 이어 붙이는 것과 동일하다.
>>> arr1 = np.array((1, 2, 3))
>>> arr2 = np.array((2, 3, 4))
>>> np.hstack((arr1, arr2))
array([1, 2, 3, 2, 3, 4])

>>> arr3 = np.array([[1], [2], [3]])
>>> arr4 = np.array([[2], [3], [4]])
>>> np.hstack((arr3, arr4))
array([[1, 2],
       [2, 3],
       [3, 4]])

 

배열 순서 정렬

  • 넘파이에는 순서를 정렬(Sorting)하는 여러 함수가 있다.
    • 이 함수들은 실행 속도, 요구되는 작업 공간이나 안정도 등에 따라 구분하기도 한다.
>>> arr = np.array([[1, 4], [3, 1]])
>>> np.sort(arr)    # 마지막 축을 따라 정렬
array([[1, 4],
       [1, 3]])
       
>>> np.sort(arr, axis=None)    # 풀린 배열을 정렬
array([1, 1, 3, 4])

>>> np.sort(arr, axis=0)    # 첫 번째 축을 따라 정렬
array([[1, 1],
       [3, 4]])

 

  • 구조화된 배열을 정렬할 때, order 키워드로 정렬할 필드를 지정할 수 있다.
>>> dtype = [('name', 'S10'), ('height', float), ('age', int)]
>>> values = [('Jin', 175, 59), ('Suho', 185, 19), ('Naeun', 162, 28)]
>>> arr = np.array(values, dtype=dtype)    # 구조화된 배열 생성
>>> np.sort(arr, order='height')
array([(b'Naeun', 162., 28), (b'Jin', 175., 59), (b'Suho', 185., 19)],
      dtype=[('name', 'S10'), ('height', '<f8'), ('age', '<i4')])

 

  • 배열을 age 기준으로 정렬한다.
    • age가 동일하면 height를 기준으로 정렬한다.
>>> np.sort(arr, order=['age', 'height'])
array([(b'Suho', 185., 19), (b'Naeun', 162., 28), (b'Jin', 175., 59)],
      dtype=[('name', 'S10'), ('height', '<f8'), ('age', '<i4')])

 

  • 배열 정렬 함수 numpy.ndarray.sort()를 이용해 정렬할 수도 있다.
>>> arr = np.array([[1, 4], [3, 1]])
>>> arr.sort(axis=1)
>>> arr
array([[1, 4],
       [1, 3]])
       
>>> arr.sort(axis=0)
>>> arr
array([[1, 3],
       [1, 4]])

 

  • numpy.argsort는 배열을 정렬할 때 인덱스를 반환한다.
>>> arr = np.array([3, 1, 2])
>>> sorted_by_index = np.argsort(arr)
>>> sorted_by_index
array([1, 2, 0], dtype=int64)

>>> for i in sorted_by_index:
...     print(arr[i])
...
1
2
3
>>> arr = np.array([[0, 3], [2, 2]])
>>> arr
array([[0, 3],
       [2, 2]])
       
>>> np.argsort(arr, axis=0)		# 첫 번째 축을 따라 정렬
array([[0, 1],
       [1, 0]], dtype=int64)
       
>>> np.argsort(arr, axis=1)		# 마지막 축을 따라 정렬
array([[0, 1],
       [0, 1]], dtype=int64)

 

  • order 키워드로 다음과 같이 배열을 정렬할 수 있다.
>>> arr = np.array([(1, 0), (0, 1)], dtype=[('x', '<i4'), ('y', '<i4')])
>>> arr
array([(1, 0), (0, 1)], dtype=[('x', '<i4'), ('y', '<i4')])

>>> np.argsort(arr)
array([1, 0], dtype=int64)

>>> np.argsort(arr, order=('x', 'y'))
array([1, 0], dtype=int64)

>>> np.argsort(arr, order=('y', 'x'))
array([0, 1], dtype=int64)

 

  • numpy.lexsort keys 인수의 순서를 사용하여 간접적으로 안정적인(stable) 정렬을 수행한다.
>>> arr1 = np.array([2, 2, 1, 1])
>>> arr2 = np.array([2, 2, 1, 1])
>>> arr = np.lexsort((arr1, arr2))
>>> arr
array([2, 3, 0, 1], dtype=int64)
  • lexsort는 keys 인수인 튜플(arr1, arr2) 중 먼저 arr2를 정렬하고 그 다음 arr1을 정렬한다.
  • arr2 요솟값들이 모두 같으면 arr1을 기준으로 정렬한다.
    • 따라서 arr2의 [2, 2, 1, 1]에서 세 번째 1과 네 번째 1을 우선 정렬해야 하는데 값이 같으므로 arr1을 참조해야 한다.
    • arr1 역시 세 번째와 네 번째 요소가 같으므로 앞부분의 1이 우선 정렬된다.
    • 결국 arr의 세 번째 위치에 있는 1이 가장 먼저 정렬되므로 그 인덱스인 2가 가장 앞에 위치한다.

 

>>> surnames = ('Hong', 'Na', 'Kim')
>>> first_names = ('Gildong', 'Haeseok', 'Mandeok')
>>> ind = np.lexsort((first_names, surnames))
>>> ind
array([0, 2, 1], dtype=int64)

>>> [surnames[i] + "," + first_names[i] for i in ind]
['Hong,Gildong', 'Kim,Mandeok', 'Na,Haeseok']

 

  • 다음과 같이 숫자들로 구성된 a와 b를 인수로 정렬할 수 있다.
    • a열에서 1은 2개이며, 위치는 인덱스 0과 2이다.
    • 따라서 동일한 숫자를 인덱싱하는 것이므로 서열을 정하기 위해 b를 참조할 때 첫 번째 1은 b에서 9고 두 번째 1은 0이므로 두 번째가 앞에 위치한다.
    • 따라서 인덱스 2가 가장 앞에 위치하는 것이다.
    • 이런 방법을 나머지 요소에도 적용한다.
>>> a = [1, 5, 1, 4, 3, 4, 4]    # 첫 번째 열
>>> b = [9, 4, 0, 4, 0, 2, 1]    # 두 번째 열
>>> ind = np.lexsort((b, a))     # a를 기준으로 정렬한 다음 b를 기준으로 정렬
>>> ind
array([2, 0, 4, 6, 5, 3, 1], dtype=int64)

>>> print(ind)
[2 0 4 6 5 3 1]

>>> [(a[i], b[i]) for i in ind]
[(1, 0), (1, 9), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4)]

>>> [(a[i], b[i]) for i in np.argsort(a)]
[(1, 9), (1, 0), (3, 0), (4, 4), (4, 2), (4, 1), (5, 4)]

 

  • 구조화된 배열은 argsort를 이용해 정렬할 수 있다.
>>> arr = np.array([(1, 9), (5, 4), (1, 0), (4, 4), (3, 0), (4, 2), (4, 1)], dtype=np.dtype([('x', int), ('y', int)]))
>>> np.argsort(arr)    # 또는 np.argsort(arr, order=('x', 'y'))
array([2, 0, 4, 6, 5, 3, 1], dtype=int64)

 

  • numpy.searchsorted는 삽입하는 위치에서 순서에 따라 인덱스를 찾는다.
>>> np.searchsorted([1, 2, 3, 4, 5], 3)
2

>>> np.searchsorted([1, 2, 3, 4, 5], 3, side = 'right')
3

>>> np.searchsorted([1, 2, 3, 4, 5], [-10, 10, 2, 3])
array([0, 5, 1, 2], dtype=int64)

 

히스토그램 함수

  • numpy.histogram은 데이터 세트의 히스토그램을 그리며 numpy.searchsorted와 함께 자주 사용한다.
    • 히스토그램 : 연속된 데이터 집합의 도수 분포를 기둥 모양의 직사각형을 이용해 나타내는 그래프
  • numpy.histogram을 사용하면 히스토그램 값과 구간인 bins를 구할 수 있다.
  • histogram() 함수를 실행해 반환되는 값은 요소를 2개 가지는 튜플형 데이터이다.
    • 첫 번째 항 : 도수 분포를 나타내는 히스토그램 값
    • 두 번째 항 : 균일한 너비의 등급을 나타내는 bins
  • 따라서 요소를 a와 b, 즉 첫 번째 항과 두 번째 항으로 각각 나타낼 수 있다.
>>> arr = np.array([15, 16, 16, 17, 19, 20, 22, 35, 43, 45, 55, 59, 60, 75, 88])
>>> np.histogram(arr, bins=[0, 20, 40, 60, 80, 100])
(array([5, 3, 4, 2, 1], dtype=int64), array([  0,  20,  40,  60,  80, 100]))

>>> a, b = np.histogram(arr, bins=[0, 20, 40, 60, 80, 100])
>>> a
array([5, 3, 4, 2, 1], dtype=int64)

>>> b
array([  0,  20,  40,  60,  80, 100])

 

  • matplotlib 모듈을 사용하면 수식으로 표현된 히스토그램을 그래프로 간단히 변환할 수 있다.
import matplotlib.pyplot as plt
import numpy as np

arr = np.array([15, 16, 16, 17, 19, 20, 22, 35, 43, 45, 55, 59, 60, 75, 88])
plt.hist(arr, bins=[0, 20, 40, 60, 80, 100])
plt.title("numbers depending on ages")
plt.show()

 

  • histogram()에 다양한 인수를 적용할 수 있다.
>>> np.histogram([1, 2, 1], bins=[0, 1, 2, 3])
(array([0, 2, 1], dtype=int64), array([0, 1, 2, 3]))

>>> np.histogram(np.arange(4), bins=np.arange(5), density=True)
(array([0.25, 0.25, 0.25, 0.25]), array([0, 1, 2, 3, 4]))

>>> np.histogram([[1, 2, 1], [1, 0, 1]], bins=[0, 1, 2, 3])
(array([1, 4, 1], dtype=int64), array([0, 1, 2, 3]))
  • bins=[0, 1, 2, 3]은 첫 번째 bin [1, 2), 두 번째 bin [2, 3), 마지막 bin [3, 4) 범위를 나타낸다.
    • [1, 2)에서 오른쪽 소괄호 앞에 있는 값은 포함되지 않음을 의미하므로 1보다 크거나 같고 2보다 작은 범위이다.

 

 

배열 연산

  • 넘파이에서 배열은 같은 타입 요소를 가지는 다차원 배열이다.
  • 요소를 나타내는 인덱스는 양의 정수인 튜플이라는 데이터 타입으로 표기된다.
  • 넘파이에서 중요하게 언급되는 차원 이라고 한다.

 

다차원 배열 연산

  • 파이썬은 고수준의 동적 언어이지만, 파이썬의 넘파이 라이브러리는 저수준 언어인 C 보다 느리다.
  • 하지만, 다차원 배열 연산을 빠르게 처리하기 위해 고성능 수치 계산을 수행하는 특별한 데이터 구조를 제공한다.
  • 넘파이는 C로 설계된 다차원 배열 구조를 실행하면서 파이썬 인터페이스를 제공하므로 빠르게 연산을 처리할 수 있다.
예제 : 축이 2개인 2차원 배열 연산하기
>>> arr = np.array([[1, 2, 3], [4, 5, 6]], dtype='int32')
>>> arr
array([[1, 2, 3],
       [4, 5, 6]])
       
>>> arr.sum(axis=0)
array([5, 7, 9])

>>> arr.sum(axis=1)
array([ 6, 15])
  • 0축은 열, 1축은 행 방향으로 연산을 수행한다.
    • axis=1 은 행 방향으로 더하므로 열 방향으로 더하는 axis=0 보다 빠르게 연산을 처리한다.

 

예제 : 축이 3개인 3차원 배열 연산하기
>>> 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.shape
(3, 2, 4)

>>> arr.sum(axis=0)
array([[24, 27, 30, 33],
       [36, 39, 42, 45]])
       
>>> arr.sum(axis=1)
array([[ 4,  6,  8, 10],
       [20, 22, 24, 26],
       [36, 38, 40, 42]])
       
>>> arr.sum(axis=2)
array([[ 6, 22],
       [38, 54],
       [70, 86]])
  • axis=1가장 안쪽 대괄호를 기준으로 열 방향으로 더한다.
  • axis=0두 번째 대괄호를 기준으로 하여 그 괄호 안에서 요소들을 각각 더한다.
  • arr의 shape (3, 2, 4)는 순서대로 axis 0, axis 1, axis 2가 된다.
    • axis=0을 따라 3개의 요소를 axis=1을 따라 2개의 요소를, axis=2를 따라 4개의 요소를 가진다는 뜻이다.

 

예제 : random() 함수를 활용해 리스트를 생성하는 속도와 넘파이에서 배열을 연산하는 처리하는 속도의 비교

 

  • 파이썬만을 이용하는 ln [8]과 넘파이 배열만 이용하는 ln [9]의 연산 처리 속도에 많은 차이가 있는 것을 알 수 있다.
  • ln [8]은 리스트 a와 b의 요소를 직접 더하는 것이 아니라 결합하는 경우이므로 arr1arr2의 각 요소를 더하는 ln [9] 연산과는 다르다.
  • 그러나 같은 조건 연산인 ln [10] ln [11]의 연산 처리 속도를 비교하면 넘파이 배열 객체의 연산이 빠른 것을 확인할 수 있다.

 

배열 반복

  • 파이썬에서 리스트를 반복하는 것처럼 배열도 반복할 수 있다.
>>> arr = np.array([1, 3, 5, 7])
>>> for i in arr:
...     print(i)
...
1
3
5
7

 

  • 다차원 배열을 반복하면 첫 번째 축에서 배열이 하위 요소를 반환한다.
>>> arr = np.arange(8).reshape(4, 2)
>>> for i in arr:
...     print(i)
...
[0 1]
[2 3]
[4 5]
[6 7]

 

  • 곱셈 연산을 하는 배열은 다음과 같이 반복한다.
>>> arr = np.arange(8).reshape(4, 2)
>>> for (a, b) in arr:
...     print(a*b)
...
0
6
20
42

 

임의의 수 생성

  • random 모듈의 rand() 함수는 주어진 shape에 대해 임의의 값들을 반환한다.

 

>>> np.random.rand(3, 2)
array([[0.74977373, 0.93662644],
       [0.69571843, 0.47650603],
       [0.78964881, 0.84466786]])
  • 입력한 shape 배열을 생성하고, [0, 1) 안에서 일정한 분포의 임의의 값을 생성해 덧붙인다.

 

  • random 모듈의 randint(low, high) 함수는 low를 포함하고 high를 제외하는 임의의 정수들을 반환한다.
  • 반만 열린 범위 [low, high)에서는 특정 dtype의 이산 균등 분포에서 임의의 정수들을 반환한다.
  • 만약 high가 기본값인 None이면 결과는 [0, low)이다.

 

>>> np.random.randint(2, size=10)
array([0, 0, 1, 0, 0, 0, 0, 0, 1, 1])

>>> np.random.randint(5, size=10)
array([3, 4, 4, 3, 2, 0, 0, 0, 1, 2])

 

  • 다음은 0과 4 사이의 정수로 2×4 배열을 생성하는 예제이다.
>>> np.random.randint(5, size=(2, 4))
array([[4, 2, 1, 0],
       [1, 4, 0, 1]])

 

  • random 모듈의 choice() 함수는 주어진 1차원 배열에서 임의의 샘플을 생성한다.
>>> np.random.choice(5, 3)
array([2, 2, 3])
  • size가 3이고 np.arange(5)에서 일정한 임의의 샘플을 생성하는 결과는 np.random.randint(0, 5, 3)과 같다.

 

  • 다음은 size가 3이고 np.arange(5) 범위에서 임의의 샘플을 생성하는 예제이다.
    • 확률을 정의하는 p 인수가 설정되었으므로 1, 4가 생성될 확률은 0이다.
>>> np.random.choice(5, 3, p=[0.1, 0, 0.3, 0.6, 0])
array([3, 3, 3])

 

  • 다음 예제는 size가 10으로 일정한 배열 객체 arr에서 임의의 샘플을 생성한다.
  • 인수 replace가 True이면 복원 추출(Sample with Replacement)을 사용한다.
    • 이는 생성되는 요소 중 반복되는 요소가 있다는 의미이다.
>>> arr = np.arange(12)

>>> np.random.choice(arr, size=10, replace=True)
array([ 7, 10,  2,  1,  1,  3,  9,  3,  0,  9])

>>> np.random.choice(arr, size=10, replace=False)
array([ 7,  5,  1,  4,  2,  8,  9, 10,  6, 11])

 

  • 다음 예제는 재형성(Reshape) 없이 size는 3이고 np.arange(5) 범위에서 일정하지 않은 임의의 샘플을 생성한다.
>>> np.random.choice(5, 3, p=[0.1, 0, 0.3, 0.6, 0])
array([2, 0, 0])

 

  • 다음과 같이 정수 대신 임의의 유사 배열을 반복할 수 있다.
>>> arr_like = ['yakyong', 'jegong', 'manduck', 'haeseok']

>>> np.random.choice(arr_like, 5, p=[0.5, 0.1, 0.1, 0.3])
array(['jegong', 'yakyong', 'yakyong', 'haeseok', 'yakyong'], dtype='<U7')

 

선형 대수

  • 넘파이는 선형 대수(Linear Algebra)를 지원하는 numpy.linalg 모듈을 제공한다.
    • 이 모듈은 선형 대수에서 요구하는 많은 기능을 제공하며, 넘파이 배열선형 대수를 적용하는 수단이다.
    • 또한 인공지능의 머신러닝과 딥러닝에 자주 적용한다.
  • 선형 대수에서 행렬과 벡터곱을 위한 함수
함수의 종류 기능
dot(a, b[, out]) Dot product of two arrays.
linalg.multi_dot(arrays, *[, out]) Compute the dot product of two or more arrays in a single function call, while automatically selecting the fastest evaluation order.
vdot(a, b, /) Return the dot product of two vectors.
inner(a, b, /) Inner product of two arrays.
outer(a, b[, out]) Compute the outer product of two vectors.
matmul(x1, x2, /[, out, casting, order, ...]) Matrix product of two arrays.
tensordot(a, b[, axes]) Compute tensor dot product along specified axes.
einsum(subscripts, *operands[, out, dtype, ...]) Evaluates the Einstein summation convention on the operands.
einsum_path(subscripts, *operands[, optimize]) Evaluates the lowest cost contraction order for an einsum expression by considering the creation of intermediate arrays.
linalg.matrix_power(a, n) Raise a square matrix to the (integer) power n.
kron(a, b) Kronecker product of two arrays.

 

  • 선형 대수의 $ax = b$ 라는 행렬의 벡터 방정식이 있을 때, 벡터 $x$ 를 구할 수 있다.

 

예제 : 3개 변수를 가지는 연립 방정식에서 $x$ 구하기
연립 방정식 행렬
$x_{1} + 2x_{2} + 3x_{3} = -2$
$2x_{1} + x_{3} = 4$
$x_{1} - x_{2} + x_{3} = -1$
$a = \begin{bmatrix} 1 & 2 & 3 \\ 2 & 0 & 1 \\ 1 & -1 & 1 \end{bmatrix}$
$b = \begin{bmatrix} -2 \\ 4 \\ -1 \end{bmatrix}$

 

  • 벡터 $a$ 와 $b$ 를 배열로 생성한 후, 다음과 같이 x를 구할 수 있는데 차례대로 $x_{1}, x_{2}, x_{3}$ 이다.
>>> a = np.array([[1, 2, 3], [2, 0, 1], [1, -1, 1]])
>>> b = np.array([-2, 4, -1])
>>> x = np.linalg.solve(a, b)
>>> x
array([ 3.42857143,  1.57142857, -2.85714286])

 

  • numpy.linalg.solve 선형 매트릭스 방정식이나 선형 스칼라 방정식을 푼다.

 

예제 : 방정식 $x_{1} + 3x_{0} = 9$ 와 $2x_{1} + x_{0} = 8$ 풀기 
>>> a = np.array([[3, 1], [1, 2]])
>>> b = np.array([9, 8])

>>> x = np.linalg.solve(a, b)
>>> x
array([2., 3.])
  • 벡터 [3, 1]과 [1, 2]가 선형 독립적인 선형 매트릭스 방정식 $ax = b$ 의 $x$ 를 구한다.

 

  • 벡터크기방향을 가진다.
  • 내적은 방향을 가진 크기를 곱하며, 비슷한 차원을 가진 벡터 사이에서 작용한다.
    • 이와 반대되는 개념은 외적이라고 하며, 다른 차원을 가진 벡터 사이의 작용을 의미한다.

 

  • 2개의 1차원 배열 $a, b$ 의 내적을 계산하면 $a$ 와 $b$ 벡터 각각의 배열 내부 요소를 곱해서 더한다.
>>> a = np.array([1, 2, 3])
>>> b = np.array([4, 5, 6])
>>> np.dot(a, b)
32

 

  • 2차원 배열 이상의 내적 계산은 배열 arr1의 마지막 축과 배열 arr2의 마지막의 두 번째 축을 곱해서 더하면 된다.
>>> arr1 = np.array([[1, 2], [3, 4]])
>>> arr2 = np.array([[4, 5], [6, 7]])
>>> np.dot(arr1, arr2)
array([[16, 19],
       [36, 43]])
  • arr1의 마지막 축은 0축이고, arr2의 마지막 두 번째 축은 1축이다.

 

  • 선형 대수의 방정식 및 역행렬을 연산하는 함수는 다음과 같다.
함수 종류 기능
linalg.solve(a, b) Solve a linear matrix equation, or system of linear scalar equations.
linalg.tensorsolve(a, b[, axes]) Solve the tensor equation a x = b for x.
linalg.lstsq(a, b[, rcond]) Return the least-squares solution to a linear matrix equation.
linalg.inv(a) Compute the (multiplicative) inverse of a matrix.
linalg.pinv(a[, rcond, hermitian]) Compute the (Moore-Penrose) pseudo-inverse of a matrix.
linalg.tensorinv(a[, ind]) Compute the 'inverse' of an N-dimensional array.
728x90
그리드형(광고전용)

'In-depth Study > NumPy' 카테고리의 다른 글

[NumPy] 넘파이 적용  (0) 2022.04.14
[NumPy] 구조화된 배열  (0) 2022.04.13
[NumPy] 넘파이 배열  (0) 2022.04.13
[NumPy] 넘파이(NumPy) 개요  (0) 2022.04.13
⚠️AdBlock이 감지되었습니다. 원할한 페이지 표시를 위해 AdBlock을 꺼주세요.⚠️
starrykss
starrykss
별의 공부 블로그 🧑🏻‍💻


📖 Contents 📖