[Python] 데이터 사이언스 스쿨 - 4.4 데이터프레임의 데이터 조작
Updated:
데이터 사이언스 스쿨 자료를 토대로 공부한 내용입니다.
실습과정에서 필요에 따라 내용의 누락 및 추가, 수정사항이 있습니다.
4.4 데이터프레임의 데이터 조작
데이터 갯수 세기
import numpy as np
import pandas as pd
# 시리즈 생성
ps = pd.Series(range(10))
ps[5] = np.nan
ps
0 0.0
1 1.0
2 2.0
3 3.0
4 4.0
5 NaN
6 6.0
7 7.0
8 8.0
9 9.0
dtype: float64
- NaN 값을 가지는 시리즈를 생성하였다.
# NaN 값은 제외하고 카운트
print(ps.count())
# len은 NaN 값을 포함
print(len(ps))
9
10
count()
는 NaN 값을 제외하고 갯수를 세며len()
은 NaN 값을 포함해서 갯수를 센다.
# 데이터 프레임 생성
np.random.seed(2)
df = pd.DataFrame(np.random.randint(5, size=(4, 4)),
index = ["a","b","c","d"],
columns = ["A","B","C","D"],
dtype=float)
df.iloc[2, 3] = np.nan
df
A | B | C | D | |
---|---|---|---|---|
a | 0.0 | 0.0 | 3.0 | 2.0 |
b | 3.0 | 0.0 | 2.0 | 1.0 |
c | 3.0 | 2.0 | 4.0 | NaN |
d | 4.0 | 3.0 | 4.0 | 2.0 |
- NaN 값을 가지는 데이터 프레임을 생성하였다.
# 열 단위로 계산
df.count()
A 4
B 4
C 4
D 3
dtype: int64
-
데이터 프레임 역시
count()
는 NaN 값을 제외하고 갯수를 센다. -
기본적으로 열 단위로 계산되는데 이는 axis=0이 디폴트여서이다.
-
axis=1로 설정하면 행 단위로 갯수를 센다.
연습 문제 4.4.1
타이타닉호 승객 데이터의 데이터 개수를 각 열마다 구해본다.
# seaborn 패키지 타이타닉 데이터 불러오기
import seaborn as sns
titanic = sns.load_dataset("titanic")
titanic.head()
survived | pclass | sex | age | sibsp | parch | fare | embarked | class | who | adult_male | deck | embark_town | alive | alone | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.2500 | S | Third | man | True | NaN | Southampton | no | False |
1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.2833 | C | First | woman | False | C | Cherbourg | yes | False |
2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.9250 | S | Third | woman | False | NaN | Southampton | yes | True |
3 | 1 | 1 | female | 35.0 | 1 | 0 | 53.1000 | S | First | woman | False | C | Southampton | yes | False |
4 | 0 | 3 | male | 35.0 | 0 | 0 | 8.0500 | S | Third | man | True | NaN | Southampton | no | True |
titanic.count()
survived 891
pclass 891
sex 891
age 714
sibsp 891
parch 891
fare 891
embarked 889
class 891
who 891
adult_male 891
deck 203
embark_town 889
alive 891
alone 891
dtype: int64
- 각 열별로 891개의 행을 가지는데 age, embarked, deck, embark_town에는 결측이 포함되어 있다.
카테고리 값 세기
# 시리즈 생성
ps2 = pd.Series(["a","b","b","c","e","a","b"],
index = range(10,17))
ps2
10 a
11 b
12 b
13 c
14 e
15 a
16 b
dtype: object
# 시리즈 각 값의 개수 확인
ps2.value_counts()
b 3
a 2
e 1
c 1
dtype: int64
-
넘파이에서 사용한
np.unique( [], return_counts= True)
,np.bincount()
와 같다. -
value_counts()
는 판다스 시리즈에서 사용가능하며 기존 시리즈의 값이 index, 값의 갯수가 values인 시리즈를 생성한다.
# df["A"].value_counts()
# df.A.value_counts()
df.loc[:,"A"].value_counts()
3.0 2
4.0 1
0.0 1
Name: A, dtype: int64
- 데이터 프레임은
value_counts()
가 없어서 각 열별로 추출(시리즈로 변환)해서 확인할 수 있다.
정렬
# value_counts()를 통해 기존 값이 인덱스로, 갯수가 값으로 변형된 새로운 시리즈가 생성
# 인덱스를 기준으로 정렬
ps2.value_counts().sort_index()
a 2
b 3
c 1
e 1
dtype: int64
sort_index()
는 인덱스를 기준으로 정렬하는 함수이다.
# 값을 기준으로 정렬 - NaN이 마지막에 출력
ps.sort_values()
0 0.0
1 1.0
2 2.0
3 3.0
4 4.0
6 6.0
7 7.0
8 8.0
9 9.0
5 NaN
dtype: float64
sort_values()
는 값을 기준으로 정렬한다.
# 내림차순 정렬
ps.sort_values(ascending = False)
9 9.0
8 8.0
7 7.0
6 6.0
4 4.0
3 3.0
2 2.0
1 1.0
0 0.0
5 NaN
dtype: float64
- 내림차순으로 정렬하려면 ascending 옵션을 False로 설정한다.
# 데이터 프레임 정렬
df.sort_values(by="A")
A | B | C | D | |
---|---|---|---|---|
a | 0.0 | 0.0 | 3.0 | 2.0 |
b | 3.0 | 0.0 | 2.0 | 1.0 |
c | 3.0 | 2.0 | 4.0 | NaN |
d | 4.0 | 3.0 | 4.0 | 2.0 |
- 데이터 프레임을 정렬하기 위해선 by 옵션으로 기준이 되는 열을 지정해야한다.
# 2순위 정렬 선택
df.sort_values(by=["A","B"], ascending = [True,False])
A | B | C | D | |
---|---|---|---|---|
a | 0.0 | 0.0 | 3.0 | 2.0 |
c | 3.0 | 2.0 | 4.0 | NaN |
b | 3.0 | 0.0 | 2.0 | 1.0 |
d | 4.0 | 3.0 | 4.0 | 2.0 |
- 2순위 정렬 기준을 추가하려면 by 옵션에 여려 열을 지정한다.
연습 문제 4.4.2
sort_values()
메서드를 사용하여 타이타닉호 승객에 대해 성별(sex) 인원수, 나이별(age) 인원수, 선실별(class) 인원수, 사망/생존(alive) 인원수를 구하라.
# 성별 인원수
titanic.sex.value_counts().sort_values()
female 314
male 577
Name: sex, dtype: int64
# 나이별 인원수
titanic["age"].value_counts().sort_values()
0.42 1
20.50 1
24.50 1
0.67 1
14.50 1
..
30.00 25
19.00 25
18.00 26
22.00 27
24.00 30
Name: age, Length: 88, dtype: int64
- 나이가 0.42세 등 이상한 값이 있지만 우선 건너띈다.
# 선실별 인원수
titanic.loc[:,"class"].value_counts().sort_values()
Second 184
First 216
Third 491
Name: class, dtype: int64
# 사망/생존 인원수
titanic["alive"].value_counts().sort_values()
yes 342
no 549
Name: alive, dtype: int64
행/열 합계
np.random.seed(1)
df2 = pd.DataFrame(np.random.randint(10, size=(4, 8)),
index = ["a","b","c","d"],
columns = ["A","B","C","D","E","F","G","H"])
df2
A | B | C | D | E | F | G | H | |
---|---|---|---|---|---|---|---|---|
a | 5 | 8 | 9 | 5 | 0 | 0 | 1 | 7 |
b | 6 | 9 | 2 | 4 | 5 | 2 | 4 | 2 |
c | 4 | 7 | 7 | 9 | 1 | 7 | 0 | 6 |
d | 9 | 9 | 7 | 6 | 9 | 1 | 0 | 1 |
# 열 합계 (디폴트 axis = 0)
df2.sum()
A 24
B 33
C 25
D 24
E 15
F 10
G 5
H 16
dtype: int64
sum()
과 같은 대부분의 축소 연산은 디폴트 axis=0으로 열 단위 연산을 한다.
# 행 합계
df2.sum(axis = 1)
a 35
b 34
c 41
d 42
dtype: int64
- 행 단위 연산을 할 때는 axis=1 옵션을 부여한다.
# 행 추가
df2.loc["colTotal",:] = df2.sum()
df2
A | B | C | D | E | F | G | H | |
---|---|---|---|---|---|---|---|---|
a | 5.0 | 8.0 | 9.0 | 5.0 | 0.0 | 0.0 | 1.0 | 7.0 |
b | 6.0 | 9.0 | 2.0 | 4.0 | 5.0 | 2.0 | 4.0 | 2.0 |
c | 4.0 | 7.0 | 7.0 | 9.0 | 1.0 | 7.0 | 0.0 | 6.0 |
d | 9.0 | 9.0 | 7.0 | 6.0 | 9.0 | 1.0 | 0.0 | 1.0 |
colTotal | 24.0 | 33.0 | 25.0 | 24.0 | 15.0 | 10.0 | 5.0 | 16.0 |
# 열 추가
df2["RowSum"] = df2.sum(axis=1)
df2
A | B | C | D | E | F | G | H | RowSum | |
---|---|---|---|---|---|---|---|---|---|
a | 5.0 | 8.0 | 9.0 | 5.0 | 0.0 | 0.0 | 1.0 | 7.0 | 35.0 |
b | 6.0 | 9.0 | 2.0 | 4.0 | 5.0 | 2.0 | 4.0 | 2.0 | 34.0 |
c | 4.0 | 7.0 | 7.0 | 9.0 | 1.0 | 7.0 | 0.0 | 6.0 | 41.0 |
d | 9.0 | 9.0 | 7.0 | 6.0 | 9.0 | 1.0 | 0.0 | 1.0 | 42.0 |
colTotal | 24.0 | 33.0 | 25.0 | 24.0 | 15.0 | 10.0 | 5.0 | 16.0 | 152.0 |
연습 문제 4.4.3
-
타이타닉호 승객의 평균 나이를 구하라.
-
타이타닉호 승객중 여성 승객의 평균 나이를 구하라.
-
타이타닉호 승객중 1등실 선실의 여성 승객의 평균 나이를 구하라.
# 1. 타이타닉호 승객의 평균 나이
# titanic.mean()
a1 = round( titanic.age.mean(), 0)
print(f"평균 나이: {a1}세")
평균 나이: 30.0세
# 2. 타이타닉호 승객 중 여성 승객의 평균 나이
titanic_female = titanic[titanic["sex"] == "female"]
a2 = round(titanic_female["age"].mean(), 0)
print(f"평균 나이: {a2}세")
평균 나이: 28.0세
# 3. 타이타닉호 승객 중 1등신 선실의 여성 승객의 평균 나이
titanic_first_female = titanic[(titanic["class"] == "First") & (titanic["sex"] == "female")]
a3 = round(titanic_first_female["age"].mean(), 0)
print(f"평균 나이: {a3}세")
평균 나이: 35.0세
apply 변환
-
apply()
를 사용하면 행이나 열 단위로 더 복잡한 처리가 가능하다. -
인수로 행 또는 열을 받는 함수를 넣으면 각 열(또는 행)을 반복하여 그 함수에 적용시킨다.
# 데이터 프레임 생성
df3 = pd.DataFrame({
'A': [1, 3, 4, 3, 4],
'B': [2, 3, 1, 2, 3],
'C': [1, 5, 2, 4, 4]
})
df3
A | B | C | |
---|---|---|---|
0 | 1 | 2 | 1 |
1 | 3 | 3 | 5 |
2 | 4 | 1 | 2 |
3 | 3 | 2 | 4 |
4 | 4 | 3 | 4 |
# 열 단위 연산
# x는 데이터 프레임을 받으며 각 열별로 뒤 함수를 적용
df3.apply(lambda x: x.max() - x.min())
A 3
B 2
C 4
dtype: int64
-
위 예시는 각 열별로 최대값 - 최소값을 반환한다.
-
디폴트는 axis=0으로 열단위 연산을 수행한다.
# 행 단위 연산
# x는 데이터 프레임을 받으며 각 행별로 뒤 함수를 적용
df3.apply(lambda x: x.max() - x.min(), axis = 1)
0 1
1 2
2 3
3 2
4 1
dtype: int64
- 위 예시는 axis=1 옵션을 부여하여서 각 행별로 최대값 - 최소값을 반환한다.
temp = pd.DataFrame({
'A': [1, 3, 4, 3, "a"],
'B': ["a", "b", "c", "d", "f"],
'C': [1, 5, 2, 4, 4]
})
temp
A | B | C | |
---|---|---|---|
0 | 1 | a | 1 |
1 | 3 | b | 5 |
2 | 4 | c | 2 |
3 | 3 | d | 4 |
4 | a | f | 4 |
# 각 열마다 사용된 값의 갯수
temp.apply(pd.value_counts)
A | B | C | |
---|---|---|---|
1 | 1.0 | NaN | 1.0 |
2 | NaN | NaN | 1.0 |
3 | 2.0 | NaN | NaN |
4 | 1.0 | NaN | 2.0 |
5 | NaN | NaN | 1.0 |
a | 1.0 | 1.0 | NaN |
b | NaN | 1.0 | NaN |
c | NaN | 1.0 | NaN |
d | NaN | 1.0 | NaN |
f | NaN | 1.0 | NaN |
-
이번엔
apply()
에pd.value_counts()
를 적용하였다. -
각 열에 대해 어떤 값이 얼마나 사용되었는지를 확인 가능하다.
-
하지만 기존 모든 열의 값이 새로운 index로 반환되서 이전 시리즈에서 적용하던 것처럼 분리해서 보는게 좋아 보인다.
# 20살을 기준으로 성인/아이 구분
titanic["adult/child"] = titanic.apply(lambda x: "adult" if x.age >= 20 else "child", axis = 1)
titanic.tail()
survived | pclass | sex | age | sibsp | parch | fare | embarked | class | who | adult_male | deck | embark_town | alive | alone | adult/child | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
886 | 0 | 2 | male | 27.0 | 0 | 0 | 13.00 | S | Second | man | True | NaN | Southampton | no | True | adult |
887 | 1 | 1 | female | 19.0 | 0 | 0 | 30.00 | S | First | woman | False | B | Southampton | yes | True | child |
888 | 0 | 3 | female | NaN | 1 | 2 | 23.45 | S | Third | woman | False | NaN | Southampton | no | False | child |
889 | 1 | 1 | male | 26.0 | 0 | 0 | 30.00 | C | First | man | True | C | Cherbourg | yes | True | adult |
890 | 0 | 3 | male | 32.0 | 0 | 0 | 7.75 | Q | Third | man | True | NaN | Queenstown | no | True | adult |
-
조금 더 복잡한 계산으로 if를 이용해서
apply()
를 적용 가능하다. -
apply(lambda x: 값1 if 조건1 값2 if 조건2 .... else 나머지 값, axis =1)
형태로 지정하면 부여 받는 x에 대해 조건별로 값을 반환한다. -
여기선 각 행(사람)마다 성인/아이를 구분하므로 axis=1 옵션을 부여하였다.
연습 문제 4.4.4
타이타닉호의 승객에 대해 나이와 성별에 의한 카테고리 열인 category1
열을 만들어라.
category1
카테고리는 다음과 같이 정의된다.
-
20살이 넘으면 성별을 그대로 사용한다.
-
20살 미만이면 성별에 관계없이 “child”라고 한다.
titanic["category1"] = titanic.apply(lambda x: x.sex if x.age >= 20 else "child", axis=1)
titanic.tail(10)
survived | pclass | sex | age | sibsp | parch | fare | embarked | class | who | adult_male | deck | embark_town | alive | alone | adult/child | category1 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
881 | 0 | 3 | male | 33.0 | 0 | 0 | 7.8958 | S | Third | man | True | NaN | Southampton | no | True | adult | male |
882 | 0 | 3 | female | 22.0 | 0 | 0 | 10.5167 | S | Third | woman | False | NaN | Southampton | no | True | adult | female |
883 | 0 | 2 | male | 28.0 | 0 | 0 | 10.5000 | S | Second | man | True | NaN | Southampton | no | True | adult | male |
884 | 0 | 3 | male | 25.0 | 0 | 0 | 7.0500 | S | Third | man | True | NaN | Southampton | no | True | adult | male |
885 | 0 | 3 | female | 39.0 | 0 | 5 | 29.1250 | Q | Third | woman | False | NaN | Queenstown | no | False | adult | female |
886 | 0 | 2 | male | 27.0 | 0 | 0 | 13.0000 | S | Second | man | True | NaN | Southampton | no | True | adult | male |
887 | 1 | 1 | female | 19.0 | 0 | 0 | 30.0000 | S | First | woman | False | B | Southampton | yes | True | child | child |
888 | 0 | 3 | female | NaN | 1 | 2 | 23.4500 | S | Third | woman | False | NaN | Southampton | no | False | child | child |
889 | 1 | 1 | male | 26.0 | 0 | 0 | 30.0000 | C | First | man | True | C | Cherbourg | yes | True | adult | male |
890 | 0 | 3 | male | 32.0 | 0 | 0 | 7.7500 | Q | Third | man | True | NaN | Queenstown | no | True | adult | male |
fillna 메서드
fillna()
는 NaN 값을 원하는 값으로 변환한다.
# 데이터 프레임 생성
temp = pd.DataFrame({
'A': [1, 3, 4, 3, "a"],
'B': ["a", "b", "c", "d", "f"],
'C': [1, 5, 2, 4, 4]
})
# 각 열별 데이터 갯수
temp2 = temp.apply(pd.value_counts)
temp2
A | B | C | |
---|---|---|---|
1 | 1.0 | NaN | 1.0 |
2 | NaN | NaN | 1.0 |
3 | 2.0 | NaN | NaN |
4 | 1.0 | NaN | 2.0 |
5 | NaN | NaN | 1.0 |
a | 1.0 | 1.0 | NaN |
b | NaN | 1.0 | NaN |
c | NaN | 1.0 | NaN |
d | NaN | 1.0 | NaN |
f | NaN | 1.0 | NaN |
temp2.fillna("결측")
A | B | C | |
---|---|---|---|
1 | 1 | 결측 | 1 |
2 | 결측 | 결측 | 1 |
3 | 2 | 결측 | 결측 |
4 | 1 | 결측 | 2 |
5 | 결측 | 결측 | 1 |
a | 1 | 1 | 결측 |
b | 결측 | 1 | 결측 |
c | 결측 | 1 | 결측 |
d | 결측 | 1 | 결측 |
f | 결측 | 1 | 결측 |
- NaN 값이 모두 지정한 “결측”이라는 문자로 변경되었다.
연습 문제 4.4.5
타이타닉호의 승객 중 나이를 명시하지 않은 고객은 나이를 명시한 고객의 평균 나이 값이 되도록 titanic 데이터프레임을 고쳐라.
# 현재 고객의 평균나이
mean_age = titanic["age"].mean()
# 시리즈에서도 fillna는 적용 되므로 다음과 같이 작성
titanic["age"] = titanic["age"].fillna(mean_age)
titanic.isna().sum()
survived 0
pclass 0
sex 0
age 0
sibsp 0
parch 0
fare 0
embarked 2
class 0
who 0
adult_male 0
deck 688
embark_town 2
alive 0
alone 0
adult/child 0
category1 0
dtype: int64
- age의 결측값이 0으로 모두 대체되었음을 확인 가능하다.
astype 메서드
astype()
은 판다스 시리즈, 데이터 프레임의 자료형을 변경해준다.
일반적인 int()
, str()
등을 사용하면 에러가 발생한다.
temp2.fillna(0)
A | B | C | |
---|---|---|---|
1 | 1.0 | 0.0 | 1.0 |
2 | 0.0 | 0.0 | 1.0 |
3 | 2.0 | 0.0 | 0.0 |
4 | 1.0 | 0.0 | 2.0 |
5 | 0.0 | 0.0 | 1.0 |
a | 1.0 | 1.0 | 0.0 |
b | 0.0 | 1.0 | 0.0 |
c | 0.0 | 1.0 | 0.0 |
d | 0.0 | 1.0 | 0.0 |
f | 0.0 | 1.0 | 0.0 |
# astype을 이용해 자료형 변경
temp2.fillna(0).astype(int)
A | B | C | |
---|---|---|---|
1 | 1 | 0 | 1 |
2 | 0 | 0 | 1 |
3 | 2 | 0 | 0 |
4 | 1 | 0 | 2 |
5 | 0 | 0 | 1 |
a | 1 | 1 | 0 |
b | 0 | 1 | 0 |
c | 0 | 1 | 0 |
d | 0 | 1 | 0 |
f | 0 | 1 | 0 |
- 기존 float형이 int형으로 잘 변경되었다.
연습 문제 4.4.6
타이타닉호의 승객에 대해 나이와 성별에 의한 카테고리 열인 category2
열을 만들어라.
category2
카테고리는 다음과 같이 정의된다.
-
성별을 나타내는 문자열
male
또는female
로 시작한다. -
성별을 나타내는 문자열 뒤에 나이를 나타내는 문자열이 온다.
-
예를 들어 27살 남성은
male27
값이 된다.
# 방법 1 - astype()
titanic["category2"] = titanic["sex"] + titanic["age"].astype(int).astype(str)
titanic[["category2"]]
category2 | |
---|---|
0 | male22 |
1 | female38 |
2 | female26 |
3 | female35 |
4 | male35 |
... | ... |
886 | male27 |
887 | female19 |
888 | female29 |
889 | male26 |
890 | male32 |
891 rows × 1 columns
# 방법 2 - apply()
titanic["category2"] = titanic.apply(lambda x: x.sex + str(int(x.age)), axis = 1)
titanic[["category2"]]
category2 | |
---|---|
0 | male22 |
1 | female38 |
2 | female26 |
3 | female35 |
4 | male35 |
... | ... |
886 | male27 |
887 | female19 |
888 | female29 |
889 | male26 |
890 | male32 |
891 rows × 1 columns
실수 값을 카테고리 값으로 변환
ages = [0, 2, 10, 21, 23, 37, 31, 61, 20, 41, 32, 101]
bins = [1, 20, 30, 50, 70, 100]
labels = ["미성년자", "청년", "중년", "장년", "노년"]
cats = pd.cut(ages, bins, labels=labels)
cats
[NaN, '미성년자', '미성년자', '청년', '청년', ..., '장년', '미성년자', '중년', '중년', NaN]
Length: 12
Categories (5, object): ['미성년자' < '청년' < '중년' < '장년' < '노년']
-
pd.cut()
은 데이터를 지정한 구간(bins)에 따라 지정한 라벨(labels)로 변경해준다. -
만약 데이터 값 중 지정한 구간외에 값이 있다면 NaN을 반환한다.
# Categorical 클래스
type(cats)
pandas.core.arrays.categorical.Categorical
pd.cut()
으로 생성한 객체는 pandas.core.arrays.categorical.Categorical 클래스로 지정되어 있다.
cats.categories
Index(['미성년자', '청년', '중년', '장년', '노년'], dtype='object')
categories
속성을 이용해서 라벨 문자열 종류를 확인 가능하다.
cats.codes
array([-1, 0, 0, 1, 1, 2, 2, 3, 0, 2, 2, -1], dtype=int8)
-
codes
속성을 이용해서 각 데이터가 몇 번째 라벨의 값인지 위치를 확인 가능하다. -
이 예제에선 라벨이 [“미성년자”, “청년”, “중년”, “장년”, “노년”, NaN]으로 설정돼 미성년자는 0으로 반환된다.
# 데이터 프레임 생성
df4 = pd.DataFrame(ages, columns=["ages"])
# 카테고리 값 추가
df4["age_cat"] = pd.cut(df4.ages, bins, labels=labels)
print("age_cat 자료형:", df4.age_cat.dtype)
df4
age_cat 자료형: category
ages | age_cat | |
---|---|---|
0 | 0 | NaN |
1 | 2 | 미성년자 |
2 | 10 | 미성년자 |
3 | 21 | 청년 |
4 | 23 | 청년 |
5 | 37 | 중년 |
6 | 31 | 중년 |
7 | 61 | 장년 |
8 | 20 | 미성년자 |
9 | 41 | 중년 |
10 | 32 | 중년 |
11 | 101 | NaN |
-
조금 더 응용하면 위 예시와 같이 데이터 프레임에 카테고리 열을 추가 가능하다.
-
물론
apply()
로 if문을 이용해서도 생성가능하지만 구간이 클수록 코드가 길어질 것 이다. -
마지막으로 age_cat은
pd.cut()
으로 생성한 열이므로 자료형은 category이다.
df4.age_cat.astype(str) + "-" + df4.ages.astype(str)
0 nan-0
1 미성년자-2
2 미성년자-10
3 청년-21
4 청년-23
5 중년-37
6 중년-31
7 장년-61
8 미성년자-20
9 중년-41
10 중년-32
11 nan-101
dtype: object
- 앞서 자료형이 category였으므로 문자형으로 바꾸고 싶다면
astype()
을 사용한다.
data = np.random.randn(1000)
# qcut은 데이터 갯수가 동일하게 끔 N개의 구간으로 나눔
cats = pd.qcut(data, 4, labels=["Q1", "Q2", "Q3", "Q4"])
cats
['Q2', 'Q1', 'Q2', 'Q3', 'Q1', ..., 'Q1', 'Q1', 'Q4', 'Q4', 'Q2']
Length: 1000
Categories (4, object): ['Q1' < 'Q2' < 'Q3' < 'Q4']
-
pd.qcut()
은pd.cut()
과 비슷하지만 구간을 직접 지정하지 않고 구간 수를 지정한다. -
명령어를 실행하면 각 구간별로 데이터의 갯수가 같게끔 데이터를 나눈다.
# 데이터 갯수가 동일한 것 확인
pd.value_counts(cats)
Q4 250
Q3 250
Q2 250
Q1 250
dtype: int64
연습 문제 4.4.7
타이타닉호 승객을 ‘미성년자’, ‘청년’, ‘중년’, ‘장년’, ‘노년’ 나이 그룹으로 나눈다.
bins = [1, 20, 30, 50, 70, 100]
labels = ["미성년자", "청년", "중년", "장년", "노년"]
그리고 각 나이 그룹의 승객 비율을 구한다. 비율의 전체 합은 1이 되어야 한다.
# 구간, 라벨 설정
bins = [1, 20, 30, 50, 70, 100]
labels = ["미성년자", "청년", "중년", "장년", "노년"]
# 그룹 추가
titanic["age_g"] = pd.cut(titanic.age, bins, labels = labels).astype(str)
titanic[ ["age", "age_g"] ]
age | age_g | |
---|---|---|
0 | 22.000000 | 청년 |
1 | 38.000000 | 중년 |
2 | 26.000000 | 청년 |
3 | 35.000000 | 중년 |
4 | 35.000000 | 중년 |
... | ... | ... |
886 | 27.000000 | 청년 |
887 | 19.000000 | 미성년자 |
888 | 29.699118 | 청년 |
889 | 26.000000 | 청년 |
890 | 32.000000 | 중년 |
891 rows × 2 columns
# 시리즈로 분리 - 그룹별 갯수
temp4 = titanic["age_g"].value_counts()
# 비율 생성
temp5 = temp4 / temp4.sum()
temp5
청년 0.456790
중년 0.270483
미성년자 0.185185
장년 0.066218
nan 0.015713
노년 0.005612
Name: age_g, dtype: float64
# 비율 합계
print("비율 합계:", temp5.sum())
비율 합계: 1.0
연습 문제 4.4.8
타이타닉호의 승객에 대해 나이와 성별에 의한 카테고리 열인 category3
열을 만들어라.
category3
카테고리는 다음과 같이 정의된다.
-
20살 미만이면 성별에 관계없이 “미성년자”라고 한다.
-
20살 이상이면 나이에 따라 “청년”, “중년”, “장년”, “노년”을 구분하고 그 뒤에 성별을 나타내는 “남성”, “여성”을 붙인다.
# 구간, 라벨 설정
bins = [20, 30, 50, 70, 100]
labels = ["청년", "중년", "장년", "노년"]
# age_g는 연습문제 4.4.7에서 생성
titanic["category3"] = titanic.apply(lambda x: "미성년자" if x.age < 20
else x.age_g + ": " + "남성" if x.sex == "male"
else x.age_g + ": " + "여성", axis=1)
titanic[["age","age_g","category3"]].tail()
age | age_g | category3 | |
---|---|---|---|
886 | 27.000000 | 청년 | 청년: 남성 |
887 | 19.000000 | 미성년자 | 미성년자 |
888 | 29.699118 | 청년 | 청년: 여성 |
889 | 26.000000 | 청년 | 청년: 남성 |
890 | 32.000000 | 중년 | 중년: 남성 |
Leave a comment