Python/Numpy

[Numpy] 넘파이 어레이(배열) 인덱싱, 슬라이싱 총정리

jimmy_AI 2022. 1. 10. 16:09
반응형

파이썬 numpy array 인덱싱과 슬라이싱

파이썬 넘파이 어레이(배열)의 인덱싱, 슬라이싱 과정에 대해서 살펴보고,

몇 가지 중요한 특징과 예제를 정리해보는 시간을 가져보겠습니다.

 

아래의 간단한 2차원 array를 대상으로 예제를 설명하겠습니다.

import numpy as np

a = np.array([[1, 2, 3, 4],
             [5, 6, 7, 8],
             [9, 10, 11, 12],
             [13, 14, 15, 16]])

 

넘파이 어레이 인덱싱

인덱싱은 원소 1개만을 골라 추출하는 과정을 일컫습니다.

 

0번부터 인덱스가 시작함에 주의하며, axis마다 원하는 원소의

좌표를 순서대로 지정해주시면 됩니다.

 

뒤 쪽부터 인덱스를 가져오는 -1, -2, ...의 인덱싱도 리스트처럼 지원합니다.

 

예를 들어, 두 번째 행, 4번째 열의 값을 가져오는 경우,

a[1, 3] 혹은 a[1][3] 처럼 값을 가져올 수 있습니다.

 

두 경우 모두 8의 값을 가져오게 됩니다.

a[1, 3] # 8
a[1][3] # 8

그러나, 실제 사용에서는 a[1, 3]과 같은 인덱싱 방법이 권장됩니다.

수행 시간의 이점이 있는데, 간단한 실험을 해보도록 하겠습니다.

import time

start = time.time()
for i in range(10 ** 7):
    a[1, 3]
print(time.time() - start) # 약 2.2초

start = time.time()
for i in range(10 ** 7):
    a[1][3]
print(time.time() - start) # 약 4.4초

a[1][3] 방식의 접근법은 사실상 인덱싱을 2번하는 과정과 동일하기에,

약 2배의 시간이 소요된 것을 확인할 수 있었습니다.

 

 

넘파이 어레이 전체 슬라이싱

슬라이싱은 여러개의 원소를 동시에 가져오는 과정을 일컫습니다.

 

가장 간단한 경우로 특정 axis(행, 열)의 원소 전체를 추출하는 예제를 보겠습니다.

1개의 행 혹은 1개의 열을 통째로 가져오는 예시입니다.

 

특정 axis 전체를 가져오는 것은 : 혹은 ... 으로 표현이 가능합니다.

a[2] # array([ 9, 10, 11, 12])
a[2, :] # array([ 9, 10, 11, 12])

a[:, 1] # array([ 2,  6, 10, 14])
a[..., 1] # array([ 2,  6, 10, 14])

가장 첫 axis(행)을 대상으로 가져올 때는, a[2] 처럼만 지정해도 무관합니다.

 

그림으로 위 과정을 요약하면 다음과 같습니다.

반응형

어레이 인덱싱과 슬라이싱의 차이 : 배열의 차원

인덱싱과 슬라이싱으로 같은 위치를 가져오는 작업을 수행해도 결과는 다를 수 있습니다.

 

인덱싱과 슬라이싱의 차이는 인덱싱은 차원을 자동으로 낮춰서 변환한 결과를 반환하는데,

반면 슬라이싱은 차원을 유지하면서 결과를 반환하게 됩니다.

 

예를 들어, 2차원 array에서 1개의 축에 인덱싱을 적용하면 결과는 1차원으로 반환되지만,

두 축 모두 슬라이싱을 적용하면 2차원으로 결과가 반환됩니다.

a[2, 1:3] # array([10, 11]) : 1차원
a[2:3, 1:3] # array([[10, 11]]) : 2차원

a[..., -1]
# array([ 4,  8, 12, 16])

a[..., -1:]
'''array([[ 4],
       [ 8],
       [12],
       [16]])'''

a[2, 1:3]과 a[2:3, 1:3]이 가져오는 위치는 동일하나, 반환되는 차원이 다릅니다.

a[..., -1:]도 2차원의 모양을 가지는 열벡터 모양으로 결과가 반환되었습니다.

 

 

numpy array 슬라이싱

1:3 처럼 1~2번 위치를 슬라이싱하거나, :3처럼 처음 ~ 2번 위치,

1: 처럼 1번 위치~끝 부분을 슬라이싱하는 원리는 리스트와 동일합니다.

 

각 axis마다 슬라이싱 결과가 반영되는데, 그림으로 이해하면 다음과 같습니다.

실제 코드로 실행한 결과는 다음과 같습니다.

a[2, 1:3] # array([10, 11])

a[:2, 1:3]
'''
array([[2, 3],
       [6, 7]])'''

 

::2 처럼 2칸씩 건너 뛰면서 슬라이싱 하거나,

1::2 처럼 1번 위치부터 2칸씩 건너 뛰는 방식,

::-1 으로 역순으로 슬라이싱하는 방식 등도 리스트와 마찬가지로 적용 가능합니다.

 

마찬가지로 각 axis마다 해당하는 위치들의 원소를 모아서 반환합니다.

실행 코드의 결과를 살펴보면서 포스팅 마무리를 하겠습니다.

a[::2, 1] # array([ 2, 10])

a[::2, 1::2]
'''array([[ 2,  4],
       [10, 12]])'''

a[::2, ...]
'''array([[ 1,  2,  3,  4],
       [ 9, 10, 11, 12]])'''

a[::2, ::-1]
'''array([[ 4,  3,  2,  1],
       [12, 11, 10,  9]])'''