Data_Analysis_Track_33/Python

Python_Pandas_04(Groupby 관련 메소드 및 일괄처리 메소드, TODO 문제풀이)

lsc99 2023. 9. 18. 18:59

filter()

DataFrameGroupBy.filter(func, dropna=True, *args, **kwargs)


- 특정 조건을 만족하는 Group의 데이터(행)들을 조회할 때 사용한다. 주로 조건은 group별 집계결과를 이용한다.
1. 함수에 group별 DataFrame을 argument로 전달한다.
2. 함수는 받은 DataFrame을 이용해 집계한 값의 조건을 비교해서 반환한다.(반환타입: Bool)
3. 반환값이 True인 Group들의 모든 행들로 구성된 DataFrame을 반환한다.
- 매개변수
    - func: filtering 조건을 구현한 함수 객체
        첫번째 매개변수로 Group으로 묶인 DataFrame을 받는다.
        bool type 값을 반환한다. 매개변수로 받은 DataFrame이 특정 조건을 만족하는지 여부를 반환한다.
    - dropna=True
        필터를 통과하지 못한 group의 DataFrame의 값들을 drop시킨다(기본값). False로 설정하면 NA 처리해서 반환한다.
    - *args, **kwargs: filter 함수의 두번째부터 선언된 매개변수에 전달할 argument 값들을 가변인자로 전달한다.

 

DataFrame 생성

import numpy as np
import pandas as pd

# cnt1 값의 범위: 사과: 10대, 귤: 20대, 배: 단단위, 딸기 30이상
data = dict(fruits=['사과', '사과','사과', '사과','사과','귤','귤','귤','귤','귤','배','배','배','배','배','딸기','딸기','딸기','딸기','딸기']
            ,cnt1=[10, 12, 13, 11, 12, 21, 22, 27, 24, 26, 7, 7, 8, 3, 2, 30, 35, 37, 41, 28]
            ,cnt2=[100,  103, 107, 107,  101,  51,  57, 58,  57, 51,  9, 9,  5,  7,  7,  208, 217, 213, 206, 204]
           )
df = pd.DataFrame(data)
df
  • cnt1의 값이 과일별 평균 이상인 값을 가지는 행들을 조회.
  • 자기가 속한 group의 평균보다 큰값을 가지는 행들을 조회.
  • df.groupby("fruits").filter(함수:조건을 체크하는 함수)
# 조건을 체크하는 함수
# 파라미터 한개 이상을 정의 -> 첫번째 파라미터: group별 DataFrame을 받을 변수
def check_mean(x):
    # x 타입 : DataFrame
    # 집계에대한 조건처리
    return x['cnt1'].mean() > 20  # return 값 : bool

 

# cnt1의 평균이 20 이상인(check_mean) 과일들의 행들을 조회(filter())
df.groupby("fruits").filter(check_mean)

 

# 과일별 cnt1의 값의 평균을 조회
df.groupby("fruits")['cnt1'].mean()

조건을 더 다양하게 만들기 위해 Parameter의 개수를 늘린다.

 

# filter함수
# 1번 파라미터(필수): DataFrame(group별), 2번째 부터는 필요한대로 선언.
# 반환값: bool  True가 리턴되는 DataFrame들이 결과값
def check_mean2(x, col_name, threshold):
    # X['col_name'].mean() >= threshold
    return x[col_name].mean() >= threshold

 

# 이전의 DataFrame만 인자로 선언한 것과 다르게 열이름과 상수도 인자로 선언
df.groupby('fruits').filter(check_mean2, col_name = 'cnt1', threshold = 30)

lambda 식 활용

# lambda식을 많이 활용한다.
df.groupby('fruits').filter(lambda x:x['cnt1'].mean() >= 30)

결측치도 출력하기 -> dropna = False (True가 defalut 값이다.)

# 조건에 만족하지 않는 값들은 결측치로 채워서 출력한다. (dropna = False)
df.groupby('fruits').filter(lambda x:x['cnt1'].mean() >= 20, dropna = False)

transform

DataFrameGroupBy.transform(func, *args), SeriesGroupBy.transform(func, *args)


- 함수(func)에 열의 값들을 group 별로 전달 한다. 함수는 그 값을 받아 통계량을 구해 반환한다. 반환된 통계량으로 원래 값들을 변경한 Series를 반환한다. 여러 컬럼에 대해 처리할 경우 DataFrame을 반환한다.
- func: 함수객체
    - 매개변수
        그룹별 컬럼값들을 받을 변수 선언
    - return
        계산한 통계량.
    - DataFrameGroupBy은 모든 컬럼의 값들을 group 별 Series로 전달한다.
- *args: 함수에 전달할 추가 인자값이 있으면 매개변수 순서에 맞게 값을 전달한다.
- transform() 함수를 groupby() 와 사용하면 컬럼의 각 원소들을 자신이 속한 그룹의 통계량으로 변환된 데이터셋을 생성할 수 있다.
- 컬럼의 값과 통계값을 비교해서 보거나 결측치 처리등에 사용할 수있다.

 

df.groupby('fruits')['cnt1'].transform("mean") # 판다스 제공 집계함수 : 문자열로 전달할 수 있다.

 

원본에 통계치 붙여서 비교하기

 

# 기존 DataFrame 을 복제한 df2 생성
df2 = df.copy()
df2.head()

 

# fruits의 cnt1열에 대한 값들을 묶어서 평균값을 내서 result에 저장
result = df2.groupby('fruits')['cnt1'].transform('mean')
result

 

# 저장한 result의 값들을 새로운 열로 추가
df2.insert(2, 'cnt1 과일별 평균', result)

df2

 

# fruits의 cnt2에 대한 값들을 묶어서 평균값을 내서 새로운 열로 추가
df2['cnt2 과일별 평균'] = df2.groupby('fruits')['cnt2'].transform('mean')
df2

# 행 섞기
# df.sample() : DataFrame의 데이터를 sampling(표본추출) -> 일부 데이터만 추출
#     - 전체 데이터를 섞은 다음에 지정한 비율/개수 만큼 추출
#     - frac = 추출할 비율(0 ~ 1 실수) / 개수 (정수)
df3 = df.sample(frac=1.0).reset_index(drop=True)
df3

아까의 DataFrame과 다르게 index 순서가 섞여 한눈에 수치들을 확인하기 어려워졌다.

# 아까와 같이 fruits의 cnt2에 대한 평균값들을 cnt2 mean 열로 추가
df3['cnt2 mean'] = df3.groupby('fruits')['cnt2'].transform('mean')
df3

최대값과 최소값의 차이를 구하는 함수 선언

# 최대 최소 값의 차이
def min_max_diff(x):
    # transform에 전달할 함수 -> 파라미터 : Series
    # 반환값 : 처리한 값.
    return x.max() - x.min()

 

df3.groupby('fruits')['cnt1'].agg(min_max_diff)

 

# fruits의 cnt1열의 최대값과 최소값의 차이를 표현한 min_max 열 추가
df3['min_max'] = df3.groupby('fruits')['cnt1'].transform(min_max_diff)
df3

람다식으로 표현

# 람다식 표현
df3['min_max2'] = df3.groupby('fruits')['cnt1'].transform(lambda x : x.max() - x.min())
df3

결측치 처리
- transform이용해서 결측치를 같은 과일별 평균값으로 변환
    전체 평균보다 좀더 정확할 수 있다.

 

# 결측치가 포함된 Series 생성
import pandas as pd
import numpy as np

s = pd.Series([10, 20, np.nan, 30, np.nan])
s

 

# 결측치를 제거 - dropna()
# 결측치를 다른값으로 대체 - fillna(대체할 값)
s.fillna(1000) # 모든 결측치를 1000 으로 대체

결측치가 아닌 값들은 변경되지 않는다.

replace_value = pd.Series([10000, 20000, 30000, 40000, 50000])
replace_value

s.fillna(replace_value) # s의 결측치를 같은 index의 replace_value값으로 변경. 결측치가 아닌값들은 변경되지 않는다.

dictionary 활용

# fillna() 상수 : 결측치를 상수로 변경.
#          dictionary, series, dataframe(대상이 dataframe) - index 별로 다른 값으로 대체
s.fillna({2:100000, 4:5000000}) # 딕셔너리를 활용하여 결측치값 대체 {index : value}, 결측치가 아닌값은 대체되지 않는다.

# 새로운 DataFrame df4 생성
df4 = df.copy()
df4.loc[[0, 1, 5, 6, 10, 11, 15, 16], 'cnt2'] = np.nan # index 0, 1, 5, 6, 10, 11, 15, 16 을 결측치로 변경
df4

dropna()

# 결측치 처리
# 제거: DataFrame : 행/열 단위로 제거. default: 행, 열단위로 지우려면 axis = 1 -> 결측치가 한 열에 너무 많을 경우
df4.dropna() # 결측치를 가진 행들이 제거된다.

fillna()

# 다른 값으로 대체
df4.fillna(1000) # 컬럼과 상관없이 모든 결측치를 동일한 값으로 변경한다.

이러한 결측치를 의미있는 값으로 변경하기 위해서는 평균값, 중앙값, 최빈값 등 중에 알맞는 값을 찾아 변경해야 한다.

# 결측치를 대체(가장 가능성 높은 값으로 변경. -> 평균/중앙값, 최빈값)
df4['cnt2_1'] = df4['cnt2'].fillna(round(df4['cnt2'].mean())) # 평균값으로 채우기
df4
# 전체 cnt2를 기준으로 평균을 계산해서 결측치를 대체 -> 특정 카테고리별로(과일) 평균값의 차이가 클 경우 전체 평균은 유용한 결측치 대체값이 아니다.

 

# fruits의 종류별로 cnt2 값 평균을 내어 결측치를 의미있는 값으로 변경할 수 있다.
m = df4.groupby('fruits')['cnt2'].transform('mean')
df4['cnt2'] = df4['cnt2'].fillna(m)
df4

diamonds.csv 파일을 가지고 TODO 문제풀이

 

# 1.  data/diamonds.csv 조회
import pandas as pd
df = pd.read_csv('data/diamonds.csv')
df

 

# 2.  cut 별 평균 가격이 4000 이상인 diamond 데이터들 조회 
# df.groupby('cut')['price'].mean() >= 4000

# x <- cut 별 DataFrame이 전달.
result = df.groupby('cut').filter(lambda x : x['price'].mean() >= 4000)
result['cut'].unique()

 

# 3. color 별 carat의 최대값과 최소값의 차이가 2이상 3미만인 모든 diamond 데이터들 조회
def min_max_check(dataframe):
    min_v = dataframe['carat'].min()
    max_v = dataframe['carat'].max()
    diff = max_v - min_v
    return diff >= 2 and diff < 3

result = df.groupby('color').filter(min_max_check)

result.color.unique()

 

# lambda 식 이용해서 확인
df.groupby('color')['carat'].agg(['min', 'max', lambda x : x.max() - x.min()])

 

# 4. clarity 별 평균 가격 컬럼을 DataFrame에 추가.
r = df.groupby('clarity')['price'].transform('mean')
df.insert(7, 'price_maen', r)
df.head(20)