Home python NumPy 인덱싱과 슬라이싱
Post
Cancel

python NumPy 인덱싱과 슬라이싱

numpy logo

이번 포스팅에선 NumPy의 인덱싱(indexing)과 슬라이싱(slicing)에 대해 알아보자.

Numpy는 머신러닝, 딥러닝의 기본적인 자료구조가 되는 친구라고 소개를 했는데, 오늘은 응용연산에 대해 두개의 포스팅으로 나누어 소개하고자 한다.

numpyndarray는 리스트와 굉장히 유사한 면이 많다는 것을 대부분의 사람들이 알고 있으리라 생각한다.

**!!!!기본적인 indexing과 slicing 또한 python의 list와 거의 같다고 볼 수 있다. (하지만 본질적으로 다른 기능이 있음)

우선 살펴보자!

기본 인덱싱과 슬라이싱

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
27
import numpy as np    # numpy 모듈을 np로 alias 해서 불러주자

arr = np.arange(10, 20, 1)   # ndarray를 만들건데 1씩 증가하는 10부터 시작해서 
                             # 20미만의 수를 담은 array를 만들것임
print(arr)
# 출력물: [10 11 12 13 14 15 16 17 18 19]


# 자 이제 slicing을 해보자.

# 난 array의 0번째 1번째 2번째 세 개의 요소 값을 알고 싶어!
tmp = arr[0:3]
print(tmp)   # ndarray의 특징중 하나는 원본에 대한 slicing 데이터 값이 같음. 즉 ndarray임
# 출력물: [10 11 12]

# 그렇다면 arr[0] = 100 으로 바꾸면 어떻게 될까?
arr[0] = 100
# arr:    [100 11 12 13 14 15 16 17 18 19]  이런 값을 갖게 됨

# arr을 보여주고 있는 tmp의 값은 어떻게 변할까?
# tmp:   [100 11 12]  이렇게 변해있다.

# 정리하자면 
# slicing 하면 view가 된다. 복사본만 따로 남는게 아님. view로 됨.
# python list 는 이게 없음. 근데 numpy는 있음. 
# view 를 이해하지 못하면 나중에 data handling 할 때 문제가 된다.
# numpy의 ndarray에 대해서 slicing을 한다면 view가 만들어짐!!!

음… 뭐 마지막에 “slicing 이 view 가 되는데 주저리주저리~” 이 부분만 제외하고 본다면 크게 문제가 없다.

그래? 그러면 indexing은 어떻게 되는거지?

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import numpy as np

arr = np.arange(0, 5, 1)   # 0부터 4까지 (5는 미포함) 1씩 증가하는 ndarray를 만들어보자
print(arr)
# 출력물: [0 1 2 3 4]

print(arr[2])
# 출력물: 2   

# 여기까지도 크게 다른 부분이 없는것 같다. 
# 조금 더 상세하게 들어가보자

print(arr[0: -1])  # 0부터 인덱스 끝까지 slicing을 해보자! 
                   # 이렇게 slicing 을 하면 제일 끝 인덱스는 미포함임
# 출력물: [0 1 2 3]

# 여기까지도 파이썬과 똑같다.
# 음... 뭐가 다르다는거지?
# 왜 굳이 ndarray를 써야하는것인가?

# ndarray는 고차원일 때 파이썬의 list와 큰 차이가 난다.
# 2차원을 살펴보자

arr = np.array([[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9], 
                [10, 11, 12]])     

# 이렇게 구분짓지 않고 oneline으로 작성해도 되나, 
# 2차원임을 강조하는 차원에서 arr을 만들었다.

# 자 출력해보자.
print(arr)
# [[ 1  2  3]
#  [ 4  5  6]
#  [ 7  8  9]
#  [10 11 12]]       4x3 매트릭스로 이쁘게 출력되었다. 

# 여기서 5라는 값을 가져오고 싶으면 어떻게 인덱싱을 하면 될까?

print(arr[1, 1])     # (0행부터 시작함을 인지하자)
                     # arr의 1행에서 1번째 열을 이렇게 표기하면 된다! 
# 출력물: 5
print(arr[1][1])   # 이렇게도 표기 가능
#출력물: 5

# 그러면 slicing도 같이 써볼까?

print(arr[2, :])     # 이건 뭘까?
                     # 파이썬 arr의 2번째 행으로가 => [7 8 9] 
                     # 그리고 칼럼(열) 전체를 출력해! => [7 8 9]
# 출력물: [7 8 9]

# 마찬가지로 파이썬의 리스트에서 써봤던 slicing과 indexing 기법이다.

자 그러면 여기서부터 점점 머리를 복잡하게 하는 생소하고 왜 필요할까? 의문이 들게끔 하지만 정말 필수적인 numpy 만의 함수가 나오기 시작한다.

Boolean Indexing

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
27
28
29
30
31
32
33
34
35
36
# boolean indexing
# 처음으로 소개할 친구가 이 boolean indexing 이다.
# 이 친구는 data cleaning 시 매우 도움이 되는 친구이니 필히 기억하도록 하자.
# True 와 False 값으로 구분하여, True의 값을 가진 요소만 indexing 하는 방식이다.

arr = np.arange(0, 10, 1)  # 다시 ndarray를 잡아주고...
# arr :  [0 1 2 3 4 5 6 7 8 9]

# 여기서 4이상인 값만 출력해보자. 
# 물론 슬라이싱으로 눈대중으로 출력이 가능할 정도로 값이 정렬이 되어있고, 데이터 개수가 적다.
# 하지만 지금은 boolean indexing 을 사용해보고자 한다. 

print( arr >= 4 )   # 이렇게 치면 어떻게 출력이 되지? boolean으로 값을 받아오지!
# [False False False False True True True True True True]

print( arr[ arr >= 4 ]) # 이렇게 값을 주게 되면
print( arr[ [False False False False True True True True True True] ]) # 이것과 같은 소리임.
# 출력값: [4 5 6 7 8 9]
# 이게 바로 boolean indexing임.
# 처음엔 생소할 수 있으나, 금방 익숙해진다.
# 그리고 데이터가 많고, 순서가 뒤죽박죽일 때 굉장히 코드의 효율성과 데이터 핸들링을 도와주는 기법이다.

# 이번엔 짝수만 출력해보자
# 그래 짝수만 어떻게 출력할건가? 
# for loop와 if문을 쓰는 비효율적인 방식보다도 더 좋은게 
# 바로 이 boolean indexing이다.

print(arr[ arr % 2 == 0])   # 파이썬아, arr로가서 arr에서 2로 나누어지는 모든 것들을 출력해줘 라고 생각하자.
# 출력값: [0 2 4 6 8]

# 왜이렇게 까지 해야하나요?
# 데이터 처리를 결국 나중엔 빨리 해야함. 그래야 대용량 데이터를 다룰때 시간이 말도 안되게 빨라짐.
# 지금 이런 boolean 인덱싱은 포문 돌리고 루프해서 컨디션 걸고 찾는거와는 차원이 다르게 빠름.
# 확실하게 익숙해지자!
# 머신러닝에서 웬만하면 포문 이프문으로 가릴 생각을 하진 말자..

Fancy Indexing

“fancy indexing”은 ndarray에 index 배열 (list, ndarray)를 전달해서 indexing 한단 소리이다.

그게 무슨 소리야? 코드를 한번 살펴보자.

1
2
3
4
5
6
7
arr = np.arange(0, 5, 1)   # [0 1 2 3 4]

print(arr[1])   # 기존에 했던 indexing
print(arr[[1, 2, 4]])   # fancy indexing
# 출력물: [1 2 4]
# 이게 무슨소리야? 리스트 안 숫자는 인덱스 번호이다. 
# 1번, 2번, 4번 인덱스의 값을 ndarray로 출력한다.

음 생각보다 간단한거 같다. 리스트를 하나 더 넣어줘서 인덱싱을 하면 되는 단순한 작업이었다.

2차원은 어떨까?

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
arr = np.arange(0, 12, 1).reshape(3, 4).copy()    
# 3x4 배열로 0부터 11까지 1씩 증가하는 12개의 요소를 만들어라.
print(arr)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]] 이렇게 생김.

print(arr[2, 1])   # 9
print(arr[1:2, 2])  # 파이썬 우선 arr 행을 [1:2] 로 슬라이싱해. [4 5 6 7] 이 된다
                    # 거기서 2번째 요소 내놔   => [6]
# 뭐야? 왜 []가 씌워져있어? 
# shape 이 뭐가 나온다 이것도 명확하게 맞춰줘야함.   [6] 이 나오는데 일반적인 6이랑 다른거임!!!
# slicing 이잖아 저건. 그러니깐 요소가 숫자만 나오는게 아니고 ndarray로 나옴
# 생각을 해보자. 행, 열로 움직이잖아. slicing을 하면 행 먼저 짤라야겠지?
# 그리고 slicing 은 원본과 결과가 똑같아야겠지? 그러니깐 2차원이 나오지
# 2차원에 대해서 하나를 인덱싱을 한거야. 그러면 당연히 1차원이 나오지.

# 다시 해보자.
print("arr[1:2]: ", arr[1:2]) # [[4 5 6 7]] 이렇게 나옴.
# 여기서 이제 추가적으로 열에 대한 인덱스를 줘보자
print("arr[1:2, 2]: ", arr[1:2, 2]) # 그리고 거기서 칼럼 2번을 찾아서 출력하자. 
#      👇
# [[4 5 6 7]]     인덱스 2번 컬름은 [6] 이 되는거임. 
# [6] 3번째 칼럼이 나오지. (인덱스로 2를 줬잖아 0 1 2)


print("arr[1:2, 2:3]", arr[1:2, 2:3]) 
# 자 다시 한번 분해해서 보자구

# arr[1:2] 는 [[4 5 6 7]] 이었음.
# 여기서 열에 관해서 2:3 으로 슬라이싱을 해보자.
#       👇      
# [[4 5 6 7]]
# 근데 indexing 이었다면 똑같이 [6]이 나왔겠지.
# 하지만 슬라이싱을 했어 [[6]] 이 나온다.
# 크 어렵다고 느껴진다면 우선 이해보다도 이 자체로 인지하고 연습해보자. 
# 그렇다면 도움이 될 지 모른다.


# 이번엔 다르게 한 번 살펴보자.
print(arr[[0,2]])   # fancy indexing으로 0번째와 2번째를 선택했다. 
                    # 근데 뭐 행? 열? 어떤거 말하는거야?
                    # 1차원이야 쉬웠지 열 하나 밖에 없었으니깐.
                    # 지금은 2차원임을 기억하자. 
    							  # 이런 경우엔 행을 선택한거다. 0번째와 2번째 행.
# 👉 [[ 0  1  2  3]
#     [ 4  5  6  7]
#  👉 [ 8  9 10 11]]       이렇게 두 친구를 선택한거임.

# 그렇다면 fancy indexing으로 선택하고, 인덱싱을 하면 어떨까?
print(arr[[0,2], 2])  
# 자 바로 위에서 2개의 행이 선택이 됐잖아? 
# 그 2x4 매트릭스에서 2번 인덱스를 가진 열을 선택한다는 소리이다.
#         👇
# [[ 0  1  2  3]
#  [ 8  9 10 11]]      
# 즉 출력물은 [2 10] 이 된다. 

# 처음 접하면 우와~~~~ 어렵다. 싶을수 있다. 하지만 당신이 머신러닝을 하고자 한다면 
# 무조건 무조건!! 인지해야함을 다시 강조한다

# 자 더 복잡하게 들어가서 보자.

print(arr[[0,2], 2:3]) # 여기서 이렇게 슬라이싱을 하면 어떻게 될까?
# 이번에는 [[2] [10]] 이 되는 것을 위의 코드를 통해 알 수 있을것이다.

# 그렇다면 이건 어떨까?
print(arr[[0,2], [0,2]])
#   👇    👇
# [[ 0  1  2  3]
#  [ 8  9 10 11]] 
# 즉,
# [[0 2]
#  [8 10]] 이 되리라 믿는다
# 하지만 매우 중요하게도 출력물은...
# [ 0 10]  이 나온다.  이건 뭔 개소리야???????? 
# fancy indexing의 특징중 하나는 행과 열 둘중에 하나만 fancy indexing을 적용할 수 있고,
# 두개 전부 적용할 수는 없다.
# 내가 생각해도 어렵고 복잡하다.

# 그렇다면 아래와 같이 출력을 하려면 어떻게 해야할까?
# [[0 2]
#  [8 10]]    
# 이때 쓰는 것이 numpy가 갖고 있는 ix_ 라는 메서드이다.

print(arr[np.ix_([0,2], [0,2])])   # 비로소 우리가 원하는대로 출력이 된다.
# [[0 2]
#  [8 10]]

# 다른 방법을 사용할 수도 있다. 
print(arr[[0,2]][:, [0,2]])   

# [[0,2]] 로 행에 대해서 fancy 인덱싱을 하고 그 결괏값에 대해서 [:, [0,2]]  모든 행에 대해서 
#  열에 대해서 fancy indexing 을 한 번 더하겠다 [0, 2].

  
# 이런게 문제다.
# 이전에는 fancy indexing 을 행과 열 모두에 적용하였을 때는, 
# 에러로 나오지 않고 정상적으로 보이는 값이 나오니깐... 
# 머신러닝으로 분석을 한다면 데이터를 분석했다면
# 그렇다면 결괏값도 이상하게 나오겠죠.
# 그래서 넘파이에 대한 기초가 정말 중요하다.
# 머신러닝은 데이터 핸들링이 정말 중요하다.
# 텐서플로우 뭐 다 좋지. 근데 핸들링 못하면 아무짝에도 쓸모가 없다.
# 결론은 데이터핸들링을 잘해야함.
# 데이터 핸들링의 기본이 되는 것은 pandas겠지만, numpy는 pandas의 근간이 되는 모듈임을 잊지 말자.

음.. 정말 복잡하다고 생각한다.

하지만 익숙해지면 문제될리 없다고도 생각한다.

열심히 happy coding 하시길!

[numpy indexing and slicing] 포스팅 끄읏!

This post is licensed under CC BY 4.0 by the author.

파이썬과 객체지향, 그리고 절차지향의 이해 (2)

python NumPy를 이용한 수치 계산

Comments powered by Disqus.