Python_Pandas_04(Groupby 관련 메소드 및 일괄처리 메소드, TODO 문제풀이)
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)