데이터 분석/판다스

[Pandas] 개요

eunnys 2023. 11. 9. 15:06

Pandas 개요


- pandas는 데이터 조작 및 분석을 위해 파이썬 프로그래밍 언어로 작성된 소프트웨어 라이브러리이다.
- 일명 파이썬의 엑셀이라 부른다.
- URL : https://pandas.pydata.org

 

pandas - Python Data Analysis Library

pandas pandas is a fast, powerful, flexible and easy to use open source data analysis and manipulation tool, built on top of the Python programming language. Install pandas now!

pandas.pydata.org

 
 

Pandas 불러오기


-pandas는 일반적으로 pd라는 별칭으로 불러온다.

import pandas as pd
import numpy as np

 
 

Pandas에서 사용하는 자료 구조


    **pandas의 구성 요소**

    (1) Series
        - DataFrame 중에서 하나의 column에 해당하는 데이터
        - 1차원 데이터 (index, values 2가지 요소로 구성)

    (2) DataFrame
        - Data Table 전체를 의미하는 데이터
        - 2차원 데이터 (index, values, columns 3가지 요소로 구성)

    (3) Index
        - Series, DataFrame의 인덱스를 구성하는 데이터
 

 

 
 
 
▶ 시리즈(Series)
 
- 인덱스 값을 명시적으로 지정하지 않으면 0부터 시작하는 일련번호로 자동 지정된다.
 

 

nums = [2,3,5,7,9]
sr = pd.Series(nums)

print(sr)
print(type(sr))
0    2
1    3
2    5
3    7
4    9
dtype: int64
<class 'pandas.core.series.Series'>

 
 
- 인덱스를 직접 지정할 수도 있다.
 

sr = pd.Series(nums, index=list('abcde'))
print(sr)
print('-'*20)
menu_list = ['토스트', '제육볶음', '치킨']
index_list = ['아침', '점심', '저녁']
sr = pd.Series(menu_list, index=index_list)
print(sr)
a    2
b    3
c    5
d    7
e    9
dtype: int64
--------------------
아침     토스트
점심    제육볶음
저녁      치킨
dtype: object

# 넘파이의 다차원 배열 형태로 반환
print(sr.index.values) 
print(sr.values)
['아침' '점심' '저녁']
['토스트' '제육볶음' '치킨']

 
 
- 딕셔너리를 이용해서 series 생성하기
 

dic_data = {'a':21, 'b':22, 'c':23, 'd':24, 'e':25}
sr = pd.Series(dic_data, dtype=np.float32)
print(sr)
a    21.0
b    22.0
c    23.0
d    24.0
e    25.0
dtype: float32

원소 선택
 

print(sr[0]) # 인덱스 값으로 가져오기
print(sr['a']) # 인덱스 이름으로 가져오기
print(sr['a':'c']) # 이름으로 가져올 땐 마지막 요소 포함
print(sr[0:3]) # 인덱스 값으로 가져올 땐 마지막 요소 포함하지 않음
21.0
21.0
a    21.0
b    22.0
c    23.0
dtype: float32
a    21.0
b    22.0
c    23.0
dtype: float32

unique(), value_counts()
unique() : 결측치를 포함하며 중복된 데이터를 제외한 데이터의 종류를 ndarray로 반환한다.
value_counts() : 결측치를 포함하지 않으며 데이터 종류의 개수를 series로 반환한다.
 

sr = pd.Series(['A', 'B', 'A', np.NaN, 'C', 'D', 'D', 'A'])
print(sr.unique())
print(sr.value_counts())
['A' 'B' nan 'C' 'D']
A    3
D    2
B    1
C    1
Name: count, dtype: int64

데이터프레임(DataFrame)
 




print(df.index)
print(df.columns)
print(df.values)
Index(['아침', '점심', '저녁'], dtype='object')
Index(['월', '화', '수'], dtype='object')
[['토스트' '시리얼' '스크램블']
 ['제육볶음' '칼국수' '육개장']
 ['치킨' '삼겹살' '라면']]

 
 
- 딕셔너리를 이용한 데이터프레임 생성
 

data = {
    '이름':['홍길동','전우치','손오공','사오정','저팔계'],
    '나이':[32, 27, 30, 31, 33],
    '전화번호':['010-111-1111','010-222-2222','010-333-3333','010-444-4444','010-555-5555']
}
df = pd.DataFrame(data, index=np.arange(1, 6))
display(df)


데이터 조회 및 처리

 

import numpy as np
import pandas as pd

data = np.random.randint(100, size=(10,10)) # 0이상 100미만의 10행10열의 정수 난수 발생
df = pd.DataFrame(data, index=list('abcdefghij'), columns=list('ABCDEFGHIJ'))
display(df)


데이터 조회
 
○  열의 값 읽기
df[열명] df.열명
 

print(df.A) # A컬럼의 값을 시리즈로 반환
print(df['A'])
a    10
b    85
c    28
d    72
e    80
f    78
g    51
h    55
i    33
j    77
Name: A, dtype: int32

# 여러 개의 열 값 읽기
print(df[['A', 'B']]) # 리스트의 형태로 전달하면 데이터 프레임으로 반환
 A   B
a  10  95
b  85  49
c  28   6
d  72  35
e  80  82
f  78  35
g  51  12
h  55  80
i  33  19
j  77  27

 
 
행의 값 읽기
- df.loc[행명]
 

df.loc['a']
A    10
B    95
C    61
D    32
E     7
F    45
G    74
H    81
I    60
J    12
Name: a, dtype: int32

# 여러 개의 행 값 읽기
df.loc[['a','c','e']]

 
 
df.head(), df.tail()
- 상위(또는 하위) n개의 행을 선택한다.
- 숫자를 지정하지 않으면 기본값으로 5개의 행을 선택한다.
 

 
 
df.sample()
- n개의 랜덤 데이터 추출
 


데이터프레임 정보 조회
 
○ df.dtypes
- 컬럼별 데이터 타입 조회

print(df.dtypes)
A    int32
B    int32
C    int32
D    int32
E    int32
F    int32
G    int32
H    int32
I    int32
J    int32
dtype: object

 
 
df.shape
- 데이터프레임의 모양 확인

print(df.shape) # (10, 10)

 
 
○ df.count()
- 데이터의 열마다 non-NA 레코드의 개수를 시리즈 형태로 반환

df.count() # 결측치는 포함하지 않음
A    10
B    10
C    10
D    10
E    10
F    10
G    10
H    10
I    10
J    10
dtype: int64
 

 
○ df.describe()
- 데이터프레임의 기술 통계 정보 요약

print(df.describe())
 A          B          C          D          E          F  \
count  10.000000  10.000000  10.000000  10.000000  10.000000  10.000000   
mean   56.900000  44.000000  53.200000  45.900000  58.300000  55.400000   
std    25.916318  31.464265  24.580028  26.421582  34.172926  28.347839   
min    10.000000   6.000000  12.000000  13.000000   7.000000  17.000000   
25%    37.500000  21.000000  33.750000  29.000000  32.000000  35.000000   
50%    63.500000  35.000000  61.500000  43.000000  66.000000  45.500000   
75%    77.750000  72.250000  63.750000  54.750000  86.500000  81.500000   
max    85.000000  95.000000  91.000000  89.000000  97.000000  95.000000   

               G         H          I          J  
count  10.000000  10.00000  10.000000  10.000000  
mean   63.000000  52.30000  63.400000  35.700000  
std    28.538473  26.52902  16.063762  25.412377  
min    17.000000  21.00000  41.000000   3.000000  
25%    39.500000  31.00000  53.000000  15.000000  
50%    73.500000  47.00000  61.000000  32.500000  
75%    83.500000  76.75000  75.500000  47.750000  
max    93.000000  94.00000  86.000000  77.000000

 
 
○ df.info()
- 데이터프레임의 각 컬럼의 타입 및 non-null count의 개수 확인

print(df.info())
<class 'pandas.core.frame.DataFrame'>
Index: 10 entries, a to j
Data columns (total 10 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   A       10 non-null     int32
 1   B       10 non-null     int32
 2   C       10 non-null     int32
 3   D       10 non-null     int32
 4   E       10 non-null     int32
 5   F       10 non-null     int32
 6   G       10 non-null     int32
 7   H       10 non-null     int32
 8   I       10 non-null     int32
 9   J       10 non-null     int32
dtypes: int32(10)
memory usage: 780.0+ bytes
None

인덱싱, 슬라이싱
 

df[['A','C']]

 
 
- 열의 순서를 이용해서 인덱싱을 할 수 없다.

# df[0] 에러 발생

 
 
- 열 위치 값을 이용해서 인덱싱이나 슬라이싱을 이용하려면 columns 속성을 이용한다.

df.columns # 컬럼의 값을 ndarray로 반환
# Index(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'], dtype='object')

df.columns[0] # 위치 값을 이용해서 컬럼명을 가져올 수 있다
# 'A'

df[df.columns[2:4]]

 
 
- 행의 이름을 이용해서 슬라이싱을 할 수 있다.
- 마지막 요소까지 범위에 포함된다,

# df['a':'c'] 동일한 결과
# 행단위 추출은 loc 속성을 이용한다
df.loc['a':'c']

 
 
- 행의 순서를 이용해서 슬라이싱을 할 수 있다.
- df.iloc 속성 사용

df.iloc[2:5]


특정 위치의 데이터 추출
 
df.at, df.iat
- 라벨 또는 인덱스를 이용해서 특정 위치의 데이터를 가져온다. (단일값 추출)

df.at['c','E'] # c행E열
df.iat[4,4] # 위치 값

 
 
df.loc, df.iloc
- 행과 열의 라벨 또는 인덱스를 이용해서 특정 위치의 데이터를 조회한다.

df.loc[['e','a','c'], ['C','C','A']]


df.iloc[0:5:2, 5:10:2]  # (시작:끝:스텝)


조건 인덱싱
 

# 데이터프레임의 A열의 요소 중 50을 초과하는 값이 있을 때 해당 값의 행을 선택
df[df['A'] > 50]


# 데이터프레임의 b행의 요소 중 50을 초과하는 값이 있을 때 해당 값의 열을 선택
df.loc[:, df.loc['b'] > 50]


df[df % 2 == 0] # 짝수 값만 표시


df2 = df.copy()

# 새로운 컬럼 추가
df2['K'] = ['one','two','one','two','one','two','one','two','one','two']

df2


# K컬럼의 one이 포함된 값만 가져오기
df2['K'].isin(['one']) # 마스킹 값
a     True
b    False
c     True
d    False
e     True
f    False
g     True
h    False
i     True
j    False
Name: K, dtype: bool

# 마스킹 값을 인덱스 값으로 넣어줌
df2[df2['K'].isin(['one'])]


# 특정 값을 포함하지 않는 레코드 검색 : ~ 연산자 사용
df2[~df2['K'].isin(['one'])]


데이터 수정
 

df['E'] = 0
df


# 특정 요소 삭제하기
df.drop('E', axis=0, inplace=True)
df.at['e','E'] = 1
df


df.loc['d':'f'] = 0
df


np.random.seed(0)
df = pd.DataFrame(np.random.randint(100, size=(10,10)),
                 index=list('abcdefghij'), columns=list('ABCDEFGHIJ'))
display(df)


데이터 정렬
 
df.sort_values()
- 행 또는 열을 지정해서 값에 따라 정렬한다.
- 기본 값은 열을 지정해서 값을 따라 오름차순으로 정렬하는 것이다.
- 내림차순으로 정렬하려면 ascending=False
- 행을 지정해서 정렬하려면 axis=1 또는 axis=columns
 

# A컬럼을 기준으로 오름차순 정렬
df.sort_values('A')


# A컬럼을 기준으로 내림차순 정렬
df.sort_values('A', ascending=False)


# a행을 기준으로 오름차순 정렬
df.sort_values('a', axis=1)

 
 
○ df.sort_index()
- 행 또는 열 값을 기준으로 정렬한다.
- 기본값은 인덱스(행)를 오름차순으로 정렬하는 것이다.
- 컬럼명에 따라 정렬하려면 axis=1
 

# 행 기준 내림차순
df.sort_index(ascending=False)


# 컬럼명을 기준으로 내림차순 정렬
df.sort_index(axis=1, ascending=False)


데이터프레임 조작

 
새로운 행 또는 열 추가

DataFrame에 새로운 추가
- df.[새로운 컬럼명] = 데이터 목록
- 데이터의 목록은 Dataframe내 다른 컬럼들의 원소와 같은 개수이어야 한다.
- 데이터 목록 타입 : list, Series, ndarray

DataFrame에 새로운 추가
- df.loc[새로운 행이름] = 데이터 목록
- 데이터 목록은 기존 DataFrame의 컬럼의 개수와 `타입`이 같아야 한다.
 

import numpy as np
import pandas as pd

df = pd.DataFrame([[1,2,3],[7,8,9],[13,14,15]],
                 index=list('ABC'), columns=list('abc'))
df


# 새로운 행 추가
df.loc['D'] = [19,20,21]
df


# 새로운 열 추가
df['d'] = [4,10,16,22]
df

 
 
Series를 이용해서 새로운 행 또는 추가
- 시리즈 객체를 이용해서 새로운 행 또는 열을 추가할 때는 **기존 데이터프레임과 같은 인덱스를 갖도록** 작성한다.
- 인덱스를 정확하게 명시하지 않으면 추가되는 요소는 NaN으로 초기화 된다.
 

# 인덱스를 명시하지 않을 경우
df2 = df.copy()
df2.loc['F'] = pd.Series([31,32,33,34])
df2


df.loc['F'] = pd.Series([31,32,33,34], index=df.columns)
df

 
 
임의의 위치에 열 삽입
df.insert(위치, 컬럼명, 값)
 

df.insert(1, 'K', [1,1,1,1,1])
df


행과 열의 변경
 

df = pd.DataFrame(np.arange(1,10).reshape(3,3), columns=list('abc')) # 컬럼은 자동생성
df

 
 
○ df.reindex()
새로운 행 또는 을 추가한다.
- 추가되는 행 또는 열의 요소는 NaN으로 초기화 된다. (fill_value 매개변수를 통해 특정 값으로 초기화 할 수 있다)
- 실행 결과는 원본에 영향을 주지 않고 **새로운 데이터프레임 객체를 반환**한다.
 

# 익덱스의 구조를 재정의 ([0,1,2] -> [0,1,2,3], (a,b,c) -> (a,b,c,d), NaN은 0으로 채움)
# 원본엔 영향을 주지 않음
df.reindex(index=[0,1,2,3], columns=list('abcd'), fill_value=0)

 
- 행 또는 열의 순서를 임의로 섞을 수도 있다.
- 기존에 존재하던 라벨을 생략해서 행 또는 열을 삭제할 수도 있다.
 

# 행을 삭제하고 순서 섞기
df.reindex(index=[0,1], columns=['b','a','b'])

 
 
df.set_index()
- 열을 인덱스로 만든다.
df.set_index(열이름, inplace=True), inplace=True : 원본을 변경하는 속성
 


# a컬럼의 값을 인덱스화
df.set_index('a', inplace=True)

 
 
○ df.reset_index()
- 인덱스를 열 데이터로 추가한다.
- 인덱스 초기화 : drop=True 인수를 전달하면 기존 인덱스를 삭제하고 기본값으로 다시 초기화 한다.
 

# 인덱스화 했던 a컬럼을 다시 컬럼으로 추가
df.reset_index()


# 인덱스화 했던 a컬럼 삭제
df.reset_index(drop=True)


행과 열의 삭제
 
- 실행 결과는 원본에 영향을 주지 않고 새로운 데이터프레임을 반환한다. (inplace=False가 기본값)

<열 제거>
del df[컬럼명]
df.drop(컬럼명 목록, axis=1) : 리스트의 형태로 전달해서 여러 개의 컬럼을 동시에 삭제할 수도 있다.
df.drop(columns=컬럼명 목록) : 명확하게 컬럼을 삭제하겠다는 의미이기 때문에 별도의 축 옵션은 필요 없다.


<행 제거>
df.drop(행이름 목록) : 기본값인 axis=0 생략 가능
df.drop(rows=행이름 목록) : 명확하게 행을 삭제하겠다는 의미이기 때문에 별도의 축 옵션은 필요 없다.

df = pd.DataFrame([[1,2],[3,4]], index=['A','B'], columns=['a','b'])
df


# df.drop('a') 에러 발생 (행 삭제로 인지, a행은 존재하지 않음)
df.drop('A') # 행 삭제


df.drop('a', axis=1) # 열 삭제


 행과 열의 이름 바꾸기
 

df = pd.DataFrame(np.random.randint(1, 100, size=(4,4)))
df


# 새로운 컬럼명을 넣어줌
df.columns = ['C1','C2','C3','C4']

# 새로운 인덱스를 넣어줌
df.index = list('abcd')
df

 
 
특정 행 또는 열의 이름만 바꾸기
df.rename(index=dictionary 객체)
df.rename(columns=dictionary 객체)
 

# key 값은 기존 인덱스, value 값은 새로운 인덱스
df.rename(index={'a':'A'}, inplace=True)
df.rename(columns={'C1':'국어'}, inplace=True)
df

 
 
컬럼 이름의 특정 문자 바꾸기
 

df.index = list('ABCD')
df.columns = ['1기 A반', '1기 B반', '2기 A반', '2기 B반']
df

 
 
str 모듈은 문자열을 다루는 함수를 가지고 있다.

# str 모듈을 사용해 컬럼명의 공백을 언더스코어로 바꾸기
df.columns = df.columns.str.replace(' ','_')
df

 
 
컬럼명에 접두사 밑 접미사 붙이기
df.add_prefix() 접두사
df.add_suffix() 접미사
 

df = df.add_prefix('KDM')
df

 


데이터프레임 간의 조합
 

df = pd.DataFrame([[1,2],[3,4]], index=['A','B'], columns=['a','b'])
df

 
pd.concat
- 두 개 이상의 데이터프레임을 행 또는 열 방향으로 연결한다.
 방향으로 연결하고자 할 경우 axis=1 인자를 전달한다. (기본값은 axis=0)
행 방향으로 연결하고자 할 때는 열 이름이 같아야 하고
 방향으로 연결하고자 할 때는 행 이름이 같아야 한다.
 

# 똑같은 데이터프레임을 행 방향으로 연결
df2 = pd.concat([df,df])
df2


# 열 방향으로 연결
df3 = pd.concat([df, df], axis=1)
df3


집계함수

 
- 모든 집계함수는 axis=0을 기본값으로 한다. ( 기준으로 집계)
df.mean() : 각 행과 열에 대한 평균을 산출한다.
df.std() : 각 행과 열에 대한 표준편자를 산출한다.
df.min() / df.max() : 각 행과 열에 대한 최소 / 최대값를 산출한다.
 


[문제] Series, DataFrame 생성 및 연산
- (1) 각 컬럼 값은 Series 객체로 생성
- (2) 앞서 만든 Series 객체를 이용해서 DataFrame 생성
- (3) 각 과목의 합계를 계산한 컬럼 추가
- (4) 각 과목의 평균을 계산한 컬럼 추가
 

index_list = ['홍길동','임꺽정','전우치','손오공','저팔계','사오정']

sr_major = pd.Series(['컴퓨터공학과','수학과','정보통신학과','수학과','컴퓨터공학과','컴퓨터공학과'], index=index_list)
sr_math = pd.Series([97,88,91,76,88,87], index=index_list)
sr_kor = pd.Series([88,89,85,90,88,77], index=index_list)
sr_eng = pd.Series([90,100,96,91,80,90], index=index_list)
score_df = pd.DataFrame({'전공':sr_major, '수학':sr_math, '국어':sr_kor, '영어':sr_eng})
score_df


score_df.shape # (6, 4)
score_df.head()
score_df.info()
score_df.describe() # 수치형 데이터만 보여줌
score_df.describe(include=object) # 명목형 데이터만 보여줌


score_df.loc['임꺽정':'손오공'] # 행 슬라이싱


# 합계 컬럼 추가(loc 사용)
score_df['합계'] = score_df.loc[:,'수학':'영어'].sum(axis=1)
score_df


score_df.drop('합계', axis=1, inplace=True)
score_df


# 합계 컬럼 추가(loc를 사용하지 않고, 복수의 컬럼 목록 가져오기)
score_df['합계'] = score_df[['수학','국어','영어']].sum(axis=1)
# 평균 컬럼 추가
score_df['평균'] = score_df.loc[:,'수학':'영어'].mean(axis=1)
score_df


# 평균 값 포맷 적용하기 (소수점 이하 2자리)
# 시리즈 객체에 apply함수 적용(x에는 평균 컬럼에 있는 값들이 하나씩 들어옴)
score_df['평균'] = score_df['평균'].apply(lambda x: f'{x:.2f}')
score_df


# 평균값이 object 타입으로 바뀜
score_df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 6 entries, 홍길동 to 사오정
Data columns (total 6 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   전공      6 non-null      object
 1   수학      6 non-null      int64 
 2   국어      6 non-null      int64 
 3   영어      6 non-null      int64 
 4   합계      6 non-null      int64 
 5   평균      6 non-null      object
dtypes: int64(4), object(2)
memory usage: 508.0+ bytes

# 평균 값을 다시 수치형으로 바꿔줌
score_df['평균'] = score_df['평균'].astype(float)
score_df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 6 entries, 홍길동 to 사오정
Data columns (total 6 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   전공      6 non-null      object 
 1   수학      6 non-null      int64  
 2   국어      6 non-null      int64  
 3   영어      6 non-null      int64  
 4   합계      6 non-null      int64  
 5   평균      6 non-null      float64
dtypes: float64(1), int64(4), object(1)
memory usage: 508.0+ bytes

결측값 처리

 

import numpy as np
import pandas as pd

data = np.random.randint(0, 10, (5,5))
df = pd.DataFrame(data, index=list('ABCDE'), columns=list('abcde'))
df


# 결측값 설정
df.at['B','c'] = np.nan # B행c열
df.at['D','e'] = np.nan # D행e열
df

 
 
df.isna(), df.notna()
- 데이터가 NaN인지 아닌지 검사
- 결과는 True/False로 반환
 

checked_nan = df.isna()
print(checked_nan)
       a      b      c      d      e
A  False  False  False  False  False
B  False  False   True  False  False
C  False  False  False  False  False
D  False  False  False  False   True
E  False  False  False  False  False

# 각 컬럼별 True값 집계
print(checked_nan.sum())
a    0
b    0
c    1
d    0
e    1
dtype: int64

 
 
df.dropna()
- 결측값을 포함한 행 또는 열을 삭제
- 기본값은 결측값이 존재하는 행을 모두 삭제하는 방식
 

df.dropna()

 
axis=1 인수를 설정하면 결측값이 있는 열을 제거할 수 있다.
 

df.dropna(axis=1)

 
subset 인수를 설정하면 모든 컬럼이 아닌 특정 컬럼의 결측값 데이터만 삭제
 

# c컬럼에 결측값이 있는 행을 제거
df.dropna(subset=['c'])

 
 
○ df.fillna()
- 결측값을 다른 값으로 채운다.
 

# 모든 결측치에 대해 적용
df.fillna(-999)


# 특정 컬럼의 평균값 넣기
df['c'] = df['c'].fillna(df['c'].mean())
df

 
- method 인자를 사용하면 열 기준 바로 앞 또는 뒤에 있는 값으로 채운다.
- method='pad'  or  method='ffill' : 앞의 값으로 채움
- method='backfill'  or  method='bfill' : 뒤의 값으로 채움
 

# D행e열의 NaN값을 앞의 값(C행e열)으로 채움
df.fillna(method='pad')


# D행e열의 NaN값을 뒤의 값(E행e열)으로 채움
df.fillna(method='bfill')


데이터 중복 제거

 
 

df = pd.DataFrame({'k1':['one','two']*3+['two'],
                  'k2':[1, 1, 2, 3, 3, 4, 4]})
df

 


▶ 중복 데이터 검사
df.duplicated() : 각 행의 중복 여부를 검사하여 결과를 True/False로 반환
 

df.duplicated() # 5번과 6번 레코드 중복
0    False
1    False
2    False
3    False
4    False
5    False
6     True
dtype: bool

 


▶ 중복 데이터 삭제
- df.drop_duplicates() : 모든 컬럼에 대해 중복 값을 갖는 행을 제거
 

df.drop_duplicates()

 


지정된 컬럼의 중복 데이터 삭제
- df.drop_duplicates(컬럼명 목록) : 매개변수로 주어진 컬럼별 목록에 대해 같은 값을 갖는 행을 제거
 

df['k3'] = np.arange(7)
df


df.drop_duplicates(['k1','k2']) # 6번 레코드 삭제

 
keep='last' parameter
- drop_duplicates는 기본적으로 처음 발견된 값을 유지한다.
- keep='last' 옵션을 지정하면 마지막으로 발견된 값을 유지한다.
 

df.drop_duplicates(['k1','k2'], keep='last') # 5번 레코드 삭제


■  데이터프레임 재구조화

 
pd.pivot_table(), df.pivot_table()
- 피벗 테이블 : 기존 데이터를 기반으로 합계나 통계를 산출할 목적으로 새로운 표를 만드는 기능
df.pivot_table(index=행방향 컬럼, columns=열방향 컬럼, values=집계대상 컬럼, aggfunc=구할 통계값)
 

df = pd.DataFrame(np.arange(16).reshape(4,4), 
                  index=list('ABCD'), columns=list('abcd'))

year_df = pd.DataFrame([2019, 2020, 2019, 2020], 
                       index=list('ABCD'), columns=['year'])

class_df = pd.DataFrame(list('AABB'), 
                  index=list('ABCD'), columns=['class'])

df = pd.concat([year_df, class_df, df], axis=1)
df


pd.pivot_table(df, index='year', columns='class')


pd.pivot_table(df, index=['year','class']) # 멀티 인덱스

 
 
- 중복된 컬럼의 항목은 하나로 합쳐지고 그 값의 평균을 기본 집계 함수로 이용한다.
 

# 2019년 a열 : 0과 8의 평균 = 4
pd.pivot_table(df, index='year', values=['a','b','c','d'])


# 중복된 컬럼의 합과 평균
pd.pivot_table(df, index='year', values=['a','b','c','d'], aggfunc=[np.sum, np.mean])

 


[실습] 피벗 테이블 실습
 

sr_year = pd.Series([2020]*4+[2021]*4+[2022]*4)
sr_quarter = pd.Series(['1Q','2Q','3Q','4Q']*3)
np.random.seed(0)
sr_sales = pd.Series(np.random.randint(500, 6000, 12))
sr_cost = pd.Series(np.random.randint(100, 1200, 12))

df = pd.DataFrame({'year':sr_year,'quarter':sr_quarter, 'sales':sr_sales, 'cost':sr_cost})
df


# 'benefit' 컬럼 추가
df['benefit'] = df['sales'] - df['cost']
df


# 피벗 데이블을 이용하여 연도별 분기 이익 데이터 확인
pd.pivot_table(df, index='year', columns='quarter', values='benefit')


df_pivot = pd.pivot_table(df, index=['year','quarter'], values='benefit')
df_pivot


df_pivot.index
MultiIndex([(2020, '1Q'),
            (2020, '2Q'),
            (2020, '3Q'),
            (2020, '4Q'),
            (2021, '1Q'),
            (2021, '2Q'),
            (2021, '3Q'),
            (2021, '4Q'),
            (2022, '1Q'),
            (2022, '2Q'),
            (2022, '3Q'),
            (2022, '4Q')],
           names=['year', 'quarter'])

# 연도별 이익에 대한 평균과 합계
pd.pivot_table(df, index='year', values='benefit', aggfunc=['mean','sum'])

 


다중 인덱스
- index, column에 중첩 배열을 전달해서 다중 인덱스를 설정할 수 있다.

df = pd.DataFrame(np.arange(16).reshape(4,4),
                 index=[[1,2,3,4],['java','python','java','python']],
                 columns=[['1기','1기','2기','2기'],['오전','오후','오전','오후']])
df


# 1기 데이터 가져오기
df['1기']


# 1기의 오후 데이터 가져오기
df['1기','오후']
1  java       1
2  python     5
3  java       9
4  python    13
Name: (1기, 오후), dtype: int32

 


# 1행에서 1기에 해당하는 값만 가져오기
df.loc[1, '1기']


그룹핑


- 특정 값을 기준으로 몇 개의 그룹으로 분할하여 처리하는 방식
 

df = pd.DataFrame({'A':['chol','young']*3+['chol'],
                   'B':['one','one','two','one','two','two','one'],
                   'C':np.random.randn(7),
                   'D':np.random.randn(7)})
df

 

 


그룹 객체 만들기
 

grouped = df.groupby('B')
print(grouped)ㄴ

for key, group in grouped:
    print('key:', key)
    print(group.head())
    print('-'*35)
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000024E27F47B10>
key: one
       A    B         C         D
0   chol  one -0.181583  0.680567
1  young  one  1.410205 -1.563497
3  young  one  0.275198 -0.242150
6   chol  one  0.033439  0.047365
-----------------------------------
key: two
       A    B         C         D
2   chol  two -0.374472 -0.566698
4   chol  two -0.960755  1.514391
5  young  two  0.376927 -0.333057
-----------------------------------

# 특정 그룹만 선택
one_group = grouped.get_group('one')
one_group.head()

 


그룹 연산

그룹별 통계치 구하기
df.groupby(컬럼명)[컬럼명 목록].통계함수()
  - DataFrame 중 지정한 컬럼들에 대해 그룹별 통계치를 구함
  - 통계함수: sum, mean, std, var, min, max, count, quantile 등
 

df.groupby('B')[['C','D']].sum()


data = pd.read_csv('./sales.csv')
data


# 부서별 매출액의 합계 (리스트의 형태로 지정하면 데이터프레임으로 반환)
data.groupby('부서')[['매출액']].sum()


# 부서별 연도별 매출액
data.groupby(['부서','연도'])[['매출액']].sum()


data.pivot_table(index=['부서','연도'], values='매출액', aggfunc='sum')

 


Aggregation
df.groupby(컬럼명)[컬럼명].agg([통계함수1, 통계함수2, ...])
 

grouped = data.groupby('부서')
# 부서별 최대 매출액과 최소 매출액의 차이 계산 함수
def min_max(x):
    return x.max() - x.min()

grouped['매출액'].agg(min_max)
부서
영업1팀     150
영업2팀     310
해외영업팀    330
Name: 매출액, dtype: int64

grouped['매출액'].agg(['sum','mean','max','min'])