지난 판다스 편에서는 판다스의 함수 또는 속성을 활용해 데이터 프레임의 기본적인 정보를 파악하고 판다스 고유 인덱서를 활용한 2차원 인덱싱과 슬라이싱에 대해 공부했다. 이번에는 판다스를 이용한 데이터 분석에 있어 특정 조건을 만족하는 데이터를 추출하고 값을 입력하거나 계산하는 방법 등에 대해 공부해 보도록 하자.
우리는 앞서 특정 열이나 행, 혹은 특정 행의 특정 열 값을 추출하는 방법을 공부했다. 이제 이를 이용해 특정 조건을 만족하는 행 또는 열의 데이터를 추출하는 방법을 배워보자.
데이터프레임에서 보험증서의 수(Number of Policies)가 2개인 사람들의 데이터만 따로 살펴보고 싶다고 하자. 즉, 컬럼 속성 가운데 Number of Policies값이 2인 사람들의 데이터만 출력하는 것이다.
df['Number of Policies']==2인 데이터를 확인하면 앞에서 배웠듯 ==, !=, >과 같은 비교 표현은 불린형 데이터를 결과로 반환한다. 즉, df['Number of Policies']==2를 실행하면 아래와 같이 Number of Policies 컬럼의 데이터 값이 2인 경우 True, 아닌 경우 False를 반환하는 시리즈가 생성된다.
이를 전체 데이터프레임에 적용하게 되면 전체 데이터 프레임에서 df['Number of Policies']==2가 True인 모든 행들이 출력된다. 전체 9134개의 행 가운데 보험증서의 개수가 2개인 경우는 2294개로 해당 컬럼의 데이터 값의 종류가 9개인 것을 감안하면 아주 많은 비중이다.
실제 데이터 분석을 해보면 보험증서의 개수가 2개인 경우에 LTV가 가장 높고, 나머지 보험증서의 개수는 LTV가 거의 비슷한데, 보험증서가 2개인 모든 행 데이터에서 Customer Lifetime value 열의 값만 살펴보자. df[df['Number of Policies']==2에 Customer Lifetime value 열을 붙이면 된다.
또한, 이 사람들의 LTV 평균을 구할 수도 있는데 df[df['Number of Policies']==2]['Customer Lifetime Value'] 끝에 mean( ) 함수를 붙이면 된다. 보험증서가 2개인 모든 행에서 Customer Lifetime Value 열의 평균은 약 15,000 달러로 전체 LTV 평균이 약 8,000달러임을 감안하면 약 2배 수준임을 알 수 있다.
위와 같은 내용을 파이썬 기본 문법이 아닌 loc 인덱서를 사용한 방식으로 해도 결과는 같다.
이번에는 Customer Lifetime value 값을 한 번 살펴보자. Customer LTV는 전체 데이터 개수 9134개 가운데 데이터 값의 종류(unique)가 8041개로 데이터 값의 종류가 아주 많은데 정상범위를 크게 벗어난 이상치를 한 번 살펴 보도록 하자.
통계학에서 이상치(아웃라이어)를 판별하는 방법 중 하나로 데이터를 4등분으로 나누고 첫 번째 25 퍼센타일(Q1)에서 사분범위X1.5를 뺀 수보다도 작거나 세 번째 75퍼센타일(Q3)에서 사분위수X1.5를 더한 수보다도 큰 경우 이상치로 본다. 여기서 사분범위(IQR, Interquartile Range)는 Q3에서 Q1을 뺀 값으로 중앙값을 중심으로 양쪽으로 동일한 25%의 백분율을 가진 두 점 사이의 거리를 말한다.
quantile( ) 함수를 이용해 Customer LTV의 Q1(25 퍼센타일)과 Q3(75 퍼센타일)을 구하고 Q3에서 Q1을 빼서 사분범위(IQR)을 구한다. 그리고 Q1에서 1.5 IQR을 뺀 값보다 작거나 Q3에서 1.5 IQR을 더한 값보다 벗어나는 위치에 있는 Customer LTV 컬럼값을 출력해 보자. ( | )는 두 가지 조건을 or로 묶는 역할을 한다. 두 가지 조건을 and로 묶기 위해서는 (&)을 사용하면 된다.
Customer LTV 값의 이상치는 817개로, Q1-1.5 IQR보다 작은 값은 없지만 Q3+1.5 IQR보다도 큰 극단적인 값들이 아주 많이 존재함을 알 수 있다. LTV 예측이라는 목표를 두고 데이터 분석을 한다면 이 이상치를 그대로 두는 경우와 어떻게 처리하는 지가 예측 정확도에 영향을 미칠 수도 있을 것이다.
첫 번째 판다스 편에서 나왔지만 새로운 열을 만들고 싶다면 새로운 열의 이름을 짓고 값을 부여(Assigning) 하면 되고, 기존 열의 데이터 값을 바꾸고 싶다면 해당 컬럼을 인덱싱한 뒤 값을 입력하면 된다.
이 때 한 가지 값을 입력하면 컬럼의 값이 모두 한 가지로 채워지고, 리스트와 같이 반복가능한 데이터 (iterable data)로 넣으면 컬럼의 값이 리스트 안의 아이템 순서대로 채워지게 된다. 단, 반복가능한 데이터를 입력한다면 입력되는 데이터는 컬럼의 행의 숫자와 같아야 할 것이다. 여기서 반복 가능한 데이터란 요소 하나하나를 개별로 리턴할 수 있는 데이터 유형을 말하는 것으로, 문자열, 리스트, 튜플, 딕셔너리, 레인지 함수 등이 이에 해당한다.
그렇다면 이번에는 특정조건을 만족하는 데이터 별로 값을 입력하는 방법을 연습해 보자. 실제 데이터 분석에서는 데이터 분석 결과 목표값(종속변수)에 영향을 주는 컬럼(독립변수)를 피처 항목으로 선택하고 머신러닝 알고리즘을 적용하기 전에 텍스트 데이터를 모두 숫자형 데이터로 바꿔줘야 하는데, 여기서는 이걸 이용해서 특정 조건별로 데이터 값(숫자)을 입력하는 방법을 연습해 보자.
Renew Offer Type라는 컬럼을 피처항목 가운데 하나로 선정하고자 하며 텍스트 데이터를 숫자형 데이터로 변경하고자 한다. Renew Offer Type은 Offer1, Offer2, Offer3, Offer4라는 4가지 데이터 값을 가지고 있고 각각 0, 1, 2, 3이라는 숫자형 데이터로 변경하려고 한다. 앞에서 배운 것처럼 특정조건을 만족하는 데이터를 출력하고 값을 변경하기 위해서는 df['Renew Offer Type']== 'Offer1'인 행을 선택하고 Renew Offer Type 컬럼을 선택하면 될 것이다.
주의할 점은 앞에서 했던 것처럼 파이썬의 기본 문법을 쓰지 않고 loc를 사용해 특정 조건을 가진 행의 특정 열을 인덱싱했다는 점이다. 특정 조건을 가진 데이터를 출력할 때는 파이썬 기본문법을 사용하든 loc를 사용하든 차이가 없지만 인덱싱 후 그 값을 입력할 때는 loc만 가능했다.
파이썬 기본 문법으로 인덱싱 후 값을 입력하려고 하면 ‘A value is trying to be set on a copy of a slice from a DataFrame’라는 오류가 뜬다. 그 이유를 정확히 모르겠지만 데이터 인덱싱은 둘 중 어떤 방법을 써도 가능하지만 데이터 수정은 데이터 프레임과 같은 2차원 형태에서만 가능한 것인지??...추측해 본다.
우리는 앞서 이미 넘파이를 배울 때 넘파이 통계함수를 연습한 적이 있다. sum이나 max, min, mean, median 모두 넘파이나 판다스나 마찬가지로 사용한다. 다만 퍼센타일의 경우 넘파이는 np.percentile( ) 함수를, 판다스는 quantile( ) 함수를 쓴다는 점이 다르다.
데이터프레임 전체나 컬럼, 특정 조건을 만족하는 행 뒤에 통계함수를 붙여 사용해 보는 것은 이미 앞에서 연습했다. 이번에는 groupby( ) 속성을 사용해 원하는 그룹별로 특정 컬럼의 통계 데이터를 구해 보자. groupby( )에 Gender 컬럼을 넣고 LTV 값의 평균을 구하면, Gender 컬럼의 유니크 값 별로 Customer LTV의 평균값을 구할 수 있다.
앞서 value_counts( ) 함수는 특정 컬럼의 데이터 종류별로 유니크 값을 세어준다고 했다. groupby( ) 속성을 사용한다면 괄호 안에 해당 컬럼을 넣고 다시 똑같은 컬럼을 센다면 같은 효과를 얻을 것이다.
df.groupby(‘A’).B.command() → # A컬럼 그룹별로 B컬럼을 어떻게 한다
sum, count, max, min, mean, std 등과 같이 파이썬에 있는 기본적인 함수 외에 커스텀 또는 복잡한 처리를 하고 싶다면 apply와 람다함수 조합을 사용한다. apply 함수는 데이터프레임 전체 혹은 열 (또는 행)을 받는 람다함수를 인수로 넣어 각 열에 반복하여 그 함수를 적용시킨다.
여기서 람다함수란 우리가 파이썬 기본 문법에서 배운 바와 같이 반복해서 사용되는 내용을 사용자가 정의해 사용하는 함수(functions)와 기본적으로 같지만 다만, 별도의 정의 없이 바로 한 줄의 코드로 사용할 수 있다는 점에서 간편 혹은 간이 함수라 할 수 있다.
lambda x: x를 포함한 함수 내용
아래 몇 가지 예를 통해 apply X 람다함수 용법에 대한 감을 익혀 보자. 첫 번째는 데이터 프레임 전체를 인수로 받는 경우다. 데이터 프레임에서 보험증서 개수별로 LTV가 가장 최대인 행들만 출력하고 싶다면? df.groupby('Number of Policies')['Customer Lifetime Value']에 max( ) 함수를 붙이면 보험증서 개수별로 가장 큰 LTV 값이 출력되지 행(row)이 출력되지는 않는다.
LTV 값이 최대인 행을 출력하는 람다함수를 넣은 apply 함수를 데이터 프레임 전체에 적용시키되 보험증서 개수 개수라는 그룹 속성을 지정하자. 원하는 대로 보험증서 개수별로 Customer LTV가 최대인 9개의 행들이 출력될 것이다.
두 번째는 Months Since Last Claim 컬럼값을 이용해 컬럼값이 12개월 이상 24개월 미만이면 ‘1년’으로, 24개월 이상 36개월 미만은 ‘2년’으로, 그 외는 ‘1년 미만’으로 처리해 새로운 열 df['Years Since Last Claim']를 만들어 보자. Months Since Last Claim 컬럼의 값을 하나씩 받아 위와 같은 조건으로 처리하는 람다함수를 반복 적용해서 만든 새로운 컬럼이다.
apply 함수를 처음부터 잘 이해하는 것을 어렵지만 우선 이것만 기억해 보자. 데이터 프레임이나 열을 받는 람다함수로 원하는 기능을 만들어 특정 조건을 만족하는 데이터 프레임을 출력하거나 새로운 열을 만들 때 사용할 수 있다고 말이다.
https://www.kaggle.com/kyungapark/pandas-selecting-assigning-and-functions