Series의 인덱스는 넘파이 array의 인덱스와 유사하지만
정수가 아니어도 된다는 점이 다르다. 아래는 예시.
인덱스 b를 직접 조회해도 1이 나오고
obj[1], 즉 2번째 값을 조회해도 1이 나오는 것을 볼 수 있다.
단, 빨간색 워닝박스 안의 내용을 보면
지금은 obj[1]로 써도 위치 기반 인덱싱으로 동작하지만
앞으로의 버전에서는 레이블 기반 인덱싱으로 동작할 것이라고
알려주고 있다. 즉, 인덱스에 "1"이라는 레이블을 가진 값이 없으면
KeyError가 발생하게 된다는 것.
다만 현재와 미래 모두 obj.iloc[1]은
정확하게 위치 기반 인덱싱으로 작동한다고.
obj[2:4]로 쓸 때 주의할 점.
시작 지점인 2는 0, 1, 2로 세는 인덱스 특징상
세 번째 값인 c가 되고, 종료 지점인 4(n)는
실제로는 3(n-1), 즉 네 번째 값까지를 불러오라는 얘기.
iloc, 즉 위치 기반 인덱싱을 써서 슬라이싱을 할 때는
끝에 입력한 값이 포함되지 않는다고 그냥 외워야 한다.
아직도 쓰다보면 많이 헷갈리는 iloc의 문법인데,
자세한 내용은 별도의 포스팅에 정리해 두었다.
obj[[1,3]] 이렇게 써도 위에서 봤던 것과 똑같이
'앞으로는 레이블 기반 인덱싱으로 바뀔 거임' 이라는
경고 메시지가 뜬다.
역시 이렇게 쓰면 아무 문제 없다.
불리언 연산을 해서
해당하는 값들만 갖고 오는 것도 가능하다.
일반적으로 [ ] 기반의 색인은
정수가 포함될 경우 이를 레이블로 인식하고,
인덱스의 자료형에 따라 작동 방식이 달라진다. 예를 들어,
위와 같이 생긴 두 Series 객체 obj1과 obj2가 있다고 할 때,
obj1에서 0, 1, 2를 찾으면
이 값들을 레이블로 인식하기 때문에
인덱스 0, 1, 2에 각각 대응하는 2, 3, 1이 출력된다.
반면 obj2에서 0, 1, 2를 찾으면
이 값들을 정수 기반 위치로 인식하여
첫 번째부터 세 번째 값들인 a, b, c가 순서대로 출력된다.
단, 이 방식 역시도 미래에는 레이블로만 인식될 거기 때문에
확실하게 위치 기반으로 출력하고 싶으면 iloc를 쓰라고 뜬다.
예를 들면 이런 식으로. (끝 값 포함x 주의)
loc를 쓸 때 정수가 인덱스에 포함되어 있지 않으면 오류가 뜬다.
거꾸로 iloc를 쓸 때 문자열로 된 인덱스 레이블을
직접 불러도 오류가 뜬다.
loc와 iloc에 대한 차이는 별도 포스팅 참고.
정수 색인으로 판다스 객체를 다루다 보면
리스트나 튜플같은 파이썬 내장 자료구조에서의
인덱스를 다루는 방법상의 차이 때문에 실수하는 경우가 있다.
예를 들어,
이런 Series가 있다고 했을 때
이런 식으로 데이터를 찾으려고 하면 오류가 발생한다.
(레이블 인덱스 중 -1은 없기 때문에)
그런데 레이블 인덱스에
0, 1, 2와 같은 정수가 포함되어 있을 경우,
사용자가 레이블 인덱스로 선택하려는 것인지
정수(위치) 인덱스로 선택하려는 것인지 추측하긴 쉽지 않다.
정수가 아닌 인덱스를 사용하면 이런 모호함이 사라진다. 예컨대
이렇게 쓰면 -1번째,
즉 가장 마지막에 있는 인덱스의 값을 불러오라는
말로 알아듣고 c에 해당하는 2를 반환해 준다.
다만 이마저도 위에서 계속 언급했던 워닝 메시지는 계속 뜬다.
즉 앞으로는 -1 이렇게 정수(integer key)를 넣으면
레이블로 간주할 테니 위치 기반으로 데이터를 뽑고 싶으면
iloc를 쓰라는 것. (아마 이 책도 한 번은 업데이트를 거칠 듯.)
정리하자면,
인덱스에 정수값이 포함되어 있을 경우, 데이터를 뽑는 사람의 의도가 정확히 구현되지 않을 위험이 있다.
따라서 항상 loc와 iloc를 적절히 사용해 모호함을 피하는 것이 효과적이다.
loc와 iloc를 사용하면 데이터를 유연하게 선택할 수 있고
DataFrame의 값을 수정하는 데에도 이 성질을 유용하게 쓸 수 있다.
다만 연쇄 색인chained indexing을 쓸 때에
약간의 주의가 필요한데, 예를 들어
이렇게 생긴 DataFrame이 있다고 치자.
여기서 loc를 써서 전체 행을 선택한 다음,
"one"이라는 레이블을 갖는 열의 값을 1로 대체하려면
이렇게 하면 된다.
one 열에 해당하는 값들이 모두 1로 바뀌었다.
여기서 다시 정수 위치가 2인,
즉 세 번째 행의 데이터를 모두 5로 바꾸려면
iloc를 써서 2, 즉 세 번째 행을 고르고 5를 입력하면 된다.
실제로 세 번째 행인 Utah 행의 데이터가 모두 5로 바뀌었다.
여기서 한 가지만 더 조건을 추가해,
four라는 열에서 값이 5를 초과하는 경우는
그 값을 3으로 대체한다고 가정해 보자.
코드와 결과는 이렇게 나와야 할 것이다.
기존의 four 열에서 5를 초과하는 경우는
NY의 15였는데, 마지막 코드 실행 결과 3으로 바뀌었음을 볼 수 있다.
그런데,
다음과 같이 값을 할당할 때 인덱스를 연결해서(chained indexing)
사용하면 문제가 발생한다. 예를 들어,
이렇게 코드를 짜면 에러가 뜬다.
의도대로 원본 DataFrame의 값을 변경하는 게 아니라
임싯값(data.loc[data.three ==5]의 결과)을 변경하려고 한다는 의미다.
다시 말해,
data.loc[data.three == 5]["three"] = 6
이라는 코드는 총 2 단계로 작동한다.
실제로 다시 한 번 data를 조회해 보면
내용이 변경되지 않고 그대로임을 확인할 수 있다.
따라서, 연쇄 색인에서 오는 이 문제를 피하기 위해서는
단일 loc 연산을 사용해야 한다.
위의 코드는 정상적으로 작동하고,
값도 6으로 잘 변경된 것을 확인할 수 있다.
하나씩 뜯어보면,
라는 의미가 된다.
이건 지금 단일 행의 단일 열 값을 바꾸는 과정에서의
연쇄 색인 오류를 나타낸 것이고, 여러 행을 선택하거나
여러 열을 선택해 변경하는 과정에서의 연쇄 색인 오류도
나올 수 있다.
SettingWithCopyWarning이라는 에러가 뜨면
연쇄 색인 오류가 생긴 건 아닌지 생각해 볼 것.