본문 바로가기

Quant

Quant : Pandas - Grouping

 

Grouping

 

PER 값에 따라 group number 부여하기

- 값을 기준으로 grouping 하기

 

 

1) boolean section(조건식) & loc 사용

df.shape
# (681, 17)
bound1 = df['PER(배)'] >= 10
# (378, 17)
bound2 = (5 <= df['PER(배)']) & (df['PER(배)'] < 10)
bound3 = (0 <= df['PER(배)']) & (df['PER(배)'] < 5)
bound4 = df['PER(배)'] < 0

df.loc[bound1, 'PER_Score'] = 1
df.loc[bound2, 'PER_Score'] = 2
df.loc[bound3, 'PER_Score'] = 3
df.loc[bound4, 'PER_Score'] = -1

df['PER_Score'].value_counts()
# 1.000     378
# 2.000     148
# -1.000    120
# 3.000      23
# Name: PER_Score, dtype: int64

 

nan 값을 체크 후, 다른 값으로 교체해야함.

df['PER_Score'].isna().sum()
# 12

df.loc[df['PER_Score'].isna(), "PER_Score"] = 0
# 아래와 같은 방식으로도 가능
# df['PER_Score'] = df['PER_Score'].fillna(0) 
# df.loc[:, 'PER_Score'] = df['PER_Score'].fillna(0) - Pandas 권장 방식

nan 값을 체크 후, 다른 값으로 교체해야함.

nan 값을 0으로 메꾸었을 때의 문제점은 기존에 이미 존재하는 값 중에서 PER_Score이 0 인 값이 있다면, 임의로 메꾼 0과 구분할 수 없습니다. 따라서 데이터 값의 특성을 잘 반영해서 메꿀 값을 선택하는 것이 중요합니다.

 

 

2) Boolean Series의 연산 특성 사용

df.loc[:, "PER_Score1"] = (bound1 * 1)  + (bound2 * 2) + (bound3 * 3) + (bound4 * -1) 
df['PER_Score'].value_counts()

# 1    378
# 2    148
# -1    120
# 3     23
# 0     12
# Name: PER_Score1, dtype: int64

- bound1, bound2... True에 해당되면 n, False에 해당되면 0

- nan값은 결국 0만 남게 된다.

- 더 간결한 코드로 위와 같은 결과

 


 

.cut()

- 위의 기능과 동일한 Pandas 함수

- dtype : category [한국, 미국, 중국] 처럼 유한한 변수의 선택지

per_cuts = pd.cut(
    df['PER(배)'],
    [-np.inf, 0, 5, 10, np.inf], 
)

per_cuts.head()

# 0    (10.0, inf]
# 1    (10.0, inf]
# 2    (5.0, 10.0]
# 3    (10.0, inf]
# 4    (10.0, inf]
# Name: PER(배), dtype: category
# Categories (4, interval[float64, right]): [(-inf, 0.0] < (0.0, 5.0] < (5.0, 10.0] < (10.0, inf]]

- cut()에 label 달아주기 可

bins = [-np.inf, 10, 20, np.inf]
labels = ['저평가주', '보통주', '고평가주']
per_cuts2 = pd.cut(
    df['PER(배)'], 
    bins=bins, 
    labels=labels
)
per_cuts2.head()

# 0     보통주
# 1    고평가주
# 2    저평가주
# 3     보통주
# 4    고평가주
# Name: PER(배), dtype: category
# Categories (3, object): ['저평가주' < '보통주' < '고평가주']

'개체 수'를 기준으로 grouping 하기

- quantile : qcut()

- qcut() 함수를 이용해서 PER 그룹 나누기

- 분포를 n 등분 했을 때, 어느 구역에 할당되는지 표시해줌

df.loc[:, 'PER_Score2'] = pd.qcut(df['PER(배)'], 10, labels=range(1, 11))
df['PER_Score2'].value_counts()
# 1     67
# 2     67
# 3     67
# 4     67
# 5     67
# 7     67
# 8     67
# 9     67
# 10    67
# 6     66
# Name: PER_Score2, dtype: int64

df['PER_Score2'].isna().sum() # nan 체크
# 12

df = df.dropna(subset=['PER(배)']) # nan 제거
df['PER_Score2'].isna().sum()
# 0

Groupby & Aggregation : 데이터 준비

- Split - Apply - Combine

- groupby() : 실제로 grouping 까지는 하지 않고, grouping이 가능한지 validation만 진행(preparation)

- aggregation : columns + functions(sum, min, max, mean, count, variance, std..)

g_df_obj = g_df.groupby(["PBR_score", "PER_score"])
g_df_obj
#<pandas.core.groupby.generic.DataFrameGroupBy object at 0x00000183FC52AF10>

type(g_df_obj)
# pandas.core.groupby.generic.DataFrameGroupBy

g_df_obj.size().head()
# PBR_score  PER_score
# 1          1             5
#            2            11
#            3            11
#            4            11
#            5             7
# dtype: int64

# Multi-level index를 가진 Series indexing하는 법 
g_df_obj.size().loc[1]
g_df_obj.size().loc[(1, 1)]

 

aggregation

- 반드시 aggregating 기능이 있는 function을 써야함.

- 여러 값 -> 1개 값

- min, max, mean, median, sum, var, size, nunique, idxmax

g_df.groupby("PBR_score").agg(
    {
        "rtn": "mean", # =  np.mean
    }
)
# 다양한 방법으로 진행하기 (같은 결과)
# g_df.groupby("PER_score")['rtn'].agg('mean').head()
# g_df.groupby("PER_score")['rtn'].agg(np.mean).head()
# g_df.groupby("PER_score")['rtn'].mean().head()


# 2개 이상의 컬럼에 대해 aggregation
g_df.groupby("PER_score")[['rtn', 'PBR(배)']].agg("mean").head(2)

# 2개 이상의 aggregation
g_df.groupby("PER_score")[['rtn', 'PBR(배)']].agg(["mean", "std"]).head(2)

# 2개 이상의 컬럼 & 각각에 대해 다른 aggregation
g_df.groupby("PBR_score").agg(
    {
        'rtn': ['mean', 'std'],
        'PER(배)': ['min']
        
    }
)

g_df1 = g_df.groupby(["PBR_score", "PER_score"])\
            .agg(
                {
                    'rtn': ['mean', 'std', 'min', 'max'],
                    'ROE(%)': [np.mean, 'size', 'nunique', 'idxmax'] 
                 }
            )

 

- nan은 groupby시 자동으로 filter out 되기 때문에, 미리 전처리 하는게 좋음

df = pd.DataFrame({
    'a':['소형주', np.nan, '대형주', '대형주'],
    'b':[np.nan, 2,         3,     np.nan],
})

df.groupby(['a'])['b'].mean()

# a
# 대형주   3.000
# 소형주     NaN
# Name: b, dtype: float64

Multi-index columns를 하나로 병합하기

level0 = g_df1.columns.get_level_values(0)
level1 = g_df1.columns.get_level_values(1)

level0
level1
# Index(['rtn', 'rtn', 'rtn', 'rtn', 'ROE(%)', 'ROE(%)', 'ROE(%)', 'ROE(%)'], dtype='object')
# Index(['mean', 'std', 'min', 'max', 'mean', 'size', 'nunique', 'idxmax'], dtype='object')

g_df1.columns = level0 + '_' + level1

 

 

* 시가총액으로 Small and Big 나누기

median_df = a_df.groupby(['date']).agg({'시가총액 (보통)(평균)(원)': 'median'})
median_df.head()
median_df.columns = ['시가총액_median']
median_df.head()

 

'Quant' 카테고리의 다른 글

Quant : Visualization - Matplotlib  (0) 2022.04.13
Quant : Pandas - 데이터 합치기  (0) 2022.04.05
Quant : Pandas - API(2)  (0) 2022.03.15
Quant : Pandas - API (1)  (0) 2022.03.14
Quant : Pandas - Series & DataFrame  (0) 2022.03.12