Py) 기초 - Pandas(Crosstab)

Py) 기초 - Pandas(Crosstab)

파이썬 기반 데이터분석을 위하여 Pandas 라이브러리의 crosstab() 함수 사용법을 알아보자.


개요

Pandas 시리즈 객체의 원소 또는 데이터프레임 객체의 특정 변수의 원소의 개수를 세거나 원소 구성비를 알아보기 위해 .value_counts() 메서드를 사용할 수 있었다.
※ 관련 포스팅: Pandas(원소/조합 세기)

이제 2개 이상의 변수를 대상으로 .value_counts() 메서드와 같은 작업을 실시하고자 할 때 사용할 수 있는 Pandas 라이브러리의 함수는 crosstab() 이다. 이 함수는 (기본적으로) 두 변수의 각 원소가 같이 속해있는 행(row) 개수가 몇 개인지 반환해주며 .groupby() 또는 .pivot_table() 메서드와 같은 결과를 반환할 수도 있다.

실습

실습을 위해서 다음과 같이 데이터를 준비해보자.

1
2
3
4
5
6
7
df = pd.DataFrame(dict(user   = ["a", "b", "c", "d", "e", "f", "g", "h"],
gender = ["F", "F", "M", "M", "M", "F", "M", "M"],
timeStamp = ["2077-04-01 15:23:09", "2077-04-01 17:03:22", "2077-04-01 17:27:39", "2077-04-01 17:30:02",
"2077-04-01 19:38:40", "2077-04-01 20:01:51", "2077-04-01 20:49:32", "2077-04-01 22:49:32"],
is_booking = [0, 1, 1, 0, 0, 0, 1, 0],
session_time = [121, 1102, 430, 254, 304, 650, 1092, 729]))
df
user gender timeStamp is_booking session_time
0 a F 2077-04-01 15:23:09 0 121
1 b F 2077-04-01 17:03:22 1 1102
2 c M 2077-04-01 17:27:39 1 430
3 d M 2077-04-01 17:30:02 0 254
4 e M 2077-04-01 19:38:40 0 304
5 f F 2077-04-01 20:01:51 0 650
6 g M 2077-04-01 20:49:32 1 1092
7 h M 2077-04-01 22:49:32 0 729

“df” 객체의 각 변수 설명은 다음과 같다.
user: 사용자 ID
gender: 성별
timeStamp: 세션 시작 시각
is_booking: 예약 여부
session_time: 세션 유지 시간

기본 사용

crosstab() 함수의 사용예는 다음과 같다.

1
pd.crosstab(df["gender"], df["is_booking"])
is_booking 0 1
gender
F 2 1
M 3 2

상기 내용은 성별(gender)과 예약 여부(is_booking) 두 변수의 원소에 대해 한 번에 살펴본 것이다. 즉, 성별이 “F”이면서 예약 여부가 0인 행이 2개 있다는 것이다. 해당 사항은 다음의 코드를 사용하여 검증해볼 수 있다.

1
2
sum((df["gender"] == "F") & (df["is_booking"] == 0))
## 2

crosstab() 함수에 인자를 명시하지 않았는데 인자를 명시하여 상기와 같은 결과를 반환하고자 하는 경우는 다음과 같이 “index”와 “columns” 인자를 명시할 수 있다.

1
pd.crosstab(index = df["gender"], columns = df["is_booking"])
is_booking 0 1
gender
F 2 1
M 3 2

각 인자에 할당되는 “df” 객체의 변수를 바꿔보면 다음과 같다.

1
pd.crosstab(index = df["is_booking"], columns = df["gender"])
gender F M
is_booking
0 2 3
1 1 2

“index”나 “columns” 인자에는 두 개 이상의 변수를 할당할 수 있는데 이 때 “df”객체의 각 변수(좀 더 정확하게 말하자면 시리즈 객체)를 리스트로 감싸서 할당해야 한다.
다음은 명목형 변수를 만들기 위해서 세션 시작 시각(timeStamp) 변수를 사용하여 “hour”변수를 새로 생성 후 crosstab() 함수를 사용한 결과는 다음과 같다.

1
2
3
df["timeStamp"] = pd.to_datetime(df["timeStamp"])
df["hour"] = df["timeStamp"].dt.hour
pd.crosstab(index = [df["gender"], df["hour"]], columns = df["is_booking"])
is_booking 0 1
gender hour
F 15 1 0
17 0 1
20 1 0
M 17 1 1
19 1 0
20 0 1
22 1 0

정규화

이제 반환되는 객체의 원소를 정규화하는 방법에 대해 알아보자.
정규화를 위해서는 “normalize” 인자를 사용해야 하며 해당 인자에는 여러 종류의 값을 입력할 수 있는데 이에 대한 설명은 crosstab() 함수의 (도움말) 문서에서 찾을 수 있다.

normalize : bool, {‘all’, ‘index’, ‘columns’}, or {0,1}, default False
Normalize by dividing all values by the sum of values.

- If passed 'all' or `True`, will normalize over all values.
- If passed 'index' will normalize over each row.
- If passed 'columns' will normalize over each column.
- If margins is `True`, will also normalize margin values.

해당 문서의 내용을 해석하자면 “normalize” 인자에 할당할 수 있는 원소는 True, False, “all”, “index”, “columns”, 0, 1 으로 총 7개 이며 기본값은 False로 되어있다는 것이다.

참고로 “all”과 True를 할당한 결과는 같고 “index”와 0, “columns”와 1의 쌍도 그 결과가 같다.

다음 예제를 보도록 하자.

1
2
pd.crosstab(index = df["gender"], columns = df["is_booking"],
normalize = True)
is_booking 0 1
gender
F 0.250 0.125
M 0.375 0.250

모든 값을 정규화 한 결과를 확인할 수 있으며 이 때 모든 원소의 합은 1이다. 이를 보다 편하게 확인하기 위해서는 “margin” 인자를 사용할 수 있는데 그 예제는 다음과 같다.

1
2
3
pd.crosstab(index = df["gender"], columns = df["is_booking"],
normalize = True,
margins = True)
is_booking 0 1 All
gender
F 0.250 0.125 0.375
M 0.375 0.250 0.625
All 0.625 0.375 1.000

혹시나 “margin” 인자로 인해 생성되는 인덱스명을 별도로 지정하고자 할 때는 “margin_name” 인자에 적절한 이름을 할당하면 된다.

1
2
3
4
pd.crosstab(index = df["gender"], columns = df["is_booking"],
normalize = True,
margins = True,
margins_name = "Total")
is_booking 0 1 Total
gender
F 0.250 0.125 0.375
M 0.375 0.250 0.625
Total 0.625 0.375 1.000

이제 “normalize” 인자에 다른 값을 할당해보자.

1
2
pd.crosstab(index = df["gender"], columns = df["is_booking"],
normalize = 0)
is_booking 0 1
gender
F 0.666667 0.333333
M 0.600000 0.400000

그리고 “normalize”인자에 “index”를 할당한 결과가 같다는 것을 확인해보자.

1
2
pd.crosstab(index = df["gender"], columns = df["is_booking"],
normalize = "index")
is_booking 0 1
gender
F 0.666667 0.333333
M 0.600000 0.400000

이렇게 행(row)방향으로 정규화가 실시된 것을 확인할 수 있으며 각 행에 있는 원소의 합은 1이다. 추가로 해석을 덧붙이자면 상기 결과는 성별(gender)에 따른 예약 비율(is_booking)을 산출한 것이며 여성(F)의 경우 약 33.3%, 남성(M)의 경우 40%가 예약을 한 것을 확인할 수 있다.

이번에는 열(column)방향으로 정규화를 실시해보자.

1
2
pd.crosstab(index = df["gender"], columns = df["is_booking"],
normalize = 1)
is_booking 0 1
gender
F 0.4 0.333333
M 0.6 0.666667
1
2
pd.crosstab(index = df["gender"], columns = df["is_booking"],
normalize = "columns")
is_booking 0 1
gender
F 0.4 0.333333
M 0.6 0.666667

상기 내용 또한 해석을 덧붙이자면 예약을 하지 않은 사람의 경우 여성(F)이 40%, 남성(M)이 60%이며 예약을 한 사람의 경우 여성(F)이 약 33.3%, 남성(M)이 약 66.7% 인 것을 확인할 수 있다.

요약 연산

.crosstab() 함수를 사용하여 요약연산을 실시하고자 할 경우 3개의 변수(또는 시리즈 객체)가 필요하며 이 경우 “values” 인자에 변수 할당이 필요하다. 그리고 “values”에 할당되는 변수를 어떻게 요약할 것인지 명시해야 하는데 이 때 “aggfunc” 인자에 함수명 또는 함수를 할당해야 한다. “aggfunc” 인자에 함수명을 지정하는 경우는 Pandas 시리즈 객체에서 지원하는 메서드명과 같으며 직접 별도의 연산규칙을 적용하고자 할 때에는 사용자 정의 함수 또는 lambda 함수를 “aggfunc” 인자에 할당해야 한다.
※ aggfunc는 aggregate function의 약어이다.

기본 요약 연산 예제는 다음과 같다.

1
2
3
pd.crosstab(index = df["gender"], columns = df["is_booking"], 
values = df["session_time"],
aggfunc = "mean")
is_booking 0 1
gender
F 385.5 1102.0
M 429.0 761.0

상기 코드의 실행 결과는 성별(gender)과 예약 여부(is_booking)에 따른 세션 유지 시간(session_time)의 평균이다. 그리고 이는 다음과 같이 .groupby() 메서드를 사용하여 산출한 결과값과 같으며 단순 자료구조만 다르다고 보면 된다.

1
df.groupby(["gender", "is_booking"])["session_time"].mean().reset_index()
gender is_booking session_time
0 F 0 385.5
1 F 1 1102.0
2 M 0 429.0
3 M 1 761.0

사용자 정의 함수의 활용

이번에는 “aggfunc” 인자에 사용자 정의 함수를 할당해보자.

1
2
3
4
5
6
def mean_10000(x):
return x.mean() + 10000

pd.crosstab(index = df["gender"], columns = df["is_booking"],
values = df["session_time"],
aggfunc = mean_10000)
is_booking 0 1
gender
F 10385.5 11102.0
M 10429.0 10761.0

함수명이 아닌 함수 객체를 “aggfunc” 인자에 할당하니 주의하자.

lambda 함수의 활용

이번에는 lambda 함수를 활용해보자.

1
2
3
pd.crosstab(index = df["gender"], columns = df["is_booking"], 
values = df["session_time"],
aggfunc = lambda x: x.mean() + 10000)
is_booking 0 1
gender
F 10385.5 11102.0
M 10429.0 10761.0

사용자 정의 함수를 사용하는 것과 별다른 차이가 없는 것을 알 수 있다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×