Data_Analysis_Track_33/Python

Python_Pandas_04-2(pivot_table, apply, cut, TODO문제)

lsc99 2023. 9. 18. 19:35

pivot_table()
엑셀의 pivot table 기능을 제공하는 메소드.
분류별 집계(Group으로 묶어 집계)를 처리하는 함수로 group으로 묶고자 하는 컬럼들을 행과 열로 위치시키고 집계값을 값으로 보여준다.
역할은 groupby()를 이용한 집계와 같은데 여러개 컬럼을 기준으로 groupby 를 할 경우 집계결과를 읽는 것이 더 편하다.(가독성이 좋다)

pivot() 함수와 역할이 다르다.
pivot() 은 index와 column의 형태를 바꾸는 reshape 함수.

DataFrame.pivot_table(values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')


매개변수
- index
    문자열 또는 리스트. index로 올 컬럼들 => groupby였으면 묶었을 컬럼
- columns
    문자열 또는 리스트. column으로 올 컬럼들 => groupby였으면 묶었을 컬럼 (index/columns가 묶여서 groupby에 묶을 컬럼들이 된다.)
- values
    문자열 또는 리스트. 집계할 대상 컬럼들
- aggfunc
    집계함수 지정. 함수, 함수이름문자열, 함수리스트(함수이름 문자열/함수객체), dict: 집계할 함수
    기본(생략시): 평균을 구한다. (mean이 기본값)
- fill_value, dropna
    fill_value: 집계시 NA가 나올경우 채울 값
    dropna: boolean. 컬럼의 전체값이 NA인 경우 그 컬럼 제거(기본: True)
- margins/margins_name
    margin: boolean(기본: False). 총집계결과를 만들지 여부.
    margin_name: margin의 이름 문자열로 지정 (생략시 All)

 

 

flights.csv 파일을 읽어와 DataFrame flight 생성

flight = pd.read_csv('data/flights.csv')
flight.shape

1개의 컬럼을 Grouping해서 집계

 

groupby()

flight.groupby('AIRLINE')['AIR_TIME'].mean()  # .to_frame() -> 데이터프레임으로 조회

pivot_table()

# flight.pivot_table(index='AIRLINE', values='AIR_TIME', aggfunc='mean')
flight.pivot_table(columns='AIRLINE', values='AIR_TIME', aggfunc='mean')

 

2개의 컬럼을 Grouping해서 집계

 

groupby()

flight.groupby(['AIRLINE', 'ORG_AIR'])['CANCELLED'].sum()

pivot_table()

flight.pivot_table(index = 'AIRLINE',    # 그룹을 나누는 기준 컬럼중 index(행)에 놓을 컬럼.
                   columns = 'ORG_AIR',  # 그룹을 나누는 기준 컬럼중 column(열)에 놓을 컬럼.
                   values = 'CANCELLED', # 집계대상 컬럼
                   aggfunc = 'sum',      # 집계 함수
                   fill_value = '-999',  # NaN을 대신할 값
                   margins = True,       # sql의 rollup
                   margins_name = '통계' # margin 컬럼/ index의 이름지정 
                  )

 

3개 이상의 컬럼을 Grouping해서 집계

 

pivot_table()

flight.pivot_table(index = ['AIRLINE', 'ORG_AIR'], 
                   columns = 'MONTH', # 묶을 대상이 여러개인 경우 리스트로
                   values = 'CANCELLED',
                   aggfunc = 'sum',
                   margins = True
                  )

 

flight.pivot_table(index = ['AIRLINE', 'ORG_AIR'], 
                   columns = 'MONTH', 
                   values = 'ARR_DELAY',
                   aggfunc = ['min', 'max'], # 집계함수가 여러개인 경우 list로 묶어 제공 
                  )

apply() - Series, DataFrame의 데이터 일괄 처리
- 데이터프레임의 행들과 열들 또는 Series의 원소들에 공통된 처리를 할 때 apply 함수를 이용하면 반복문을 사용하지 않고 일괄 처리가 가능하다.

DataFrame.apply(함수, axis=0, args=(), **kwarg)


- 인수로 행이나 열을 받는 함수를 apply 메서드의 인수로 넣으면 데이터프레임의 행이나 열들을 하나씩 함수에 전달한다.
- 매개변수
    - 함수: DataFrame의 행들 또는 열들을 전달할 함수
    - axis: 0-컬럼(열)을 전달, 1-행을 전달 (기본값 0)
    - args: 함수에 행/열 이외에 전달할 매개변수를 위치기반(순서대로) 튜플로 전달
    - **kwarg: 함수에 행/열 이외에 전달할 매개변수를 키워드 인자로 전달

Series.apply(함수, args=(), **kwarg)


- 인수로 Series의 원소들을 받는 함수를 apply 메소드의 인수로 넣으면 Series의 원소들을 하나씩 함수로 전달한다.
- 매개변수
    - 함수: Series의 원소들을 전달할 함수
    - args: 함수에 원소 이외에 전달할 매개변수를 위치기반(순서대로) 튜플로 전달
    - **kwarg: 함수에 원소 이외에 전달할 매개변수를 키워드 인자로 전달

 

# 1: 월 ~ 7: 일
flight.WEEKDAY.value_counts().sort_index()

 

# 요일 전체(일괄처리) : 정수 -> 문자열로 변환
flight.WEEKDAY

 

# Series의 원소 하나를 받아서 처리후 반환하는 함수
def change_weekday(value):
    l = list('월화수목금토일')
    v = l[value-1]
    return v+'요일'

 

flight['WEEKDAY'] = flight['WEEKDAY'].apply(change_weekday) # 한글로 보기 쉽게 월 ~ 일요일로 변환하여 WEEKDAY컬럼으로

 

# WEEKDAY: 문자열 -> 정수 index
flight.insert(3, 'WEEKDAY2',
             flight['WEEKDAY'].apply(lambda x : d[x]) # x : 원소 한개 -> 요일문자열
             )

 

flight.head()

 

# DataFrame.apply(함수) # 함수(컬럼 또는 행 : Series)
def func(column):
    # column : pd.Series - 개별 컬럼의 값들을 Series로 받는다.
    # 각 컬럼의 대표값을 반환. -> 숫자형: 평균, 문자열: 최빈값
    if column.dtype == 'object':
        return column.mode()[0]
    else:
        return column.mean()

 

flight.apply(func)

 

flight.select_dtypes(include='number').apply(lambda x : x * 5)

cut()/qcut() - 연속형(실수)을 범주형으로 변환
- cut() : 지정한 값을 기준으로 구간을 나눠 그룹으로 묶는다.

pd.cut(x, bins,right=True, labels=None)


- 매개변수
    - x: 범주형으로 바꿀 대상. 1차원 배열형태(Series, 리스트, ndarray)의 자료구조
    - bins: 범주로 나눌때의 기준값(구간경계)들을 리스트로 묶어서 전달한다.
    - right: 구간경계의 오른쪽(True-기본)을 포함할지 왼쪽(False)을 포함할지
    - labels: 각 구간(범주)의 label을 리스트로 전달
        생략하면 범위를 범주명으로 사용한다. (ex: (10, 20], ()-포함안함, []-포함)


- qcut() : 대상배열의 최대값 ~ 최소값을 지정한 개수의 동등한 size(원소의개수)가 되도록 나눈다.

pd.qcut(x, q, labels)


- 매개변수
    - x: 나눌 대상. 1차원 배열형태의 자료구조
    - q: 나눌 개수
    - labels: 각 구간(범주)의 label을 리스트로 전달

 

import numpy as np
np.random.seed(0)
age = np.random.randint(1, 100, 30) # 1 ~ 100 사이 정수를 랜덤하게 생성
tall = np.round(np.random.normal(170, 10, 30), 2)

 

np.random.normal(170, 10, 30) # 평균 : 170, 표준편차 : 10 인 정규분포를 따르는 실수를 랜덤하게 생성
# 정규분포 -> 95%의 값들은 평균 +- 2표준편차의 값을 가진다. -> 생성되는 값의 95%는 150 ~ 190 사이의 실수

DataFrame 생성

df = pd.DataFrame({
    "나이" : age,
    "키" : tall,
})

 

# 각 value의 개수 출력
df.나이.value_counts()

df.키.value_counts()

df.키.mean() # 키의 평균

 

df.groupby('나이')['키'].mean().sort_index()

 

# cut
result = pd.cut(df.나이, bins=3) # bins : 몇등분 할지
# 지정한대로 그룹을 만든 다음에 개별값들을 그룹값으로 모두 처리한 결과를 반환
result.value_counts()
  • 범위 표시
  • (시작, 끝) : 불포함 -> opened
  • [시작, 끝] : 포함     -> closed
  • (62.667, 89.0]  : 62.667 < 범위 <= 89.0
result[:5]

df.나이.iloc[:5] # 두개의 결과값 확인해보기

 

result2 = pd.cut(df.나이, bins = 3, right = False) # right = False 분위 값을 왼쪽(시작쪽)에 포함
result2.value_counts()

 

result3 = pd.cut(df.나이, bins=3, right=False,
                labels=['나이대1', '나이대2', '나이대3']) # 나뉜 그룹에 이름들을 지정
result3.value_counts()

복제 DataFrame 생성

df2 = df.copy()

 

df2['나이대'] = pd.cut(df['나이'], bins=3, labels=['나이대1', '나이대2', '나이대3'])
df2.groupby('나이대')['키'].mean()

 

df2['나이대'].value_counts().to_frame()

포함 미포함의 범위를 생각하면서 결과 확인하

# 원하는 범위(기준)로 그룹을 나누기.
l = [0, 20, 40, 60, 100]
# 0 ~ 20, 20 ~ 40, 40 ~ 60, 60 ~ 100
result4 = pd.cut(df2['나이'], 
                 bins=l)

result4.value_counts().sort_index()

labels의 이름에 의미를 부여하여 나타낼 수도 있다.

result5 = pd.cut(df2['나이'], 
                 bins=l, 
                 labels=["청소년", "청년", "장년", "노년"])

result5.value_counts().sort_index()

새로운 columns 추가

df2['나이대2'] = result5
df2

의미를 부여한 나이대들의 키의 최소,최대값 확인

df2.groupby('나이대2')['키'].agg(['min', 'max'])

qcut()

# pd.qcut() => 동일한 원소의 개수가 되도록 그룹을 나눈다.
result = pd.qcut(df['나이'], q=3)  # q: 몇개로 나눌지
result.value_counts()

 

result = pd.qcut(df['나이'], q=4, labels=['A','B','C','D'])
result.value_counts()

# 1: data/diamonds.csv 를 읽어 DataFrame으로 만든다.
dia = pd.read_csv('data/diamonds.csv')
dia.shape

cut 활용

# 2: price 컬럼을 '고가', '중가', '저가' 세개의 범주값을 가지는 "price_cate" 컬럼을 생성한다.
price_cate1 = pd.cut(dia['price'], bins=3, 
                     labels=['저가', '중가', '고가'])
price_cate1.value_counts()

 

dia['price'].agg(['min','max'])
price_ranage = [300, 1000, 10000, 20000]
# 300 ~ 1000: 저가, 1000~ 10000: 중가, 10000 ~ 20000: 고가
price_cate2 = pd.cut(dia.price, bins=price_ranage, labels=['저가', '중가', '고가'])
price_cate2.value_counts()

qcut 활용

price_cate3 = pd.qcut(dia.price, q=3, labels=['저가', '중가', '고가'])
price_cate3.value_counts()

 

## 가격대 컬럼을 추가
dia['price_cate'] = price_cate1
dia.head()

 

# 3 가격대(price_cate) 별 carat의 평균을 조회
dia.groupby('price_cate')['carat'].mean()

 

# 4 가격대(price_cate)와 cut별 평균 가격(price)를 피봇테이블로 조회
dia.pivot_table(index='cut', columns='price_cate', values='price', aggfunc='mean')

 

# 5 cut, color, price_cate 별 carat의 평균을 피봇테이블로 조회
dia.pivot_table(index=['cut', 'price_cate'], columns='color', values='carat', 
               aggfunc='mean')