오늘은 데이터 전처리 중에서 결측치 어떻게 처리할까 고민하면서 데이터 처리를 중심으로 진행헀다.
어제 대통령 선거였어서, 개인적으로 기획서 2일 새벽에 후딱 내고 전처리를 혼자 진행했다.

1. 결측치 처리
# 암호화된 postal code 컬럼 삭제
customer.drop(columns=['postal_code'], inplace=True, axis=1)

# AGE 결측치 행 삭제 (15,861)
customer = customer.dropna(subset=['age'])

# club_member_status 를 UNKNOWN 결측값 대체
customer['club_member_status'] = customer['club_member_status'].fillna('UNKNOWN')

# FN 결측치 처리
# 1. FN 이 NAN 인 행 중 fashion_news_frequency가 'None'인 경우에는 0으로 처리
customer.loc[customer['FN'].isna() & (customer['fashion_news_frequency'].str.lower() == 'none'), 'FN'] = 0

# 2. FN 이 여전히 NAN 인 경우, -1 로 처리
customer['FN'].fillna(-1,inplace=True)

# 3. FN 이 -1 인 경우 -> MISSING 파생 변수 생성
customer['FN_missing'] = (customer['FN'] == -1).astype(int)

# Active 결측치 처리
# 1. 파생변수 Active_missing 생성
customer['Active_missing'] = customer['Active'].isna().astype(int)

# 2. Active 결측치 0 으로 대체
customer['Active'].fillna(0.0, inplace=True)

# Fashion_new_missing 결측치 처리
# 1. 결측치 생성
customer['fashion_news_missing'] = customer['fashion_news_frequency'].isna().astype(int)

# 2. 결측을 'None'으로 채우기
customer['fashion_news_frequency'].fillna('None', inplace=True)

일단은 전처리도 진짜 확인하보고 KNN도 진행했는데, AGE 의 경우에는 15000개의 결측치가 있는 바람에 KNN을 차라리 하지 않는게 나아서 아예 제거했다. 이래저래 여러가지로 좀 비교도 해보면서 왜 이 컬럼을 제거하고, 이 결측치를 채워야했는지 팀원들과 대화하면서 진행했다.

# 3. FN(패션 구독 여부,1/NAN) 와 fashion_news_frequency(뉴스 구독 주기 상태) 결측에 상관관계가 있는 지 확인

## 총 FN 컬럼 중 NAN 값의 개수 확인
### 계산은 다음과 같이 진행 -> FN 값 중 NAN 값인 사람들 중 NONE 인 사람들의 비중과 아닌 사람들의 비중 확인
fn_nan = customer[customer['FN'].isna()]
total_fn_nan = len(fn_nan)
fashion_none_count = (fn_nan['fashion_news_frequency'].str.lower() == 'none').sum()

print(f"FN이 NaN인 행의 수: {total_fn_nan}")
print(f"그 중 fashion_news_frequency가 'None'인 행의 수: {fashion_none_count}")
print(f"비율: {fashion_none_count / total_fn_nan:.2%}" if total_fn_nan > 0 else "해당 조건을 만족하는 행이 없습니다.")

# 결론 : 전체 결측값 895050 개중 구독하지 않는 ㅏ람 들 중, 패션 뉴스를 받지 않는 사람(NONE)의 수는 97.97%

# 총 FN 결측치 중 NAN 이지만 NONE이 아닌 행들의 개수 확인하기
## 1. FN 이 NAN이지만, FNF은 NONE 이 아닌 경우(18,128)
fn_nan_fashion_not_none = customer[customer['FN'].isna() & (customer['fashion_news_frequency'].str.lower() != 'none')]
count_fn_nan_fashion_not_none = len(fn_nan_fashion_not_none)
print(f"FN이 NaN이고 fashion_news_frequency가 'None'이 아닌 행의 수: {count_fn_nan_fashion_not_none}")

## 2.FN 이 NAN이 아니지만 FNF는 NONE 인 행의 수(789)
fn_not_nan_fashion_none = customer[customer['FN'].notna() & (customer['fashion_news_frequency'].str.lower() == 'none')]
count_fn_not_nan_fashion_none = len(fn_not_nan_fashion_none)
print(f"FN이 NaN이 아니고 fashion_news_frequency가 'None'인 행의 수: {count_fn_not_nan_fashion_none}")

## 3. 둘다 결측인 경우(16,009)
both_missing = customer[customer['FN'].isna() & customer['fashion_news_frequency'].isna()]
print(f"FN과 fashion_news_frequency가 모두 결측인 고객 수: {len(both_missing)}")
# Active개수, 비율 확인
print(customer['Active'].value_counts(dropna=False))
print(customer['Active'].unique())
print(customer['Active'].value_counts(dropna=False, normalize=True))

# 파생변수 ACtive_missing 생성
customer['Active_missing'] = customer['Active'].isna().astype(int)

# Active 결측치 0 으로 대체
customer['Active'].fillna(0.0, inplace=True)

# 1. 0은 frequency 값 있음, 1은 값 없음
customer['fashion_news_missing'] = customer['fashion_news_frequency'].isna().astype(int)
print(customer.groupby('fashion_news_missing')['FN'].value_counts())
print(customer.groupby('fashion_news_missing')['club_member_status'].value_counts(normalize=True))
print(customer.groupby('fashion_news_missing')['age'].mean())
print(customer.groupby('fashion_news_missing')['Active'].value_counts(normalize=True))

# 요약 </br>
# 1. FN(패션 뉴스 구독 여부) -> 결측인데 거의 뉴스레터를 안본다 / 2는 무언가 이상함
# 2. 회원 상태 -> 결측인데 ACTIVE ? PRE-CREATIVE? UNKNOWN? 좀 불완전 고객일 가능성
# 3. 나이 연령대 -> 나이는 좀 있음
# 4. 커뮤니케이션 가능 상태 -> 거의 없다고 봐야함 (거의 비활성 고객)

# 1. 결측치 생성
customer['fashion_news_missing'] = customer['fashion_news_frequency'].isna().astype(int)

# 2. 결측을 'None'으로 채우기
customer['fashion_news_frequency'].fillna('None', inplace=True)

print("결측치 여부 확인:", customer['fashion_news_frequency'].isna().sum())  # 0이어야 함
print(customer['fashion_news_missing'].value_counts())  # 0: 정상, 1: 원래 결측이었던 고객 수

내가 담당한 customer 부분은 완료! 나머지는 article 부분과 transaction 부분을 같이 전처리 진행했다. 그 덕분에 figma 에는 어떻게 전처리 했는지 가득했는데, 

생각보다 customer 부분이 전처리가 원래 할게 많은부분인걸 나중에서야 알았다. 그래도 팀원들이 내 논리를 납득할 수 있는 수준이었기 때문에 잘 이어갈 수 있었다.

내일은 이상치와 관련된 논의를 진행하고 튜터님을 찾아뵐 예정이다. 그리고 튜터님을 찾아뵙기 전 코드 통합까지! 그러면 일단은 이번주 내로 다 끝낸거긴 하다. 고객 segment 가 가장 어려운 부분이라서 되도록이면 빨리 처리해보기로 결정했다.

myo