본문 바로가기
Python/NumPy

NumPy(넘파이)에서의 indexing(인덱싱) - NumPy(6)

by 콩돌 2019. 2. 8.
반응형

참고 자료

https://docs.scipy.org/doc/numpy/user/basics.indexing.html



파이썬 버전 3.7 기준

NumPy 버전 1.15 기준


본 포스팅에서는 NumPy에서의 indexing을 다룬다.



NumPy에서의 indexing


넘파이에서의 인덱싱은 기본적으로 두개의 대괄호를 이용하여 구성된다.

그외에 넘파이의 강력한 점을 느낄 수 있게 해주는 다양한 옵션이 존재한다. 


배정과 참조?

  ○ 배정은 말 그대로 특정 인덱스의 값을 배정시키는 것, 즉 입력을 의미하고

  ○ 참조는 특정 인덱스의 값을 참조하여 불러오는 것, 즉 출력을 의미한다.

  ○ 배정과 참조는 유사하게 사용되어진다.



하나의 요소에 대한 인덱싱

1차원 배열에서 인덱싱

  ○ 1차원 배열에서 요소 하나에 대해서는 파이썬의 시퀀스형 자료형에서 내장자료형을 참조하는 것과 완전히 동일하다.

  ○ 파이썬 내장자료형과 마찬가지로 인덱싱에 음수를 활용할 수 있다. 


사용 예)

# 데이터 입력

In[5]: x = np.arange(10)

In[6]: x

Out[6]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

# 양수로 인덱싱

In[7]: x[1]

Out[7]: 1

# 음수로 인덱싱

In[8]: x[-1]

Out[8]: 9


다차원 배열에서 인덱싱

  ○ 리스트와 튜플과는 다르게 다차원 인덱싱을 지원한다.

    ▷ 다차원 배열에서 한 개의 요소를 참조하기 위한 인덱싱을 할 경우 여러 개의 대괄호를 사용하지 않아도 된다. 

    ▷ 리스트 튜플과 유사하게 여러 개의 대괄호를 사용해도 같은 참조를 수행하긴한다.

      - 상기 방법은 불러온 배열에서 또 불러오는 방식으로 진행되므로 비효율적이다.

  ○ 다차원 배열에서 하나의 인덱스를 입력할 경우 그에 따르는 서브 다차원 행렬을 참조한다. 


사용 예)

# 데이터 입력

In[9]: x = np.arange(20).reshape(5,4)

In[10]: x

Out[10]: 

array([[ 0,  1,  2,  3],

       [ 4,  5,  6,  7],

       [ 8,  9, 10, 11],

       [12, 13, 14, 15],

       [16, 17, 18, 19]])


# 2차원 인덱싱

In[11]: x[2,2]

Out[11]: 10


# 2개의 대괄호를 사용한 인덱싱, 비효율적

In[12]: x[2][2]

Out[12]: 10


※ 참고사항 

  NumPy는 C-order 인덱싱을 사용한다. 첫번째 인덱스가 메모리에서 가장 신속하게 바뀔 수 있는 위치인 곳에서, 마지막 인덱스는 포트란과 IDL과는 다르게 일반적으로 가장 신속하게 바뀔 수 있는 위치를 나타낸다.



여러 요소에 대한 인덱싱(슬라이스, 인덱스 배열)

슬라이스 활용 법

  ○ 넘파이에서는 파이썬의 내장자료형과 거의 유사하게 슬라이스 기능 등을 활용하여 여러 요소에 대해 인덱싱이 가능하다. 

    ▷ 사용하는 방법은 파이썬 내장자료형인 리스트와 튜플과 사실상 거의 유사하다.


사용 예)

# 데이터 입력

In[13]: x = np.arange(20).reshape(5,4)

In[14]: x

Out[14]: 

array([[ 0,  1,  2,  3],

       [ 4,  5,  6,  7],

       [ 8,  9, 10, 11],

       [12, 13, 14, 15],

       [16, 17, 18, 19]])


# 콜론 슬라이싱

In[15]: x[1:3,1:4]

Out[15]: 

array([[ 5,  6,  7],

       [ 9, 10, 11]])


# 슬라이스 함수 사용

In[16]: x[slice(1,4),1:4]

Out[16]: 

array([[ 5,  6,  7],

       [ 9, 10, 11],

       [13, 14, 15]])


※ 참고사항 

  슬라이스를 이용하여 배열을 작성할 경우 원본 배열을 카피하는 것이 아니라 새로운 배열을 작성한다. 


1차원 배열에서 인덱스 배열을 활용하는 방법

  ○ 넘파이에서 배열은 다른 배열을 통해 인덱싱이 될 수 있다.
    ▷ 넘파이의 배열객체 말고도 배열로 치환될 수 있는 시퀀스형 자료형(리스트 등)도 활용될 수 있다.
  ○ 인덱스 배열을 사용할 수 있는 범위는 굉장히 단순한 경우부터 복잡하고 이해하기 어려운 범위까지 포함한다.
  ○ 슬라이스와 다르게 인덱스 배열을 활용할 경우 복사본이 생성된다.
  ○ 인덱스 배열은 반드시 정수형이어야 한다. 
  ○ 일반적으로 인덱스 배열을 통해 반환되는 배열은 인덱스 배열과 동일한 길이를 가지나, 데이터의 자료형과 값은 실제 배열의 데이터를 가져온다. 
  ○ 다차원 배열에서도 적용이 가능하다.

사용 예)

# 데이터 입력 

In[28]: x = np.arange(20)

In[29]: x

Out[29]: 

array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,

       17, 18, 19])


# 배열을 이용한 인덱싱

In[30]: x[np.array([0,3])]

Out[30]: array([0, 3])


다차원 배열에서 인덱스 배열을 활용하는 방법

  ○ 넘파이에서는 1차원 뿐만이 아니라 다차원에대해서도 인덱스 배열을 활용할 수 있다. 
  ○ 인덱스 배열로 인덱싱되어 반환되는 배열은 인덱스 배열과 동일한 사이즈를 가진다.
  ○ 인덱스 배열들은 같은 모양을 가져야 한다.
    ▷ 만약 같지 않다면, 같은 모양으로 만들어주기 위해 브로드캐스팅을 시도하게 되는데, 브로드캐스팅이 되지 않는 경우 에러가 발생한다.
  ○ 인덱스 배열의 값은 좌표라고 생각하면 이해하기 편하다.

사용 예)

# 데이터 입력

In[22]: x = np.arange(20).reshape(5,4)

In[23]: x

Out[23]: 

array([[ 0,  1,  2,  3],

       [ 4,  5,  6,  7],

       [ 8,  9, 10, 11],

       [12, 13, 14, 15],

       [16, 17, 18, 19]])


# 2개의 배열을 이용한 인덱싱 

In[24]: x[np.array([0,3]), np.array([0,3])]

Out[24]: array([ 0, 15])


# 브로드케스팅 예

In[25]: x[np.array([0,3]), np.array([0])]

Out[25]: array([ 0, 12])


불린(Boolean) 혹은 마스크 인덱스 배열

  ○ 인덱스로서 사용되는 불린 배열은 인덱스 배열과는 다른 방식으로 인덱스를 다룬다.

  ○ 불린배열은 배열의 초기 차원과 같은 차원이어야 한다. 

    ▷ 불린배열보다 인덱스되어지는 배열이 고차원일 경우 불린배열의 좌표에 해당되는 하위 배열을 반환한다. 

  ○ 불린배열에서 True 값을 가지는 요소와 같은 위치를 가지는 요소들을 반환한다.

  ○ 인덱스 배열과는 다르게 결과가 1차원 배열으로 반환된다. 

  ○ 슬라이스와 다르게 인덱스 배열을 활용할 경우 복사본이 생성된다.


사용 예)

# 데이터 입력, 일반데이터와 마스크 배열

In[34]: x = np.arange(20).reshape(5,4)

In[35]: y = (x%2) == 0

In[36]: x

Out[36]: 

array([[ 0,  1,  2,  3],

       [ 4,  5,  6,  7],

       [ 8,  9, 10, 11],

       [12, 13, 14, 15],

       [16, 17, 18, 19]])

In[37]: y

Out[37]: 

array([[ True, False,  True, False],

       [ True, False,  True, False],

       [ True, False,  True, False],

       [ True, False,  True, False],

       [ True, False,  True, False]])


# 마스크 배열을 이용한 출력

In[38]: x[y]

Out[38]: array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])


# 마스크 배열을 이용한 서브 배열 출력

In[39]: z = np.array([True, True, False, False, False])

In[40]: x[z]

Out[40]: 

array([[0, 1, 2, 3],

       [4, 5, 6, 7]])


  ○ x[np.nonzero(y)]와 사실상 같은 결과를 보여준다. 


인덱스 배열과 슬라이스의 조합

  ○ 인덱스 배열은 슬라이스와 조합되어 사용되어질 수 있다. 

  ○ 이 경우 슬라이스는 인덱스 배열과 같은 형태로 변경된다.

  ○ 불린 인덱스와 조합되는 것 역시 가능하다.


사용 예)

# 데이터 입력

In[54]: x = np.arange(20).reshape(5,4)


# 인덱스 배열과 슬라이스의 조합

In[55]: x[np.array([0,3,4]), 0:3]

Out[55]: 

array([[ 0,  1,  2],

       [12, 13, 14],

       [16, 17, 18]])

In[56]: x[np.array([0,3,4]), 0:2]

Out[56]: 

array([[ 0,  1],

       [12, 13],

       [16, 17]])


# 인덱스 배열과 마스크 배열의 조합

In[57]: z = np.array([True, True, False, False, False])
In[58]: x[z, 0:2]
Out[58]: 
array([[0, 1],
       [4, 5]])



구조적인 인덱싱 툴 numpy.newaxis과 생략 문법

  ○ numpy.shape은 배열의 형태를 출력해주는 함수이다.

  ○ numpy.newaxis는 새로운 차원의 축을 만들어주기 위해 numpy에서 제공되는 툴이다. 

    ▷ 인덱스를 입력하는 자리인 대괄호에 이 함수를 삽입하면, 1의 크기를 가지는 축을 생성시켜준다.

    ▷ 요소없이 단순히 차원만 늘려주는 역할을 한다. 

    ▷ 두 개의 배열을 조합하여 

  ○ 생락문법은 명시하지 않은 차원을 선택하기 위해서 사용되어진다. 

    ▷ 생략 대신 Ellipsis 오브젝트를 이용해도 동일한 결과가 얻어진다.


사용 예)

# shape 메서드를 이용한 형상 출력과 np.newaxis를 활용한 차원축의 형성 

In[59]: x = np.arange(20).reshape(5,4)

In[60]: x.shape

Out[60]: (5, 4)

In[61]: x[:,np.newaxis,:].shape

Out[61]: (5, 1, 4)


# 차원축의 형성 예

In[62]: x = np.arange(5)

In[63]: x[:,np.newaxis]

Out[63]: 

array([[0],

       [1],

       [2],

       [3],

       [4]])

In[64]: x[np.newaxis,:]


# 차원축을 활용한 배열의 생성

Out[64]: array([[0, 1, 2, 3, 4]])

In[65]: x[:,np.newaxis]+x[np.newaxis,:]

Out[65]: 

array([[0, 1, 2, 3, 4],

       [1, 2, 3, 4, 5],

       [2, 3, 4, 5, 6],

       [3, 4, 5, 6, 7],

       [4, 5, 6, 7, 8]])


# 공백 문법과 Ellipsis 예제

In[66]: x = np.arange(27).reshape(3,3,3)

In[67]: x

Out[67]: 

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],

        [24, 25, 26]]])

In[68]: x[0,...,1]

Out[68]: array([1, 4, 7])

In[69]: x[0,Ellipsis,1]

Out[69]: array([1, 4, 7])

In[70]: x[0,:,1]

Out[70]: array([1, 4, 7])



배열의 인덱스를 활용한 값의 배정

  ○ 다차원 배열에서는 특정 인덱스에 배정된 서브인덱스를 반환받을 수 있다.

    ▷ 정수, 슬라이스, 인덱스 혹은 마스크 배열이 이용가능하다. 

  ○ 만약 상위 자료형(정수보다 실수형 자료형이 더 상위임)을 배정 받는다면 문제가 발생할 수 도 있다.

    ▷ 예를들어, 정수형 배열에 실수형을 배정하고자 할 경우 소수점은 버림되어 정수형이 압력된다.

    ▷ 실수형 배열에 복소수형을 배정을 시도할 경우에는 예외가 발생한다.

  ○ 배정은 참조와는 다르게 항상 원본 데이터를 만들어 낸다는 특징이 있다.

    ▷ 몇몇 동작은 단순한 예상처럼 작동하지 않는 경우도 있다.


사용 예)

# 데이터 입력

In[12]: x = np.arange(5)

In[13]: x

Out[13]: array([0, 1, 2, 3, 4])


# 인덱싱을 이용한 배정

In[14]: x[0] = 1

In[15]: x

Out[15]: array([1, 1, 2, 3, 4])


# 슬라이싱을 이용한 배정

In[16]: x[1:3] += 1

In[17]: x

Out[17]: array([1, 2, 3, 3, 4])


# 인덱싱을 이용한 배정과 상위 자료형 배정시 예제

In[18]: x[3] = 4.5

In[19]: x

Out[19]: array([1, 2, 3, 4, 4])


# 인덱스 배열을 이용한 배정 예제

In[20]: x[np.array([4,4,4,0,1])] += 1

In[21]: x

Out[21]: array([2, 3, 3, 4, 5])


※ 위의 마지막 예에서 인덱스 1이 1개가 잡혀있기 때문에 1위치에 있는 10에 1을 3번 더해서 13이 나올 것 같지만 실제로는 10에 1을 한번 더해서 11이 된다. 그 이유로는 넘파이에서 위와 같은 상황이 되었을때에는 먼저 1의 위치에 10을 불러와 10에 1을 더한뒤 '임시적'으로 저장을 한다. 그리고 그뒤에 다시 1을 불러올때 '원래의 값'인 10을 다시불러와 1을 더하므로 다시 11를 임시적으로 다시 저장을 한다. 이와같은 방식을 반복하게 되므로 사실상 1위치에 10에는 1만 더해지게 된다.

 


튜플을 이용하여 프로그램 내에서 인덱스 변수 다루기

  ○ 위의 인덱스 문법은 매우 강력하지만, 인덱스 변수를 사용하기에는 제한적인 면이 있다.
  ○ 튜플은 인덱스로 실행되어 질 수 있다.

    ▷ 즉, 사용자가 원하는 변수를 추리는 함수를 작성하고, 이 함수로 튜플을 만들어 인덱싱 하는 것이 가능하다.

    ▷ 튜플은 각 차원의 좌표로 입력받게 된다.

      - 리스트는 인덱스 배열과 동일하게 입력이 된다.

  ○ 파이썬 내장 함수중 하나인 slice() 역시 적용하는 것이 가능하다. 

  ○ 생략문법 혹은 Ellipsis 오브젝트 역시 사용되는 것이 가능하다. 


사용 예)

# 데이터 입력

In[22]: x = np.arange(25).reshape(5,5)

In[23]: x

Out[23]: 

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, 24]])


#  인덱스 튜플 이용

In[24]: y = (1,1)

In[25]: x[y]

Out[25]: 6


# 튜플에 슬라이스의 조합 

In[26]: y = (1,slice(1,3))

In[27]: x[y]

Out[27]: array([6, 7])


# 튜플에 생략문법 적용

In[28]: x = np.arange(125).reshape(5,5,5)

In[29]: x[(1,...,1)]

Out[29]: array([26, 31, 36, 41, 46])


  ○ 사용자가 새롭게 인덱싱을 하고자 하는 범위를 입력한 배열을 통해 인덱싱을 하는 것 또한 가능하다.

  ○ 불린 배열을 이용하여 필요한 데이터만 필터링 하는 것 역시 가능하다.

반응형

댓글