데이터 해독: 지저분하고 시끄러운 현실 세계에 대비한 훈련

데이터 해독: 지저분하고 시끄러운 현실 세계에 대비한 훈련

데이터 디톡스데이터 디톡스
작성자별 이미지

# 소개

우리 모두는 모델을 디버깅하는 데 몇 시간을 보냈지만 그것이 알고리즘이 아니라 행 47,832의 결과를 조작하는 잘못된 null 값이라는 것을 발견했습니다. 캐글 대회를 보면 데이터가 계층 불균형 문제 없이 깨끗하고 라벨이 잘 지정된 CSV로 생성된다는 인상을 주지만 실제로는 그렇지 않습니다.

이 문서에서는 실제 데이터 프로젝트를 사용하여 지저분한 실제 데이터 세트 처리를 준비하기 위한 4가지 실제 단계를 살펴보겠습니다.

# NoBroker 데이터 프로젝트: 실제 혼돈에 대한 실습 테스트

NoBroker는 중개인이 없는 시장에서 부동산 소유자와 임차인을 직접 연결하는 인도 부동산 기술(prop-tech) 회사입니다.

데이터 디톡스데이터 디톡스

이 데이터 프로젝트 NoBroker의 데이터 과학 직위에 대한 채용 과정에서 사용됩니다.

이 데이터 프로젝트에서 NoBroker는 주어진 기간 내에 부동산이 받을 상호 작용 수를 추정하는 예측 모델을 구축하기를 원합니다. 여기서 전체 프로젝트를 완료하지는 않지만 지저분한 실제 데이터에 대해 스스로를 훈련하는 방법을 찾는 데 도움이 될 것입니다.

여기에는 세 가지 데이터 세트가 있습니다.

  • property_data_set.csv
    • 유형, 위치, 편의 시설, 크기, 임대료 및 기타 주택 특징과 같은 부동산 세부 정보가 포함되어 있습니다.
  • property_photos.tsv
    • 숙소 사진이 포함되어 있습니다.
  • property_interactions.csv
    • 속성에 대한 상호 작용의 타임스탬프를 포함합니다.

# 깨끗한 인터뷰 데이터와 실제 생산 데이터 비교: 현실 점검

인터뷰 데이터 세트는 세련되고 균형이 잡혀 있으며 지루합니다. 실제 생산 데이터? 누락된 값, 중복 행, 일관되지 않은 형식, 자동 오류가 발생하여 금요일 오후 5시까지 파이프라인이 중단될 때까지 기다리는 것은 쓰레기통입니다.

3개의 테이블에 걸쳐 28,888개의 속성이 포함된 실제 세계의 혼란스러운 NoBroker 속성 데이터 세트를 살펴보겠습니다. 언뜻 보면 괜찮아 보입니다. 하지만 더 자세히 살펴보면 11,022개의 누락된 사진 URL(Uniform Resource Locator), 악성 백슬래시가 포함된 손상된 JSON 문자열 등을 발견할 수 있습니다.

이것은 깨끗함과 혼란스러움의 경계입니다. 깨끗한 데이터는 모델을 구축하도록 교육하지만, 생산 데이터는 어려움을 겪으면서 살아남도록 교육합니다.

우리는 자신을 훈련하기 위한 네 가지 방법을 살펴보겠습니다.

데이터 디톡스데이터 디톡스

# 연습 #1: 누락된 데이터 처리

데이터 누락은 짜증나는 일이 아닙니다. 결정의 포인트입니다. 행을 삭제하시겠습니까? 평균으로 채우시겠습니까? 알 수 없음으로 신고하시겠습니까? 대답은 데이터가 누락된 이유와 손실을 감당할 수 있는 정도에 따라 다릅니다.

NoBroker 데이터 세트에는 세 가지 유형의 누락 데이터가 있었습니다. 그만큼 photo_urls 열에는 28,888개의 행 중 11,022개의 값이 누락되었습니다. 이는 데이터 세트의 38%입니다. 코드는 다음과 같습니다.

출력은 다음과 같습니다.

데이터 디톡스데이터 디톡스

이러한 행을 삭제하면 귀중한 자산 기록이 지워집니다. 대신에 해결책은 누락된 사진을 마치 아무것도 없는 것처럼 처리하고 계속 진행하는 것이었습니다.

def correction(x):
    if x is np.nan or x == 'NaN':
        return 0  # Missing photos = 0 photos
    else:
        return len(json.loads(x.replace('\\', '').replace('{title','{"title')))
pics['photo_count'] = pics['photo_urls'].apply(correction)

다음과 같은 숫자 열의 경우 total_floor (23개 누락) 및 다음과 같은 범주형 열 building_type (38개 누락) 전략은 전가되었습니다. 수치적 공백은 평균으로 채우고 범주형 공백은 최빈값으로 채웁니다.

for col in x_remain_withNull.columns:
    x_remain[col] = x_remain_withNull[col].fillna(x_remain_withNull[col].mean())
for col in x_cat_withNull.columns:
    x_cat[col] = x_cat_withNull[col].fillna(x_cat_withNull[col].mode()[0])

첫 번째 결정: 의심 없이 삭제하지 마세요!

패턴을 이해하세요. 누락된 사진 URL은 무작위가 아닙니다.

# 연습 #2: 이상값 감지

이상치가 항상 오류인 것은 아니지만 항상 의심스럽습니다.

21개의 욕실, 800년 된 건물 또는 40,000평방피트의 공간을 갖춘 부동산을 상상할 수 있습니까? 당신은 꿈의 장소를 찾았거나 누군가 데이터 입력 오류를 범했습니다.

NoBroker 데이터 세트는 이러한 위험 신호로 가득 차 있었습니다. 박스 플롯은 100년이 넘는 부동산, 10,000평방피트(평방피트)를 초과하는 크기, 350만 개를 초과하는 예금 등 여러 열에 걸쳐 극단적인 값을 보여주었습니다. 일부는 합법적인 고급 부동산이었습니다. 대부분은 데이터 입력 실수였습니다.

df_num.plot(kind='box', subplots=True, figsize=(22,10))
plt.show()

출력은 다음과 같습니다.

데이터 디톡스데이터 디톡스

해결책은 IQR(사분위간 범위) 기반 이상값 제거였습니다. 이는 IQR의 2배를 초과하는 값에 플래그를 지정하는 간단한 통계 방법입니다.

이를 처리하기 위해 먼저 해당 이상값을 제거하는 함수를 작성합니다.

def remove_outlier(df_in, col_name):
    q1 = df_in[col_name].quantile(0.25)
    q3 = df_in[col_name].quantile(0.75)
    iqr = q3 - q1
    fence_low = q1 - 2 * iqr
    fence_high = q3 + 2 * iqr
    df_out = df_in.loc[(df_in[col_name] = fence_low)]
    return df_out  # Note: Multiplier changed from 1.5 to 2 to match implementation.

그리고 이 코드를 숫자 열에 실행합니다.

df = dataset.copy()
for col in df_num.columns:
    if col in ['gym', 'lift', 'swimming_pool', 'request_day_within_3d', 'request_day_within_7d']:
        continue  # Skip binary and target columns
    df = remove_outlier(df, col)
print(f"Before: {dataset.shape[0]} rows")
print(f"After: {df.shape[0]} rows")
print(f"Removed: {dataset.shape[0] - df.shape[0]} rows ({((dataset.shape[0] - df.shape[0]) / dataset.shape[0] * 100):.1f}% reduction)")

출력은 다음과 같습니다.

데이터 디톡스데이터 디톡스

이상값을 제거한 후 데이터 세트는 17,386개 행에서 15,170개로 줄어들어 모델을 정상적으로 유지하면서 데이터의 12.7%가 손실되었습니다. 절충안은 그만한 가치가 있었습니다.

다음과 같은 대상 변수의 경우 request_day_within_3d삭제 대신 캡핑을 사용했습니다. 극단적인 이상치가 예측을 왜곡하는 것을 방지하기 위해 10보다 큰 값은 10으로 제한되었습니다. 다음 코드에서는 이전과 이후의 결과도 비교합니다.

def capping_for_3days(x):
    num = 10
    return num if x > num else x
df['request_day_within_3d_capping'] = df['request_day_within_3d'].apply(capping_for_3days)
before_count = (df['request_day_within_3d'] > 10).sum()
after_count = (df['request_day_within_3d_capping'] > 10).sum()
total_rows = len(df)
change_count = before_count - after_count
percent_change = (change_count / total_rows) * 100
print(f"Before capping (>10): {before_count}")
print(f"After capping (>10): {after_count}")
print(f"Reduced by: {change_count} ({percent_change:.2f}% of total rows affected)")

결과는?

데이터 디톡스데이터 디톡스

더 깔끔한 배포, 더 나은 모델 성능, 더 적은 디버깅 세션.

# 연습 #3: 중복 및 불일치 처리

복제는 쉽습니다. 불일치는 어렵습니다. 중복 행은 단지 df.drop_duplicates(). 서로 다른 세 가지 시스템에 의해 변조된 JSON 문자열과 같이 일관되지 않은 형식에는 탐지 작업이 필요합니다.

NoBroker 데이터 세트에는 내가 본 최악의 JSON 불일치 중 하나가 있었습니다. 그만큼 photo_urls 열은 유효한 JSON 배열을 포함해야 했지만 대신 잘못된 형식의 문자열, 누락된 따옴표, 이스케이프된 백슬래시 및 임의의 후행 문자로 채워졌습니다.

text_before = pics['photo_urls'][0]
print('Before Correction: \n\n', text_before)

수정 전 내용은 이렇습니다.

데이터 디톡스데이터 디톡스

이 수정 사항에서는 구문 분석하기 전에 형식을 수정하기 위해 여러 문자열 교체가 필요했습니다. 코드는 다음과 같습니다.

text_after = text_before.replace('\\', '').replace('{title', '{"title').replace(']"', ']').replace('],"', ']","')
parsed_json = json.loads(text_after)

출력은 다음과 같습니다.

데이터 디톡스데이터 디톡스

수정 후 JSON은 실제로 유효하고 구문 분석 가능했습니다. 이런 종류의 문자열 조작을 수행하는 가장 깔끔한 방법은 아니지만 작동합니다.

문자열로 저장된 날짜, 범주형 값의 오타, 부동 소수점으로 저장된 숫자 ID 등 모든 곳에서 일관되지 않은 형식을 볼 수 있습니다.

해결책은 JSON 형식에서 했던 것처럼 표준화입니다.

# 연습 #4: 데이터 유형 유효성 검사 및 스키마 검사

모든 것은 데이터를 로드할 때 시작됩니다. 날짜가 문자열이거나 숫자가 객체라는 것을 나중에 알아내는 것은 시간 낭비입니다.

NoBroker 프로젝트에서는 프로젝트가 올바른 데이터 유형을 사전에 적용했기 때문에 CSV 읽기 자체 중에 유형이 검증되었습니다. 팬더 매개변수. 코드는 다음과 같습니다.

data = pd.read_csv('property_data_set.csv')
print(data['activation_date'].dtype)  
data = pd.read_csv('property_data_set.csv',
                   parse_dates=['activation_date'], 
                   infer_datetime_format=True, 
                   dayfirst=True)
print(data['activation_date'].dtype)

출력은 다음과 같습니다.

데이터 디톡스데이터 디톡스

상호작용 데이터세트에도 동일한 검증이 적용되었습니다.

interaction = pd.read_csv('property_interactions.csv',
    parse_dates=['request_date'], 
    infer_datetime_format=True, 
    dayfirst=True)

이는 좋은 관행일 뿐만 아니라 다운스트림 모든 작업에 필수적이었습니다. 프로젝트에서는 활성화 날짜와 요청 날짜 사이의 날짜 및 시간 차이를 계산해야 했습니다.

따라서 날짜가 문자열인 경우 다음 코드에서는 오류가 발생합니다.

num_req['request_day'] = (num_req['request_date'] - num_req['activation_date']) / np.timedelta64(1, 'D')

스키마 검사를 통해 구조가 변경되지 않는지 확인하지만 실제로는 시간이 지남에 따라 분포가 변경되는 경향이 있으므로 데이터도 표류하게 됩니다. 입력 비율을 약간 다르게 하여 이러한 드리프트를 모방하고 모델이나 해당 검증이 해당 드리프트를 감지하고 대응할 수 있는지 확인할 수 있습니다.

# 청소 단계를 문서화하기

3개월이 지나면 왜 제한했는지 기억하지 못할 것입니다. request_day_within_3d to 10. 지금부터 6개월 후에 팀원이 이상값 필터를 제거하여 파이프라인을 중단할 것입니다. 1년 안에 모델이 생산에 들어갈 예정인데, 그 모델이 왜 실패하는지 아무도 이해하지 못할 것입니다.

문서화는 선택사항이 아닙니다. 이것이 재현 가능한 파이프라인과 그렇지 않을 때까지 작동하는 부두 스크립트의 차이입니다.

NoBroker 프로젝트는 설명 및 목차와 함께 코드 주석 및 구조화된 노트북 섹션의 모든 변환을 문서화했습니다.

# Assignment
# Read and Explore All Datasets
# Data Engineering
Handling Pics Data
Number of Interactions Within 3 Days
Number of Interactions Within 7 Days
Merge Data
# Exploratory Data Analysis and Processing
# Feature Engineering
Remove Outliers
One-Hot Encoding
MinMaxScaler
Classical Machine Learning
Predicting Interactions Within 3 Days
Deep Learning
# Try to correct the first Json
# Try to replace corrupted values then convert to json
# Function to correct corrupted json and get count of photos

버전 관리도 중요합니다. 청소 논리의 변경 사항을 추적합니다. 중간 데이터세트를 저장합니다. 시도한 것과 효과가 있었던 것에 대한 변경 로그를 유지하십시오.

목표는 완벽함이 아니다. 목표는 명확성입니다. 결정을 내린 이유를 설명할 수 없다면 모델이 실패했을 때 이를 방어할 수도 없습니다.

# 최종 생각

깨끗한 데이터는 신화입니다. 최고의 데이터 과학자는 지저분한 데이터 세트에서 도망치는 사람이 아닙니다. 그들은 그들을 길들이는 방법을 아는 사람들입니다. 훈련 전에 누락된 값을 발견합니다.

예측에 영향을 미치기 전에 이상값을 식별할 수 있습니다. 테이블을 조인하기 전에 스키마를 확인합니다. 그리고 다음 사람이 0부터 시작할 필요가 없도록 모든 것을 적습니다.

완벽한 데이터에서는 실질적인 영향을 미치지 않습니다. 이는 잘못된 데이터를 처리하면서도 여전히 기능적인 것을 구성하는 능력에서 비롯됩니다.

따라서 데이터 세트를 처리해야 할 때 Null 값, 끊어진 문자열, 이상값이 표시되더라도 걱정하지 마세요. 당신이 보는 것은 문제가 아니라 실제 데이터 세트에 대해 당신의 기술을 보여줄 수 있는 기회입니다.

네이트 로시디 데이터 과학자이자 제품 전략 분야의 전문가입니다. 그는 또한 분석을 가르치는 부교수이기도 하며 데이터 과학자가 상위 기업의 실제 인터뷰 질문을 사용하여 인터뷰를 준비하는 데 도움이 되는 플랫폼인 StrataScratch의 창립자이기도 합니다. Nate는 취업 시장의 최신 동향에 대해 글을 쓰고, 인터뷰 조언을 제공하고, 데이터 과학 프로젝트를 공유하고, SQL의 모든 것을 다룹니다.

출처 참조

Post Comment

당신은 놓쳤을 수도 있습니다