[Python Data Analysis]9.array 인덱싱 이해하기
in Development on DataAnalysis
array 인덱싱 이해하기
기본 인덱싱
numpy array의 꽃은 인덱싱(indexing)입니다. 기본적인 인덱싱은 Python 리스트와 매우 유사합니다. arr이라는 array가 있을 때, arr[5]
와 같이 특정한 인덱스를 명시할 수도 있고, arr[5:8]
과 같이 범위 형태의 인덱스를 명시할 수도 있습니다. arr[:]
의 경우, 해당 array의 전체 성분을 모두 선택한 결과에 해당합니다.
이러한 인덱싱 방식은 2차원 array에 대해서도 아주 유사한 방식으로 적용됩니다. arr2d라는 2차원 array를 정의한 뒤, arr2d[2, :]
를 실행하게 되면 arr2d에서 인덱스가 2에 해당하는 행(3행)의 모든 성분이 1차원 array의 형태로 얻어집니다. arr2d[:, 3]
을 실행하면, arr2d에서 인덱스가 3에 해당하는 열(4열)의 모든 성분이 1차원 array의 형태로 얻어집니다.
2차원 array에서는 이렇게 두 개의 인덱스를 받을 수 있는데, ,
를 기준으로 앞부분에는 ‘행’에 대한 인덱스가, 뒷부분에는 ‘열’에 대한 인덱스가 입력됩니다. arr2d[1:3, :]
혹은 arr2d[:, :2]
와 같이, 행 또는 열에 범위 인덱스를 적용하여 여러 개의 행 혹은 열을 얻을 수도 있습니다.
한편 2차원 array에서 4행 3열에 위치한 하나의 성분을 얻고자 하는 경우, arr2d[3, 2]
를 실행하면 됩니다. 인덱싱을 통해 선택한 성분에 새로운 값을 대입하는 경우에도, arr2d[:2, 1:3] = 0
과 같이 하면 됩니다.
여러 가지 방법으로 인덱싱을 시도하면서, array에 대한 인덱싱 방식에 익숙해지시길 바랍니다.
불리언 인덱싱
array와 관련하여 중요하게 사용되는 또 다른 인덱싱 방식이 불리언 인덱싱입니다.
names = np.array(["Charles", "Kilho", "Hayoung", "Charles", "Hayoung", "Kilho", "Kilho"])
data = np.array([[ 0.57587275, -2.84040808, 0.70568712, -0.1836896 ],
[-0.59389702, -1.35370379, 2.28127544, 0.03784684],
[-0.28854954, 0.8904534 , 0.18153112, 0.95281901],
[ 0.75912188, -1.88118767, -2.37445741, -0.5908499 ],
[ 1.7403012 , 1.33138843, 1.20897442, -0.58004389],
[ 1.11585923, 1.02466538, -0.74409379, -1.55236176],
[-0.45921447, 2.53114818, 0.5029578 , -0.24088216]])
위와 같은 names array와 data array가 정의되었다고 가정합시다. names array 내 각각의 성분이, data array의 각 행에 순서대로 대응된다고 가정합시다. 이러한 상황에서 이름이 “Charles”인 사람의 행 데이터만을 추출하고 싶다고 할 때, names == "Charles"
를 실행하면 다음과 같은 결과를 얻을 수 있습니다.
names == "Charles"
>>> array([ True, False, False, True, False, False, False], dtype=bool)
얻어진 array를 잘 보면, 값이 “Charles”인 성분의 위치에는 True
가, 그 외의 위치에는 False
가 들어가 있는 것을 확인할 수 있습니다. 이렇게 names == "Charles"
와 같은 조건식 형태의 코드를 실행하였을 때 생성되는 불리언 array를 다른 말로 ‘마스크(mask)’라고 합니다.
이런 마스크는 다른 array를 인덱싱하는 데 사용할 수 있습니다. 예를 들어 data[names == "Charles", :]
를 실행하면, 위에서 보인 마스크가 True
에 해당하는 행만을 data array로부터 가져오게 되고, 이들만으로 구성된 array를 얻을 수 있습니다.
data[names == "Charles", :]
>>> array([[ 0.57587275, -2.84040808, 0.70568712, -0.1836896 ],
[ 0.75912188, -1.88118767, -2.37445741, -0.5908499 ]])
이러한 조건을 여러 개 추가할 수도 있습니다. 예를 들어 값이 “Charles” 혹은 “Kilho”인 성분에 대응되는 행을 얻고 싶다면, 다음과 같이 하면 됩니다.
data[(names == "Charles") | (names == "Kilho"), :]
>>> array([[ 0.57587275, -2.84040808, 0.70568712, -0.1836896 ],
[-0.59389702, -1.35370379, 2.28127544, 0.03784684],
[ 0.75912188, -1.88118767, -2.37445741, -0.5908499 ],
[ 1.11585923, 1.02466538, -0.74409379, -1.55236176],
[-0.45921447, 2.53114818, 0.5029578 , -0.24088216]])
혹은 기존의 data array의 각 성분의 값을 기준으로 불리언 인덱싱을 수행할 수도 있습니다. 예를 들어 data[:, 3]
을 실행하여 4열만의 값을 본다고 할 때, data[:, 3] < 0
을 실행하여 4열 상에서 그 값이 0보다 작은 성분을 조사하면, 다음과 같은 마스크를 얻을 수 있습니다.
data[:, 3] < 0
>>> array([ True, False, False, True, True, True, True], dtype=bool)
data array의 4열의 값이 0보다 작은 행에 대해서는 행 내 모든 성분에 0을 대입하도록 해 봅시다. 다음과 같이 하면 됩니다.
data[data[:, 3] < 0, :] = 0
불리언 인덱싱은 처음 접할 때 굉장히 헷갈릴 수 있기 때문에, 여러분들이 직접 다양한 array에 대하여 반복 연습해 보면서 제대로 익힐 필요가 있습니다.