251204_BootCamp Day 34_데이터프레임 고급 집계 이론

2025. 12. 4. 20:46TIL

파이썬에서 Window Function을 이용할 수 있는 방법과, 다양한 변수들 간의 상관계수를 계산할 수 있는 방법

1. Python의 window function

1) SHIFT

시계열 데이터의 인덱스를 원하는 기간 만큼 땡기거나 미는 메서드 (SQL의 LAG와 LEAD)
df.shift(periods=기간, freq=None, axis=0, fill_value=None)

  • periods : 이동할 기간. 음수 또는 양수를 입력
  • freq : 선택 매개변수. 시계열 데이터에서 이동할 단위를 선택. Y, M, D, H, T, S, Timestamp, 'infer' 등이 위치
  • infer는 시계열 데이터를 보고 자동으로 이동 단위를 결정함
df3_prac1['T_SHIFT1'] = df3_prac1['Time stamp'].shift(1)
df3_prac1['T_SHIFT-1'] = df3_prac1['Time stamp'].shift(-1)

 

2) ROLLING

데이터프레임 내 열에 대해서 일정 크기의 창(window)을 지정하여 그 window 안의 값을 추가 연산을 통해 계산하는 메서드
df.rolling(window=X, min_periods=None, center=False, win_type=None, on=None, axis=0, closed=None, method='single')

  • window : 계산할 창의 크기 (열 기준으로 계산할 경우 행의 수)
  • min_periods : 계산할 최소 크기. 기본적으로 window의 크기와 동일함
  • center : 계산을 중간 행에서 할 지 결정하는 파라미터. 기본값은 False
  • win_type : triang/gaussian 등 가중치를 넣어 계산할 때, 그 계산 방식
  • on : 시계열 인덱스나, 시계열과 유사한 열이 있을 경우 이 열을 기준으로 rolling을 수행할 수 있다.
  • axis : 연산할 축 방향 설정
  • closed : 연산이 닫히는 방향을 설정 (left, right, both, neither)
  • method : numba 라이브러리를 이용해 테이블 계산을 수행하여 속도를 높일지 여부. 현재 'single'만 사용 가능
merged_df = pd.merge(df3_prac2, df2, left_on='user id', right_on='Customer ID', how='inner')
merged_df = merged_df[['Customer ID', 'Purchase Amount (USD)', 'Time stamp']]
# groupby의 응용 연산 : 기준 컬럼에 대해 집계할 컬럼 별로 다른 연산을 하고 싶을 때
merged_df = merged_df.groupby(['Time stamp']).agg({'Customer ID':'count', 'Purchase Amount (USD)':'sum'}).reset_index()
merged_df2 = merged_df.copy()
# Rolling 메서드를 활용한 purchase amount 3일 이동평균 구하기
window_size = 3
merged_df['Moving Average'] = merged_df['Purchase Amount (USD)'].rolling(window=window_size).mean().round(1)
# 3일 이동평균을 중간 행을 기준으로 계산
merged_df['Moving Average2'] = merged_df['Purchase Amount (USD)'].rolling(window=window_size, center=True).mean().round(1)
# 3일 이동평균을 중간 행을 기준으로 계산하고, 왼쪽 값을 포함하여 계산
merged_df['Moving Average3'] = merged_df['Purchase Amount (USD)'].rolling(window=window_size, center=True, closed='left').mean().round(1)
# 3일 누적합 구하기
merged_df['prefix_sum'] = merged_df['Purchase Amount (USD)'].rolling(window=window_size).sum().round(1)

 

3) EXPANDING

행이나 열의 값에 대해 누적ㅇ로 연산을 수행하는 메서드
df.expanding(min_periods=1, axis=0, method='single').추가메서드()

  • min_periods : 연산을 수행할 요소의 최소 개수
  • axis : 연산할 축 방향 설정
  • method : 연산 방식. single, table이 있으며, table을 사용하기 위해서는 numba 라이브러리를 추가로 import해야 함
merged_df["Cumulative Mean Purchase"] = merged_df["Purchase Amount (USD)"].expanding().mean()
merged_df["Cumulative Sum Purchase"] = merged_df["Purchase Amount (USD)"].expanding().sum()

 

2. DATA TYPE 별 상관관계 계산

df = pd.read_csv('articles_hm.csv')
df2 = pd.read_csv('customer_hm.csv')
df3 = pd.read_csv('transactions_hm.csv')

mdf = pd.merge(df2, df3, how='inner', on='customer_id')
mdf2 = pd.merge(mdf, df, how='inner', on='article_id')
# 범주형 변수의 범위 확인하기
mdf2['club_member_status'].unique()
mdf2['fashion_news_frequency'].unique()
mdf2['sales_channel_id'].unique()

 

1) Point-Biserial Correlation

연속형 변수와 이분형 범주형 변수 간의 상관관계를 계산하는 상관계수
이분형 변수를 0/1로 코딩한 다음 연속형 변수와 Pearson 상관계수를 계산
결과값은 -1 ~ 1 사이의 값을 가지며, 절대값이 1에 가까울수록 상관관계가 크다는 것을 의미함.
단순 검정통계량만 확인하는 것이 아니라, p-value를 함께 확인해 통계적으로 유의미한 값인지 검증해 봐야 함.

r, p_value = stats.pointbiserialr(mdf['sales_channel_id'], mdf['price'])
  • r(상관계수)이 0.17 정도로 두 변수는 상관관계가 높지 않음을 알 수 있음.
  • p-value가 0.05보다 작으므로, 해당 결과는 통계적으로 의미가 있다.

2) ANOVA 검정

연속형 변수와, 3개 이상의 범주를 가지는 범주형 변수 간의 상관관계를 분석하는 검정 방식
명목형 범주형 변수 (범주 간 순서가 없는 범주형 변수)에 대해, 연속형 변수의 평균이 범주형 변수에 따라 차이가 있는지 분석해 상관계수를 계산한다.
Category를 club_member_status의 unique 값들로 지정하고, 이를 모든 행에 적용해 필터링한 데이터셋에서 price 컬럼만 가져온다.

groups = [mdf2['price'][mdf2['club_member_status']==category] for category in mdf2['club_member_status'].unique()]

ANOVA 검정의 결과 해석

  • F <1 : 무의미
  • 1 <= F < 3 : 거의 무의미
  • 3 <= F <10 : 경우에 따라 유의미
  • 10 <= F < 50 : 유의미 (강한 차이)
  • F > 50 : 거의 확실한 유의미 (아주 강한 차이)
  • F >= 100 : 확실한 유의미 (매우 강한 차이)

F 값이 큰것만 확인하는 것이 아니라, 이를 통계적으로 설명 가능한 지 P-value 도 함께 봐야 한다.
F 값이 크게 나왔지만, P-value 가 0.05 보다 크다면, 통계적으로 의미가 없을 수 있음


계산 결과, F는 150 이상인데 p-value는 0이다.
따라서 클럽 멤버상태와 결제 금액은 매우 강한 상관관계가 있다. 

 

3) Cramer's V

두 범주형 변수 간의 상관관계를 분석할 때 사용
Cramer's V 상관계수는 두 범주형 변수 중 하나의 변수가 3개 이상의 범위를 가지는 경우 사용할 수 있음

  • 1단계 : 범주형 변수의 값들을 LabelEncoding
  • 2단계 : 함수 정의
  • 3단계 : 혼동행렬 생성

Cramer's V 상관계수는 0 ~ 1의 값을 가지며, 값이 클수록 높은 상관관계가 있음을 의미함
scipy 라이브러리와 sklearn 라이브러리로 구현할 수 있다

from scipy.stats.contingency import association
from sklearn import preprocessing
from scipy.stats import chi2_contingency

# 1단계 : Label Encoding
base_df = mdf2[['club_member_status', 'fashion_news_frequency']]
base_df = base_df.dropna() #범주형 변수이기 때문에 결측치를 반드시 삭제해줘야 한다 (대체도 안됨)

label = preprocessing.LabelEncoder()
data_encoded = pd.DataFrame()

for i in base_df.columns:
    data_encoded[i] = label.fit_transform(base_df[i])
data_encoded.head()
data_encoded.groupby(['club_member_status']).count()

# 2 ~ 3단계 : 함수 정의 및 혼동행렬(confusion matrix) 생성
# Cramer's V 함수 정의 (공식이라써 쓰는 게 아니고 복사 붙여넣기 하면 된다)
def cramers_V(var1, var2):
    crosstab = np.array(pd.crosstab(var1, var2, rownames=None, colnames=None))
    stat = chi2_contingency(crosstab)[0]
    obs = np.sum(crosstab)
    phi2 = stat / obs
    r, k = crosstab.shape
    phi2corr = max(0, phi2 - (((k-1)*(r-1))/(obs-1)))
    rcorr = r - ((r - 1)**2)/(obs - 1)
    kcorr = k - ((k - 1)**2) / (obs - 1)
    return np.sqrt(phi2corr / min((kcorr - 1), (rcorr - 1)))

# 혼동행렬 생성
rows = []

for var1 in data_encoded:
    col = []
    for var2 in data_encoded:
        cramers = cramers_V(data_encoded[var1], data_encoded[var2])
        col.append(round(cramers, 2))
    rows.append(col)

cramers_results = np.array(rows)
data_encoded_df = pd.DataFrame(cramers_results, columns = data_encoded.columns, index = data_encoded.columns)

data_encoded_df