4.4 데이터프레임의 데이터 조작
1. 데이터프레임의 데이터 조작
- 판다스는 넘파이 2차원 배열에서 가능한 대부분의 데이터 처리가 가능
- 추가로 데이터 처리 및 변환을 위한 다양한 함수와 메서드를 제공
2. #### 데이터 갯수 세기
- 가장 간단한 데이터 분석은 데이터의 갯수를 세는 것이다.
- count 메서드를 사용한다.
- NaN 값은 세지 않는다.
s = pd.Series(range(10))
s[3] = np.nan
s
#결과
0 0.0
1 1.0
2 2.0
3 NaN
4 4.0
5 5.0
6 6.0
7 7.0
8 8.0
9 9.0
dtype: float64
s.count()
#결과
9
- 데이터프레임에서는 각 열마다 별도로 데이터 갯수를 센다.
- 데이터에서 값이 누락된 부분을 찾을 때 유용하다.
np.random.seed(2)
df = pd.DataFrame(np.random.randint(5, size=(4,4)), dtype=float)
df.iloc[2, 3] = np.nan
df
#결과
0 1 2 3
0 0.0 0.0 3.0 2.0
1 3.0 0.0 2.0 1.0
2 3.0 2.0 4.0 NaN
3 4.0 3.0 4.0 2.0
df.count()
0 4
1 4
2 4
3 3
dtype: int64
- 다음 명령으로 타이타닉호의 승객 데이터를 데이터프레임으로 읽어올 수 있다.
- 이 명령을 실행하려면 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
3. #### 카테고리 값 세기
- 시리즈의 값이 정수, 문자열, 카테고리 값인 경우에는
- value_counts 메서드로 각각의 값이 나온 횟수를 셀 수 있다.
np.random.seed(1)
s2 = pd.Series(np.random.randint(6, size=100))
s2.tail()
#결과
95 4
96 5
97 2
98 4
99 3
dtype: int32
s2.value_counts()
#결과
1 22
0 18
4 17
5 16
3 14
2 13
dtype: int64
# 데이터프레임에는 value_counts 메서드가 없으므로 각 열마다 별도로 적용해야 한다.
df[0].value_counts()
#결과
3.0 2
0.0 1
4.0 1
Name: 0, dtype: int64
#결과
s2.value_counts()
1 22
0 18
4 17
5 16
3 14
2 13
dtype: int64
# 데이터프레임에는 value_counts 메서드가 없으므로
# 각 열마다 별도로 적용해야 한다.
df[0].value_counts()
#결과
3.0 2
0.0 1
4.0 1
Name: 0, dtype: int64
4. 정렬
- 데이터를 정렬하려면 sort_index 메서드 sort_valies 메서드를 사용
- sort_index 메서드는 인덱스 값을 기준으로,
- sort_valies 메서드는 데이터 값을 기준으로 정렬한다.
- 앞에서 s2 시리즈의 각 데이터 값에 따른 데이터 갯수를 인덱스에 따라 정렬하려면
- 다음처럼 sort_idex를 적용한다
s2
#결과
0 5
1 3
2 4
3 0
4 1
..
95 4
96 5
97 2
98 4
99 3
Length: 100, dtype: int32
s2.value_counts().sort_index()
#결과
0 18
1 22
2 13
3 14
4 17
5 16
dtype: int64
# NaN값이 있는 경우에는 정렬하면 NaN값이 가장 나중으로 간다.
s.sort_values()
#결과
0 0.0
1 1.0
2 2.0
4 4.0
5 5.0
6 6.0
7 7.0
8 8.0
9 9.0
3 NaN
dtype: float64
# 큰 수에서 작은 수로 반대 방향 정렬하려면
# ascending=False 인수를 지정한다.
s.sort_values(ascending=False)
#결과
9 9.0
8 8.0
7 7.0
6 6.0
5 5.0
4 4.0
2 2.0
1 1.0
0 0.0
3 NaN
dtype: float64
# 데이터프레임에서 sort_valies 메서드를 사용하려면
# by 인수로 정렬 기준이 되는 열을 지정해 주어야 한다.
df.sort_values(by=1)
#결과
0 1 2 3
0 0.0 0.0 3.0 2.0
1 3.0 0.0 2.0 1.0
2 3.0 2.0 4.0 NaN
3 4.0 3.0 4.0 2.0
# by 인수에 리스트 값을 넣으면 이 순서대로 정렬 기준의 우선 순위가 된다.
# 즉, 리스트의 첫번째 열을 기준으로 정렬한 후 동일한 값이 나오면
# 그 다음 열로 순서를 따지게 된다.
df.sort_values(by=[1, 2])
#결과
0 1 2 3
1 3.0 0.0 2.0 1.0
0 0.0 0.0 3.0 2.0
2 3.0 2.0 4.0 NaN
3 4.0 3.0 4.0 2.0
5. ### 행/열 합계
---
- 행과 열의 합계를 구할 때는 sum(axis) 메서드를 사용한다.
- axis 인수에는 합계로 인해 없어지는 방향축(0=행, 1=열)을 지정한다.
np.random.seed(1)
df2 = pd.DataFrame(np.random.randint(10, size=(4,8)))
df2
#결과
0 1 2 3 4 5 6 7
0 5 8 9 5 0 0 1 7
1 6 9 2 4 5 2 4 2
2 4 7 7 9 1 7 0 6
3 9 9 7 6 9 1 0 1
# 행방향 합계를 구할 때는 sum(axis=1) 메서드를 사용한다.
df2.sum(axis=1)
#결과
0 35
1 34
2 41
3 42
dtype: int64
df2["RowSum"] = df2.sum(axis=1)
df2
#결과
0 1 2 3 4 5 6 7 RowSum
0 5 8 9 5 0 0 1 7 70
1 6 9 2 4 5 2 4 2 68
2 4 7 7 9 1 7 0 6 82
3 9 9 7 6 9 1 0 1 84
# 열 합계를 구할 때는 sum(axis=0) 메서드를 사용하는데
# axis인수의 디폴트 값이 0이므로 axis인수를 생략할 수 있다.
df2.sum()
#결과
0 24
1 33
2 25
3 24
4 15
5 10
6 5
7 16
RowSum 304
dtype: int64
df2.loc["ColTotal", :] = df2.sum()
df2
#결과
0 1 2 3 4 5 6 7 RowSum
0 5.0 8.0 9.0 5.0 0.0 0.0 1.0 7.0 70.0
1 6.0 9.0 2.0 4.0 5.0 2.0 4.0 2.0 68.0
2 4.0 7.0 7.0 9.0 1.0 7.0 0.0 6.0 82.0
3 9.0 9.0 7.0 6.0 9.0 1.0 0.0 1.0 84.0
ColTotal 24.0 33.0 25.0 24.0 15.0 10.0 5.0 16.0 304.0
- mean 메서드는 평균을 구하며 sum 메서드와 사용법이 같다.
6. #### apply 변환
---
- 행이나 열 단위로 더 복잡한 처리를 하고 싶을 때는 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
# 예를 들어 각 열의 최대값과 최소값의 차이를 구하고 싶다면
# 다음과 같은 람다 함수를 넣는다.
df3.apply(lambda x: x.max() - x.min())
#결과
A 3
B 2
C 4
dtype: int64
# 만약 행에 대해 적용하고 싶으면 axis=1 인수를 쓴다.
df3.apply(lambda x : x.max() - x.min(), axis=1)
#결과
0 1
1 2
2 3
3 2
4 1
dtype: int64
# 각 열에 대해 어떤 값이 얼마나 사용되었는지 알고 싶다면
# value_counts 함수를 넣으면 된다.
df3.apply(pd.value_counts)
#결과
A B C
1 1.0 1.0 1.0
2 NaN 2.0 1.0
3 2.0 2.0 NaN
4 2.0 NaN 2.0
5 NaN NaN 1.0
# 다음과 같이 타이타닉호의 승객 중 나이가20살을 기준으로
# 성인(adult)과 미성년자(child)를 구별하는 라벨 열을 만들 수 있다.
titanic["adult/child"] = titanic.apply(lambda r: "adult" if r.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
7. #### fillna 메서드
---
- NaN값은 fillna 메서드를 사용하여 원하는 값으로 바꿀 수 있다.
df3.apply(pd.value_counts).fillna(0).astype(int)
A B C
1 1 1 1
2 0 2 1
3 2 2 0
4 2 0 2
5 0 0 1
8. #### 실수 값을 카테고리 값으로 변환
---
- 실수 값을 크기 기준으로 하여 카테고리 값으로 변환하고 싶을 때는 다음과 같이 명령
- cut: 실수 값의 경계선을 지정하는 경우
- qcut : 갯수가 똑같은 구간을 나누는 경우
- 예를 들어 다음과 같이 나이 데이터가 있다고 하자.
ages = [0, 2, 10, 21, 23, 37, 31, 61, 20, 41, 32, 101]
- cut 명령을 사용하면 실수값을 다음처럼 카테고리 값으로 바꿀 수 있다.
- bins 인수는 카테고리를 나누는 기준값이 된다.
- 영역을 넘는 값은 NaN으로 처리된다.
bins = [1, 20, 30, 50, 70, 100]
labels = ["미성년자", "청년", "중년", "장년", "노년"]
cats = pd.cut(ages, bins, labels=labels)
cats
#결과
[NaN, '미성년자', '미성년자', '청년', '청년', ..., '장년', '미성년자', '중년', '중년', NaN]
Length: 12
Categories (5, object): ['미성년자' < '청년' < '중년' < '장년' < '노년']
- cut 명령이 반환하는 값은 Categorical 클래스의 객체
- categories 속성으로 라벨 문자열을, codes 속성으로 정수로 인코딩한 카테고리 값을 가진다.
type(cats)
pandas.core.arrays.categorical.Categorical
cats.categories
Index(['미성년자', '청년', '중년', '장년', '노년'], dtype='object')
df4 = pd.DataFrame(ages, columns=["ages"])
df4["age_cat"] = pd.cut(df4.ages, bins, labels=labels)
df4
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
# 따라서 위 데이터프레임의 age_cat 열값은 문자열이 아니다.
# 이를 문자열로 만들려면 astype 메서드를 사용해야 한다.
df4.age_cat.astype(str) + df4.ages.astype(str)
0 nan0
1 미성년자2
2 미성년자10
3 청년21
4 청년23
5 중년37
6 중년31
7 장년61
8 미성년자20
9 중년41
10 중년32
11 nan101
dtype: object
- qcut 명령은 구간 경계선을 지정하지 않고
- 데이터 갯수가 같도록 지정한 수의 구간을 나눈다.
- 예를 들어 다음 코드는 1000개의 데이터를 4개의 구간으로 나누는데
- 각 구간은 250개씩의 데이터를 가진다.
data = np.random.randn(1000)
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.value_counts(cats)
Q1 250
Q2 250
Q3 250
Q4 250
dtype: int64
'😀 기초 > 판다스(Pandas)' 카테고리의 다른 글
4.6 데이터프레임 합성 (0) | 2022.02.04 |
---|---|
4.5 데이터프레임 인덱스 조작 (0) | 2022.02.02 |
4.3 데이터프레임 고급 인덱싱 (0) | 2022.01.30 |
4.2 데이터 입출력 (0) | 2022.01.28 |
4.1장 판다스 패키지의 소개(4.1_데이터프레임 클래스) (0) | 2022.01.26 |