본문 바로가기
국비 교육/데이터

[데이터 분석] Pandas - 3

by 육츠 2023. 11. 11.
Contents 접기
df = pd.DataFrame([[1,2],[3,4]], index= ['A','B'], columns= ['a','b'])
df

새로운 데이터 프레임 생성(인덱스가  A,B , 컬럼명이 a,b 인 1행이 1,2, 2행이 3,4 로 채워진 2행 2열의 데이터 프레임)

 

데이터프레임 간의 조합

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

df2 = pd.concat([df,df])
df2

concat()의 축 기본값이 axis = 0 이기 때문에 열에 방향(아래쪽으로) df가 만들어진다.

 

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

축 방향을 설정함으로써 행의 방향(옆 쪽으로) df가 만들어진다.

 

집계함수
모든 집계함수는 axis = 0 을 기본값으로 한다.
일반적인 상황에서 행 방향의 집계는 유의미하지 않다.
df.mean() : 각 행/열에 대한 평균을 산출한다.
df.std() : 각 행/열에 대한 표준편차를 산출한다.
df.min()/df.max() : 각 행/열에 대한 최소/최대값을 산출한다.

 

[문제] Series, DataFrame 생성 및 연산

- 각 컬럼 값은 Series 객체로 생성
- 앞서 만든 Series 객체를 이용해서 DataFrame 생성
- 각 과목의 합계를 계산한 컬럼 추가
- 각 과목의 평균을 계산한 컬럼 추가

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([77,85,90,76,88,77], index= index_list)
sr_eng = pd.Series([90,100,96,91,80,90], index= index_list)

각 컬럼의 값을 Series 객체로 생성한다. (나는 하나의 행을 하나의 데이터 프레임으로 만들었다. )

위의 데이터프레임을 만들어야 하는 예시이다.

score_df = pd.DataFrame({'전공':sr_major, '수학': sr_math, '국어':sr_kor, '영어':sr_eng})
score_df

Series 값을 이용하여 데이터 프레임을 생성한다.

 

# score_df['합계'] = score_df.loc[:,'수학':'영어'].sum(axis=1)
score_df['합계'] = score_df[['수학','국어','영어']].sum(axis=1) 
# 복수의 컬럼 목록을 가져오는
# score_df.drop('합계', axis=1,inplace= True)
score_df

수학부터 영어까지 슬라이싱 하여 합계를 구한다.

 

score_df['평균'] = score_df.loc[:,'수학':'영어'].mean(axis=1)
score_df

# 평균값 포맷 적용하기
score_df['평균'] = score_df['평균'].apply(lambda x:f'{x:.2f}')
# x 변수 : 평균 컬럼의 값
score_df

수학부터 영어까지 슬라이싱 하여 평균을 구한다. (람다식을 이용할 수도 있다.)

 

# 평균값이 object -> float 형변환
score_df['평균'] = score_df['평균'].astype(float)

score_df.info()

지금은 예제로 한 문제이기 때문에 평균의 타입을 변경할 필요는 없지만, 한 번 변경해 본다..

 

import pandas as pd
import numpy as np

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

새로운 데이터프레임 생성

 

결측값 처리

import pandas as pd
import numpy as np

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
df.at['D','e'] = np.nan
df

결측값 있는 새로운 데이터프레임 생성


- df.isna(), df.notna()
데이터가 NaN인지 아닌지 검사

checked_nan = df.isna()
print(checked_nan)

#        a      b      c      d      e
# A  False  False  False  False  False
# B  False  False   True  False  False

print(checked_nan.sum()) # 컬럼별 집계

# a    0
# b    0
# c    1

 

df.dropna()
결측값을 포함한 행 또는 열을 삭제한다. 기본값은 결측값이 존재하는 행을 모두 제거하는 방식이다.
새로운 데이터 프레임을 반환 > 원본의 영향을 받지 않는다.

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

df.dropna()
#       a	b	c	d	e
#  A	8	0	5.0	6	4.0
#  C	6	9	0.0	4	4.0

df.dropna(axis= 1)

#       a	b	d
#  A	8	0	6

 

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

df.dropna(subset=['c']) 
# 'c' 컬럼의 결측값이 있는 행을 제거한다.
#       a	b	c	d	e
#  A	8	0	5.0	6	4.0
#  C	6	9	0.0	4	4.0

 

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

df.fillna(-999)

NaN이 -999로 채워진다.

 

df['c'] = df['c'].fillna(df['c'].mean())
df

c 컬럼에 대해서만 평균값을 집어 넣음

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

# c컬럼에 대해서만 근처값을 집어 넣음

df.fillna(method='pad') # 앞

df.fillna(method='bfill') # 뒤
df = pd.DataFrame({'k1':['one','two']*3 +['two'],
                  'k2': [1,1,2,3,3,4,4]}) #7개 원소 리스트
df

새로운 데이터 프레임 생성

 

데이터 중복 제거

중복 데이터 검사
- df.duplicated()
각 행의 중복 여부를 검사하여 True/False 로 알려준다.

df.duplicated()
# 5    False
# 6     True
# dtype: bool

 

- df.drop_duplicates()
모든 컬럼에 대해 중복 값을 갖는 행을 제거한다.

df.drop_duplicates()
#  4	one	3
#  5	two	4

뒤에 있는 중복된 값이 지워진다.

- df.drop_duplicates(컬럼명 목록)
매개변수로 주어진 컬럼명 목록에 대해 같은 값을 갖는 행을 제거한다.

df['k3'] = np.arange(7)
df
df.drop_duplicates(['k1','k2'])

k1,k2에 대해 중복된 부분을 찾고 k1:k3까지의 중복된 행을 제거 한다.

keep = 'last' 파라매터
drop_duplicates() 는 기본적으로 처음 발견된 값을 유지한다. keep = 'last' 옵션을 지정하면 마지막으로 발견된 값을 유지한다.

df.drop_duplicates(['k1','k2'],keep= 'last')

6번째 행이 아닌 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']) 
# 멀티 인덱스도 가능하다

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

 

# 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

 

피봇 테이블을 이용하여 연도별 분기 이익 데이터 확인

# 1.
pd.pivot_table(df, index = 'year', columns='quarter', values = 'benefit')

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

#3.
df_pivot.index # 튜플의 형태

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

# 1. 은 인덱스를 year 로 컬럼명은 연도로 설정하여 이익을 비교하는 피봇테이블이다.
# 2. 는 인덱스를 year과 quarter로 설정하여 이익을 비교하는 피봇테이블이다.
# 4. aggfunc 를 이용하여 sum 함수와 mean 함수를 이용한다. 

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

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기'] # 데이터 프레임 형태

# 2.
df['1기','오후'] # 시리즈 형태

# 3.
df.loc[1,'1기'] # 1행에서 1기에 해당하는

# 1. 1기에 오전과 오후가 속해있기 때문에 데이터 프레임 형태이다.
# 2. 1기에 오후로 설정을 했기 때문에 단일값이므로 시리즈 형태로 결과를 반환한다.
# 3. 1행에 해당하며 1기의 속해있는 하나의 값만 결과를 반환한다.