1_4_ndarray_Indexing_and_Slicing

NumPy Master Class

Chapter1 ndarray

Notebook4 ndarray Indexing and Slicing

이번 Notebook에서는 ndarray에 대한 Indexing과 Slicing을 다뤄보도록 한다.

그리고 그 뒤에 Python list의 Indexing과 Slicing과는 무엇이 다른지 살펴보도록 하자.

In [2]:
import numpy as np

다음은 Python list에 대한 indexing과 slicing이다.

In [6]:
python_list = [1, 2, 3, 4, 5]
print(python_list[3]) # Python list indexing
print(python_list[2:5]) # Python list slicing
4
[3, 4, 5]

ndarray를 설계한 사람들도 당연히 이런 indexing과 slicing이 동일하게 작동하도록 만들어줬다.

In [7]:
test_np = np.array([1, 2, 3, 4, 5])
print(test_np[3]) # ndarray indexing
print(test_np[2:5]) # ndarray slicing
4
[3 4 5]

그러면 2차원 이상의 array에서는 어떻게 indexing과 slicing을 할까?

먼저 python list를 이용한 matrix의 indexing과 slicing을 살펴보자

In [16]:
python_matrix = [[1, 2], [3, 4], [5, 6], [7, 8]]
print(python_matrix)

print(python_matrix[0])
print(python_matrix[1])
print(python_matrix[0:3])
[[1, 2], [3, 4], [5, 6], [7, 8]]
[1, 2]
[3, 4]
[[1, 2], [3, 4], [5, 6]]

위와 같이 python list에 대한 indexing, slicing을 할 때, python list는 각각의 위치에 있는 원소값들 돌려준다.

이때 주의할 점은 우리가 matrix를 다룰 때 생각하는 (m,n)-th entry에 접근하는 방법과는 거리가 있다는 점이다.

무슨 말이냐면, 우리가 python_matrix[0]을 뽑아내면 이 첫 번째 원소는 list이다. 그리고 이 1차원 list의 입장에서 다시 indexing, slicing이 가능해지는 것이지 처음 matrix의 입장에서 (m,n)의 원소에 접근하는 방법은 사용할 수 없다는 것이다. 예를 들어 우리가 (1,1) 원소에 접근하고 싶다면 python_matrix[1,1]를 사용하려고 할 수 있다. 그러면

In [17]:
python_matrix = [[1, 2], [3, 4], [5, 6], [7, 8]]
print(python_matrix)

print(python_matrix[1,1])
[[1, 2], [3, 4], [5, 6], [7, 8]]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-17-287e0d5713f9> in <module>
      2 print(python_matrix)
      3 
----> 4 print(python_matrix[1,1])

TypeError: list indices must be integers or slices, not tuple

위와 같이 오류가 뜨는 것을 볼 수 있다.

이는 python list는 말그대로 list일 뿐이지 matrix나 tensor를 다루는 방법이 아닌 것이다.

따라서 python list를 이용하여 matrix를 만들었을 때 각 원소에 접근하는 방법은

In [19]:
python_matrix = [[1, 2], [3, 4], [5, 6], [7, 8]]
print(python_matrix)

print(python_matrix[1]) # 두 번째 원소에 접근하고 이 원소는 다시 python list를 가짐
print(python_matrix[1][1]) # 이 python list [3,4]에 대해 다시 한 번 indexing을 해줘야 함
[[1, 2], [3, 4], [5, 6], [7, 8]]
[3, 4]
4

위와 같이 list의 차원을 낮춰가며 indexing, slicing을 해줘야 하는 것이다.

그러면 ndarray는 어떨까?

먼저 matrix에 대해 row, column 중 rows에 대한 indexing, slicing을 해보자

In [28]:
python_matrix = [[1, 2], [3, 4], [5, 6], [7, 8]]
nd_matrix = np.array(python_matrix)

print(nd_matrix, '\n')
print("nd_matrix[0]:", nd_matrix[0])
print("nd_matrix[1]:", nd_matrix[1])
print("nd_matrix[0:3]:\n", nd_matrix[0:3])
[[1 2]
 [3 4]
 [5 6]
 [7 8]] 

nd_matrix[0]: [1 2]
nd_matrix[1]: [3 4]
nd_matrix[0:3]:
 [[1 2]
 [3 4]
 [5 6]]

위와 마찬가지로 아직까진 python list와 동일한 작용을 하는 것을 확인할 수 있다.

그러면 각 원소에 접근 하는 방법을 살펴보면

In [34]:
python_matrix = [[1, 2], [3, 4], [5, 6], [7, 8]]
nd_matrix = np.array(python_matrix)
                     
print("nd_matrix[0,0]:", nd_matrix[0,0])
print("nd_matrix[3,1]:", nd_matrix[3,1])
nd_matrix[0,0]: 1
nd_matrix[3,1]: 8

위와 같이 우리가 수학적으로 다루는 (m,n)-th entry에 접근하는 방법이 가능하다.

또한 ndarray는 말그대로 vecot, matrix, tensor에 최적화 되있는 data type이므로 다음과 같은 indexing, slicing이 가능하다.

In [37]:
python_matrix = [[1, 2], [3, 4], [5, 6], [7, 8]]
nd_matrix = np.array(python_matrix)
                 
print("nd_matrx \n", nd_matrix, '\n')
print("nd_matrix[0,0]:", nd_matrix[:,0])
print("nd_matrix[3,1]:", nd_matrix[:,1])
nd_matrx 
 [[1 2]
 [3 4]
 [5 6]
 [7 8]] 

nd_matrix[0,0]: [1 3 5 7]
nd_matrix[3,1]: [2 4 6 8]

즉, 첫 번째 column과 두 번째 column을 뽑아내는 방법도 python list에서 사용하는 indexing, slicing을 이용하여 구할 수 있게 된다.

이처럼 python list보다 ndarray는 우리가 수학적으로 data를 다루는 방법에 더 친화적이며 이를 통해 불필요한 코딩들을 할 필요가 없어지는 장점이 있다.

예를 들어, 다음과 같이 H=1080, W=1920, C=3(RGB channel)의 모양을 가지는 ndarray가 있다고 가정해보자.

In [41]:
img = np.random.randint(low = 0, high = 255, size = (1080, 1920, 3))
print(img.shape)
(1080, 1920, 3)

이때 R,G,B channel에 해당하는 값들을 다음과 같이 추출할 수 있게 된다.

In [42]:
R_channel = img[:,:,0]
G_channel = img[:,:,1]
B_channel = img[:,:,2]

print("R_channel.shape:", R_channel.shape)
print("G_channel.shape:", G_channel.shape)
print("B_channel.shape:", B_channel.shape)
R_channel.shape: (1080, 1920)
G_channel.shape: (1080, 1920)
B_channel.shape: (1080, 1920)

+ Recent posts