데이터 분석

[day32] 이상치, 결측치 / range의 step 은 0이 될 수 없다!

경 민 2025. 3. 19. 20:54
👩🏻‍💻  Point of Today I LEARNED 
📌 Python 
● 라이브세션 5,6회차 복습 (결측치, 이상치 개념 + 실습)
● 코드카타
시각화 완강 

 

Python

1-1. 라이브세션 5회차 복습

1) 결측치 대체

df['컬럼'] = df['컬럼'].fillna(기준컬럼.기준컬럼으로부터계산된값)

series 형태로 출력하는 mode

  • 주요 대체값
    • mode, mean, median, ...
    • 바로 위값(fillna(method='ffill')), 아래 값(fillna(method='bfill')), groupby 값
  • 예시
mode

아래값

 

groupby 값

1) 그루핑 먼저해서 그룹별 중앙값 확인 (확인용)

df.groupby('Is Amazon Seller')['sw'].median().sort_index(ascending=False)

 

 

2) 대체하기

df['sw'] = df['sw'].fillna(df.groupby('Is Amazon Seller')['sw'].transform('median'))
# ⚠️ 마지막에 .transform

'Is Amazon Seller' 그룹별로 중앙값 다르게 출력!


  • ⭐️ 문자형을 숫자형으로 형변환해서 집계값 구하고 싶을 때
    1. split str.split().str[갖고싶은 값의 index번호]
    2. 숫자형으로 변환 pd.to_numeric 
df['sw'] = df['Shipping Weight'].str.split().str[0]
df['sw'] = pd.to_numeric(df['sw'], errors='coerce')

Q. 형변환하고 결측값 개수 세어봤는데 2개 더 증가했다? 왜 이러지?
A. 형변환에 실패한 값들은 결측치 처리된다
예를 들면 비정상적인 문자열(공백, 특수문자, 단위 포함 등)이 포함된 경우.

 


 

2) 이상치 식별 (Z-Score, IQR, Isolation Forest, DBScan)

Z-Score (표준점수) ⭐️⭐️
데이터가 정규분포를 이룰 때,
값이 평균으로부터 얼마나 떨어져있어?
(= Z값이 얼마야?)
너무 많이 떨어져있어? 그럼 너 이상치야.
(= ±3 이상이야? 그럼 너 이상치야.)
해석
Z-Score = 0  ➜ 평균에서 떨어진 거리가 0 (평균이랑 같음)

Z-Score > 0 ➜ 평균보다 큼
Z-Score < 0 ➜ 평균보다 작음

Z-Score = 1 ➜ 평균보다 1 표준편차만큼 크다

Z-Score = -1 ➜ 평균보다 1 표준편차만큼 작다

 

  • Z-Score로 이상치 구하기 흐름
df1 = df[['sw']]   # 표준화할 컬럼 따로 빼오기 
                   # 주의 ! Dataframe 형태로 가져오기 위해서 [] 두번 묶어주기

scale_df = StandardScaler().fit_transform(df1)   # 표준화
scale_df = pd.DataFrame(scale_df)   # Dataframe 형태로 변환

merge_df = pd.concat([df1,scale_df], axis=1)   # 비교해보기 위해 두 컬럼 붙이기
merge_df.columns = ['Shipping Weight', 'Z-Score']   # 컬럼 이름 수정

strange_df = merge_df[(merge_df['Z-Score']>3)|(merge_df['Z-Score']<-3)]   # +- 3이상인 조건으로 이상치 선별
strange_df.count()   # 최종 이상치 개수 구하기

 

 

 IQR(Interquartile Range) ⭐️
데이터가 정규분포를 이루지 않을 때,
사분위값이 얼마야?
(= 값이 최대/최소값 밖이야? 그럼 너 이상치야.)
  • IQR로 이상치 구하기 흐름
df1 = df[['sw']]   # 이상치 점검할 컬럼 따로 빼오기 
                   # 주의 ! Dataframe 형태로 가져오기 위해서 [] 두번 묶어주기

def is_outlier(df1):
    score = df1['sw']   
    if score > 7 + (1.5 * 6) or score < 1 - (1.5 * 6):
        return '이상치'
    else:
        return '이상치아님'   # 이상치 구하는 함수 (외울 필요 없음)


df1['이상치여부'] = df1.apply(is_outlier, axis=1)   # 함수 돌린 값들을 수직으로 나열한 새로운 컬럼 생성

df1.groupby('이상치여부').count()   # 최종 이상치 개수 구하기

 

Isolation Forest
  • 머신러닝 기법으로 이상치 구할 때 필요한 기준 (컬럼)이 많을 때 사용
  • 기준마다 트리형태로 표현 True/False 
    • 경로 길이를 0~1 사이로 산출
    • 1에 가까우면 (너무 짧게 끝나면) 이상치로 판단

 DBScan
  • 밀도 기반
  • 주로 지리, 이미지 데이터 분석에서 활용
  • 흐름
    1. 특정 거리 내에 특정 개수의 다른 데이터가 있을 경우 핵심 포인트로 간주
    2. 핵심 포인트끼리 연결되어 군집 형성
    3. 그 군집에 포함되지 않으면 이상치로 판단


2-2. Pandas 개인과제 6번

  • 문제 포인트
    • datetime의  형변환 time ver.
    • lambda 이용해서 컬럼 만들어주기
    • 여러 조건을 lnline if 절 안에서 걸어주기 (elif 안 됨)
import pandas as pd

# string -> datetime 형변환
df['Dep_Time'] = pd.to_datetime(df['Dep_Time'], format = '%H:%M')

df['time_of_dep'] = df['Dep_Time'].apply(lambda x: '아침' if 5 <= x.hour < 12 \
                                                        else ('낮' if 12 <= x.hour < 18 \
                                                        else ('오후' if 18 <= x.hour < 24 \
                                                        else '밤')))
df.groupby('time_of_dep')['Airline'].count()
Q1. 아까 IQR이상치 구할 때 .apply(~~~ , axis=1) 했던 거 같아서 여기도 넣어줬는데 실행 안됨. 뺐더니 됨.

axis =1  안 써줘도 되는 이유?

 ✔︎ Series 형태일 때는 필요 없음
 ✔︎ DataFrame 형태일 때는 필수



Q2. 다 하고 나서 제대로 값 들어갔는지 확인하려고
df[df['Dep_Time'].hour == 0] 했더니 에러가 났다.

.dt.hour 해주니까 정상적으로 실행됨! 
왜 .dt 를 붙여줘야함??

✔︎ 가져오는 게 Series이고, 그 Series의 타입이 datetime일 때, 각 행을 개별적으로 가져옴
✔︎ 따라서 df['Dep_Time']은 Series 이기 때문에 dt 넣어줘야 한다.

def solution(x, n):
    answer = []
    for i in range(x,(x*n)+x,x):
      answer.append(i)
    return answer

solution(0,7)

2-3. 코드카타 16번

https://school.programmers.co.kr/learn/courses/30/lessons/12954

  • 첫 번째 시도
    • range() 설정의 오류 발생 
      • x=0 일 때 오류가 났다.

이유: range() 의 step 파라미터는 0이 될 수 없다..!

  • 두 번째 시도
    • 그럼 step을 다른 방식으로 표현해보자.
def solution(x, n):
    answer = []
    for i in range(n):
        value = x + (x*i)
        answer.append(value)
    return answer