[Python] Numpy 라이브러리 (feat. indexing, slicing)

    Numpy

    Numpy (Numerical Python)란?

    Numpy 는 행렬이나 대규모 다차원 배열을 쉽게 처리할 수 있도록 지원하는 파이썬의 라이브러리입니다.
    데이터 구조 외에도 수치 계산을 위해 효율적으로 구현된 기능을 제공해요.

    파이썬에는 행렬을 표현하는 기본적인 자료형이 존재하지 않아요.
    그래서 벡터 행렬 계산을 효율적으로 처리하기 위해 Numpy 라이브러리를 이용하죠.
    Numpy 라이브러리는 Numeric 라이브러리와 NumArray 라이브러리가 합쳐져 높은 수준의 다차원 배열 계산을 고속으로, 효율적으로 처리할 수 있어요.
    또한 위에서 언급한 것처럼 N차원 배열 객체를 이용한 선형 대수학이나 푸리에 변환 등의 수치계산 기능을 지원해요.

    그렇다면 AI 와는 어떤 관련이 있을까요?
    AI 에서 말하는 데이터(이미지, 사운드 등)는 대부분 숫자 배열로 볼 수 있어요.
    Numpy 는 이를 반복문 없이 배열 처리할 수 있고, 파이썬 리스트에 비해 빠른 연산과 효율적인 메모리 사용을 지원해요.

    출처: elice

    Numpy 사용하기

    파이썬 배열 중에는 list 가 있죠.
    이전 글에서 다뤘지만, Numpy 와 비교하기 위해 list 를 다시 살펴볼게요.

    비교를 위해 몇가지 특징들을 짚어 볼게요.
    - 요소를 ',(comma)' 로 구분한다.
    - type 은 'list' 이다.
    list_arr = list(range(5))
    
    print(list_arr)         # [0, 1, 2, 3, 4]
    print(type(list_arr)) 	# <class 'list'>​

     

    이제 Numpy 로 돌아올게요.
    Numpy 를 사용하려면 import 키워드를 사용해야 합니다.
    아래처럼 'as np'를 이용하면 별칭으로 np 를 사용할 수 있어요. (관습적으로 numpy 는 np 를 별칭으로 사용합니다.)

    import numpy as np
    이제 Numpy 를 사용해서 배열을 만들고 list 와 비교할게요.
    import numpy as np
    
    np_arr = np.array(range(5))
    
    print(np_arr)       # [0 1 2 3 4]
    print(type(np_arr)  # <class 'numpy.ndarray'>​

    이제 특징을 살펴보죠.
    - 선언할 때는 .array()라는 numpy 에 선언된 함수를 사용해요.
    - 요소를 공백으로 구분한다.
    - type 은 'numpy.ndarray'이다. (ndarray = n-dimensional array, n차원 배열)

    list 와 numpy 의 차이 확인되시나요?

     


    배열의 기초

    dtype: 배열의 데이터 type

    Numpy 의 array 와 파이썬의 list 는 몇가지 차이가 더 있습니다.
    array 는 list 와 다르게 같은 데이터 타입만 저장할 수 있어요.
    코드를 통해 살펴 볼게요.

    arr = np.array([0, 1, 2, 3, 4], dtype=float)
    
    print(arr)               # [0. 1. 2. 3. 4.]
    print(arr.dtype)         # 'float64'
    print(arr.astype(int))   # [0 1 2 3 4]

    arr 이라는 array 의 요소를 dtype 을 통해 float (실수형) 로 만들었어요.
    마지막에는 'astype(int)'라는 함수를 통해 요소를 정수형으로 출력했네요.

    dtype 에는 아래 표처럼 4가지 옵션이 있어요.

    출처: elice

    ndarray 의 속성

    차원관련 속성

    ndarray 의 차원에 관련된 2가지 속성을 알아볼게요.

    • .ndim: 몇차원 인지 구하는 함수 (column 과 달라요!)
    • .shape: 모양에 대한 함수

    예시를 볼게요. 주석까지 보면 감이 오죠!

    import numpy as np
    
    list_1 = [0, 1, 2, 3]
    list_2 = [[0, 1, 2], [3, 4, 5]]
    
    arr_1 = np.array(list_1)
    arr_2 = np.array(list_2)
    
    print(arr_1.ndim)     # 1 차원
    print(arr_2.ndim)     # 2 차원
    
    print(arr_1.shape)    # (4,)  4개의 column이 1개의 row 로 존재
    print(arr_2.shape)    # (2,3) 3개의 colunm이 2개의 row 로 존재

    크기 속성과 shape 조절

    ndarray 의 크기를 확인할 수 있는 두가지 속성이 있어요.
    .len() 에 주의하면서 확인해보죠.

    • .size: 배열안에 있는 요소의 수
    • .len()
      • 1차원 (1행) array 의 경우 요소의 수
      • 다차원 array 의 경우 row 의 수

    예시를 볼까요?

    import numpy as np
    
    arr = np.array([0, 1, 2, 3, 4, 5, 6])
    
    print("arr.shape: {}".format(arr.shape))       # arr.shape: (6,)
    print("배열 요소의 수: {}".format(arr.size))   # 배열 요소의 수: 6
    print("배열의 길이: {}".format(len(arr)))      # 배열의 길이: 6

    이해가 되셨다면 shape 함수를 통해 ndarray 의 shape 을 바꾸는 것도 확인해보죠.
    (6,) 의 array 를 (3,2) 로 바꿀 건데요.
    요소의 수는 변함이 없으니 .size는 그대로, 다차원이 되었으니 len() 는 row 의 수를 출력하겠군요.

    import numpy as np
    
    arr = ap.array([0, 1, 2, 3, 4, 5])
    arr.shape = 3, 2
    
    print("arr.shape: {}".format(arr.shape))       # arr.shape: (3,2)
    print("배열 요소의 수: {}".format(arr.size))   # 배열 요소의 수: 6
    print("배열의 길이: {}".format(len(arr)))      # 배열의 길이: 3

    Indexing & Slicing

    찾고 잘라내자

    인덱싱과 슬라이싱은 "시퀀스 자료형" 을 다룬 글에서 이미 소개했죠.
    numpy 의 array 에도 같은 방식을 적용할 수 있어요.

    Indexing

    인덱싱은 인덱스로 그 값을 찾아낸는 방식이죠.
    먼저 1차원 배열 예시를 볼게요.

    import numpy as np
    
    x = np.arange(7)
    
    print(x)     # [0 1 2 3 4 5 6]
    print(x[0])  # 0
    print(x[7])  # IndexError: index 7 is out of bounds
    
    x[0] = 10
    print(x)     # [10 1 2 3 4 5 6]

    이번에는 다차원 배열에 인덱싱을 이용해볼게요.
    참고로 .arange 함수의 매개변수는 (시작(포함), 끝(불포함), step) 이렇게 3가지가 있습니다.

    import numpy as np
    
    x = np.arange(1, 13, 1)   # 1 부터 12를 1 간격으로 포함하는 ndarray 생성
    x.shape = 3, 4            # 3행, 4열의 shape 으로 변형
    print(x)                  # [[ 1  2  3  4]
                              #  [ 5  6  7  8]
                              #  [ 9 10 11 12]]
    print(x[2,3])             # 12

    sclicing

    슬라이싱은 인텍스의 값으로 배열의 일부분을 가져오는 방식입니다.
    이번에도 1차원 배열 예시를 먼저 보죠.

    import numpy as np
    
    x = np.arange(7)
    print(x)            # [0 1 2 3 4 5 6]
    
    print(x[1:4])       # [1 2 3]
    print(x[1:])        # [1 2 3 4 5 6]
    print(x[:4])        # [0 1 2 3]
    print(x[::2])       # [0 2 4 6]

    이번엔 다차원 배열을 슬라이싱 해볼게요.

    import numpy as np
    
    x = np.arange(1, 13, 1)   # 1 부터 12를 1 간격으로 포함하는 ndarray 생성
    x.shape = 3, 4            # 3행, 4열의 shape 으로 변형
    print(x)                  # [[ 1  2  3  4]
                              #  [ 5  6  7  8]
                              #  [ 9 10 11 12]]
    
    print(x[1:2,:2:3])        # [[5]]
    print(x[1:, :2])          # [[5 6 ]
                              #  [9 10]]

    Boolean Indexing

    Boolean indexing 은 배열의 각 요소의 선택 여부를 Boolean mask 를 이용해 지정하는 방식입니다.
    Boolean 은 논리학에서 참/거짓을 나타내는데 사용합니다.
    Boolean mask 는 True, False 로 구성된 mask array 를 의미합니다.

    즉, Boolean indexing 은 전체 요소 중 조건에 맞는 데이터는 참, 맞지 않은 데이터는 거짓으로 판단해 Boolean mask 를 만들어준다고 생각하면 됩니다.

    import numpy as np
    
    x = np.aranage(7)
    
    print(x)          # [0 1 2 3 4 5 6]
    print(x < 3)      # [True True True False False False False]
    print(x > 7)      # [False False False False False False False]

    또한 Boolean mask 를 이용해 True 요소에 해당하는 index 를 조회할 수도 있어요.

    import numpy as np
    
    x = np.aranage(7)
    
    print(x)             # [0 1 2 3 4 5 6]
    print(x[x < 3])      # [0 1 2]
    print(x[x %2 == 0])  # [0 2 4 6]

    Fancy Indexing

    Index 배열을 이용해서 배열의 각 요소 선택하는 방식입니다.
    아래 예시의 두번째 print 문을 보면 index 배열을 사용했습니다.

    import numpy as np
    
    x = np.arange(7)
    
    print(x)             # [0 1 2 3 4 5 6]
    print(x[1, 3, 5])    # [1 3 5]

    다차원 배열에서 확인해볼까요?
    print 문을 비교하면 이해에 도움이 될 거에요.
    하나는 다차원 배열의 indexing 출력이고, 아래 두개는 다차원 배열의 indexing 배열 출력입니다.

    import numpy as np
    
    x = np.arange(1, 13, 1).reshape(3,4)    # x.shape=3,4 를 한번에 설정
    print(x)                                # [[ 1  2  3  4]
                                            #  [ 5  6  7  8]
                                            #  [ 9 10 11 12]]
                                            
    print(x[0,2])                           # 3
    print(x[[0,2]])                         # [[ 1 2 3 4]
                                            #  [ 9 10 11 12]]
    print(x[[0,2],[1,3]])                   # [ 2 12]

    풀어서 설명해볼게요.
    첫 print 문은 x[0,2] 로 0행 2열의 값을 출력하라는 의미죠.
    두번째 print 문은 x[[0,2]] 로 0행과 2행을 출력하라는 의미가 됩니다.
    세번째 print 문은 0행 1열의 값과 2행 3열의 값을 출력하라는 의미네요.

    마무리 예시

    마지막으로 indexing 과 slicing 을 조합해서 사용한 코드를 볼면서 정리할게요.

    import numpy as np
    
    x = np.arange(1, 13, 1).reshape(3,4)    # x.shape=3,4 를 한번에 설정
    print(x)                                # [[ 1  2  3  4]
                                            #  [ 5  6  7  8]
                                            #  [ 9 10 11 12]]
    
    print(x[1,2])                           # 7 - 요소 조회
    print(x[1:2, 2])                        # [7] - 슬라이싱 (배열 조회)
    
    
    print(x[[0,2]])                         # [[ 1 2 3 4]
                                            #  [ 9 10 11 12]] - 0행, 2행의 모든 열 조회
    print(x[[0,2], 2])                      # [ 3 11] - 0행, 2행의 2열만 조회
    print(x[[0,2], :2])                     # [[ 1 2]
                                            #  [ 9 10]] - 0행, 2행중 1열까지 슬라이싱

    여기까지 "Numpy"에 대해 알아봤어요.
    글이 도움이 되셨다면 공감 버튼 눌러주세요. 😊

    댓글

    Designed by JB FACTORY