본문 바로가기

😀 기초/판다스(Pandas)

4.4 데이터프레임의 데이터 조작

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