EDA Level Test 02

JERRY·2025년 3월 18일

EDA

목록 보기
17/27
post-thumbnail

EDA Level Test 02 ⭐️⭐️⭐️

WeekData AnalysisDifficultyThis Notebook
1서울시 인구 데이터⭐️
2화장품 성분 데이터⭐️⭐️⭐️👈
3올림픽 데이터⭐️⭐️
4국가별 인터넷 사용률 데이터⭐️⭐️
5전국 박물관/미술관 정보 표준 데이터⭐️⭐️⭐️
6서울시 흡연율 통계 데이터⭐️⭐️
7웹크롤링⭐️
8서울시 지하철 호선별 역별 유/무임 승하차 인원 정보 데이터⭐️⭐️
9서울시 교통사고 현황 (사고유형별) 통계 데이터⭐️⭐️
10서울시 교통사고 현황 (사고유형별) 통계 + 서울시 차량통행속도 (구별/월별) 통계 + 서울시 인구밀도 (구별) 통계⭐️⭐️⭐️

문제 소개 및 데이터 준비 단계

  • 해당 EDA Level TEST는 기업연계프로젝트 실무 사례 중 일부분입니다.
  • 따라서, 난이도가 조금 있을 수 있습니다.
  • 이점 참고하시어, 학습에 도움이 되셨으면 좋겠습니다 🙂
  • 총 3단계 데이터 분석 상황 제시

    • 총점 100점
  • 1단계 DataFrame 불러오기 & 기초 전처리

  • 2단계 전처리

  • 3단계 Mapping

데이터 원본 출처

Target Data(CSV): 화장품의 종류, 브랜드, 제품명, 성분명 등이 포함된 csv 파일

성분 사전: 화장품 전성분의 한글명, 표준영문명, 구명칭, 구 영문명이 포함된 pdf 파일

참고사항

  • pdf를 Pandas DataFrame으로 변환하기 위해 Tabula 등의 Library를 사용할 수 있음.
  • 혹, 설치시 오류가 발생하거나 코드 실행에 어려움을 겪을 경우 해당 pdf를 읽어 만든 pickle 파일 제공(ingredients_list.pkl)
  • 해당 pickle파일은 List형태로 되어 있으며, List 내의 Elements는 각 페이지별 표가 dataframe 형태로 되어 있음
    • ex: [df1, df2, df3, ...]

import pandas as pd
# 채점을 위한 코드입니다. 반드시 실행해주세요.
from grading import *

df_target = pd.read_csv('./datas/cosmetics.csv')
df_target = df_target.iloc[:5] # 5개의 row만 사용합니다
df_target


PDF 파일에 담긴 정보를, 파이썬으로 불러와서 작업하고 싶은 상황!

  • 화장품 성분 데이터 분석을 하고 싶은데..
  • 잘 정리된 파일을 찾았는데 그게 PDF파일인 경우!

PDF 파일 내용 확인

  • 직접 열어서 꼭 확인해보세요 ^^
  • 성분사전 표준화명칭목록 데이터
    • 성분코드
    • 표준 성분명
    • 표준 영문명
    • 구명칭
    • 구영문명
  • 총 1460page, 23249만개 데이터

작업절차

  • 파이썬으로 PDF 파일을 읽은 후, pickle 이라는 포맷의 데이터로 저장합니다.
    • 파이썬으로 PDF 파일을 읽기 위해 필요한 모듈
      • pip install tabula-py
    • pickle 파일이란?
      • pickle은 파이썬에서 사용하는 딕셔너리, 리스트, 클래스 등의 자료형을 변환 없이 그대로 파일로 저장하고 이를 불러올 때 사용하는 모듈
      • 설명출처: https://wikidocs.net/110788
import tabula
import pickle
# module 'tabula' has no attribute 'read_pdf' 
에러 발생하면, 아래 코드 주석 해제 후 실행
from tabula.io import read_pdf

# Tabula로 PDF 읽기 -> DataFrame List
ingredients_list = tabula.read_pdf('./datas/별첨1. 표준화명칭목록_220530.pdf', pages='all', lattice=True)

# DataFrame List를 Pickle로 저장
with open('./datas/ingredients_list.pkl', 'wb') as f:
    pickle.dump(ingredients_list, f)

tabula 실행이 안됩니다!!!

EDA Lv Test Doc 에서 TroubleShooting ➡️ EDA Level Test 02 tabula module error 부분 확인

  • 위 문서 절차대로 진행해도 오류가 발생하시는 분들은, 지금 단계를 생략 해주세요
  • 아래 문제 시작 부분부터 진행하시면 됩니다!

문제 시작! 🏃🏻

1단계: 성분사전 불러오기 & DataFrame 합치기, 수정하기

Load Pickle File
  • Load: Pickle
  • List to DataFrame: Pandas

위 데이터 준비 단계에서 에러가 발생하시는 분들은, 지금 단계부터 시작해주시면 됩니다!


from google.colab import drive
drive.mount('/content/drive')
# Pickle File 불러오기
import pickle

with open('./datas/ingredients_list.pkl', 'rb') as f:
    ingredients_list_pkl = pickle.load(f)

문제 1-1) 성분사전 DataFrame 만들기(10점)

  • Pickle을 이용해 Load한 ingredients_list_pkl를 하나의 DataFrame으로 합치세요.

    • 조건1: 데이터는 index 기준으로 합치세요.

    • 조건2: join없이 단순히 합치세요.

    • hint1: 20375개의 행이 만들어져야 합니다.

    • hint2: ingredients_list_pkl는 여러 DataFrame들이 Element로 들어있는 List입니다.

  • 완료 후 결과 dataframe 변수를 check_01_01 함수에 입력하여 채점하세요.


ingredients_list_pkl


# 1-1
# pd.concat : 리스트에 있는 여러 DataFrame을 하나의 DataFrame으로 합침
# ignore_index=T/F: 이 옵션은 합치는 과정에서 원래의 index를 유지할 것인지를 결정합니다. 
# False-DataFrame들의 index가 그대로 유지/True-index는 리셋되어 0부터 다시 시작

ingredients_df = pd.concat(ingredients_list_pkl, ignore_index=False)

ingredients_df


문제 1-2) 성분사전 DataFrame 내의 Data 수정하기 - 1(15점)

  • 이 성분 사전 DataFrame에는 원본에는 없는 '\r' 이 아래와 같이 data 사이에 끼어 있습니다. 이 '\r'을 삭제하세요

    • hint1: '\r'를 대체 할 때 한글('표준 성분명', '구명칭')의 경우 띄어쓰기가 없고, 영어('표준 영문명', '구영문명)의 경우 띄어쓰기를 해야 합니다.

    • hint2: 모든 영문명의 경우 대문자로 시작합니다.

  • 완료 후 결과 dataframe 변수를 check_01_02 함수에 입력하여 채점하세요.


#1-2
ingredients_df.reset_index(drop=True, inplace=True)

ingredients_df['표준 성분명'] = ingredients_df['표준 성분명'].replace('\r', "",regex = True)
ingredients_df['구명칭'] = ingredients_df['구명칭'].replace('\r', "", regex = True)
ingredients_df['표준 영문명'] = ingredients_df['표준 영문명'].replace('\r', " ", regex = True)
ingredients_df['구영문명'] = ingredients_df['구영문명'].replace('\r', " ", regex = True)

ingredients_df


문제 1-3) 성분사전 DataFrame 내의 Data 수정하기 - 2(15점)

  • pdf를 dataframe으로 전환하면서 일부 누락된 데이터가 있습니다. 아래 cell의 replace_dict는 현재값(key):변경할값(value)의 쌍으로 이루어져 있습니다. 이 replace_dict를 이용하여 성분사전 dataframe '표준 영문명' column의 값을 변경하세요.

    • 참고: replace_dict의 내용만 변경하면 됩니다. 다른 누락사항을 확인하여 변경할 필요는 없습니다.
  • 완료 후 결과 dataframe 변수를 check_01_03 함수에 입력하여 채점하세요.


# 1-3
replace_dict = {
    'Acetobacter/Lycium Chinense Fruit/Rehmannia Glutinosa Root/Cuscuta Chinensis Fruit/Cistanche Deserticola/Zanthoxylum Piperitumit/Chrysanthemum Morifolium Fruit/Poria Cocos/ Cinnamomum Cassia': 'Acetobacter/Lycium Chinense Fruit/Rehmannia Glutinosa Root/Cuscuta Chinensis Fruit/Cistanche Deserticola/Zanthoxylum Piperitumit/Chrysanthemum Morifolium Fruit/Poria Cocos/ Cinnamomum Cassia Ferment',
    'Saccharomyces/Licorice Root/Rehmannia Glutinosa Root/Angelica Gigas Root/Ophiopogon Japonicus Root/Atractylodes Macrocephala Root/Paeonia Lactiflora Root/Anemarrhena Asphodeloides Root/Fraxinus Excelsior Bark/Asparagus Cochinchinensis/Phellodendron Amurense': 'Saccharomyces/Licorice Root/Rehmannia Glutinosa Root/Angelica Gigas Root/Ophiopogon Japonicus Root/Atractylodes Macrocephala Root/Paeonia Lactiflora Root/Anemarrhena Asphodeloides Root/Fraxinus Excelsior Bark/Asparagus Cochinchinensis/Phellodendron Amurense Bark Ferment Extract',
    'Bacillus/Lycium Chinense Fruit/Rehmannia Glutinosa Root/Cuscuta Chinensis Fruit/Cistanche Deserticola/Zanthoxylum Piperitum Fruit/Chrysanthemum Morifolium Fruit/Poria Cocos/ Cinnamomum Cassia': 'Bacillus/Lycium Chinense Fruit/Rehmannia Glutinosa Root/Cuscuta Chinensis Fruit/Cistanche Deserticola/Zanthoxylum Piperitum Fruit/Chrysanthemum Morifolium Fruit/Poria Cocos/ Cinnamomum Cassia Ferment',
    'Bifida/Angelica Gigas/Angelica Tenuissima Root/Antler Velvet/Rehmannia Glutinosa Root/Atractylodes Japonica Rhizome/Cnidium Officinale Root/Cordyceps Sinensis/Ledebouriella Seseloides Root/Licorice Root/Paeonia Lactiflora Root/Panax Ginseng': 'Bifida/Angelica Gigas/Angelica Tenuissima Root/Antler Velvet/Rehmannia Glutinosa Root/Atractylodes Japonica Rhizome/Cnidium Officinale Root/Cordyceps Sinensis/Ledebouriella Seseloides Root/Licorice Root/Paeonia Lactiflora Root/Panax Ginseng Root/Phellinus Linteus/Scutellaria Baicalensis Root Ferment',
    'Leuconostoc/Lycium Chinense Fruit/Rehmannia Glutinosa Root/Cuscuta Chinensis Fruit/Cistanche Deserticola/Zanthoxylum Piperitum Fruit/Chrysanthemum Morifolium Fruit/Poria Cocos/ Cinnamomum Cassia': 'Leuconostoc/Lycium Chinense Fruit/Rehmannia Glutinosa Root/Cuscuta Chinensis Fruit/Cistanche Deserticola/Zanthoxylum Piperitum Fruit/Chrysanthemum Morifolium Fruit/Poria Cocos/ Cinnamomum Cassia Ferment',
    'Saccharomyces/Anemarrhena Asphodeloides Root/Angelica Gigas Root/Asparagus Cochinchinensis/Atractylodes Macrocephala Root/Fraxinus Excelsior Bark/Licorice Root/Ophiopogon Japonicus Root/Paeonia Lactiflora Root/Phellodendron Amurense': 'Saccharomyces/Anemarrhena Asphodeloides Root/Angelica Gigas Root/Asparagus Cochinchinensis/Atractylodes Macrocephala Root/Fraxinus Excelsior Bark/Licorice Root/Ophiopogon Japonicus Root/Paeonia Lactiflora Root/Phellodendron Amurense Bark Ferment Extract',
    'Saccharomyces/Camellia Japonica Flower/Castanea Crenata Shell/Diospyros Kaki Leaf/Paeonia Suffruticosa Root/Rhus Javanica/Sanguisorba Officinalis Root Extract': 'Saccharomyces/Camellia Japonica Flower/Castanea Crenata Shell/Diospyros Kaki Leaf/Paeonia Suffruticosa Root/Rhus Javanica/Sanguisorba Officinalis Root Extract Ferment Filtrate',
    'Lactobacillus/Honeysuckle Flower/Licorice Root/Morus Alba Root/Pueraria Lobata Root/Schisandra Chinensis Fruit/Scutellaria Baicalensis Root/Sophora Japonica Flower': 'Lactobacillus/Honeysuckle Flower/Licorice Root/Morus Alba Root/Pueraria Lobata Root/Schizandra Chinensis Fruit/Scutellaria Baicalensis Root/Sophora Japonica Flower Extract Ferment Filtrate',
    'Lactobacillus/Lycium Chinense Fruit/Rehmannia Glutinosa Root/Cuscuta Chinensis Fruit/Cistanche Deserticola/Zanthoxylum Piperitum Fruit/Chrysanthemum Morifolium Fruit/Poria Cocos/Cinnamomum Cassia': 'Lactobacillus/Lycium Chinense Fruit/Rehmannia Glutinosa Root/Cuscuta Chinensis Fruit/Cistanche Deserticola/Zanthoxylum Piperitum Fruit/Chrysanthemum Morifolium Fruit/Poria Cocos/Cinnamomum Cassia Ramulus Bark Ferment Filtrate',
    'Saccharomyces/Lycium Chinense Fruit/Rehmannia Glutinosa Root/Cuscuta Chinensis Fruit/Cistanche Deserticola/Zanthoxylum Piperitum Fruit/Chrysanthemum Morifolium Fruit/Poria Cocos/ Cinnamomum Cassia': 'Saccharomyces/Lycium Chinense Fruit/Rehmannia Glutinosa Root/Cuscuta Chinensis Fruit/Cistanche Deserticola/Zanthoxylum Piperitum Fruit/Chrysanthemum Morifolium Fruit/Poria Cocos/ Cinnamomum Cassia Ferment',
}

ingredients_df['표준 영문명'] = ingredients_df['표준 영문명'].replace(replace_dict)

ingredients_df


2단계: Target Data 수정하기

문제 2-1) Target DataFrame 중 Ingredients Column 내의 Data 수정하기(20점)

  • Kaggle에서 가져오는 데이터는 수정을 필요로 하는 경우가 있습니다. 제시된 Target DataFrame 또한 성분사전과 다른 표현을 쓰는 경우가 있어 해당 내용에 대해 수정을 하고자 합니다. 다음 조건에 맞게 Ingredients Column의 데이터를 수정하세요.

    • 조건1: 맨 끝에 마침표('.')가 있다면 마지막 마침표만 제거하세요

      • ex) 'Algae (Seaweed) Extract. Sea Salt.' -> 'Algae (Seaweed) Extract. Sea Salt'
    • 조건2: '. May Contain'를 포함하고 있다면, '. May Contain' 이후의 데이터를 제거하세요

      • ex) 'Algae (Seaweed) Extract. May Contain: Sea Salt, Fragrance' -> 'Algae (Seaweed) Extract'
    • 조건3: 아래의 replace_str_dict는 현재값(key):변경할값(value)의 쌍으로 이루어져 있습니다. 이 replace_str_dict를 이용하여 데이터를 변경하세요

      • 참고: replace_str_dict의 내용만 변경하면 됩니다. 다른 누락사항을 확인하여 변경할 필요는 없습니다.
  • 완료 후 결과 dataframe 변수를 check_02_01 함수에 입력하여 채점하세요.


replace_str_dict = {
    'Algae (Seaweed) Extract': 'Algae Extract',
    'Citrus Aurantifolia (Lime) Extract': 'Citrus Aurantifolia (Lime) Fruit Extract',
    'Eucalyptus Globulus (Eucalyptus) Leaf Oil': 'Eucalyptus Globulus Leaf Oil',
    'Galactomyces Ferment Filtrate (Pitera)': 'Galactomyces Ferment Filtrate',
    'Bacillus/Soybean/ Folic Acid Ferment Extract': 'Bacillus/Folic Acid/Soybean Ferment Extract',
    'Butyrospermum Parkii (Shea Butter)': 'Butyrospermum Parkii (Shea) Butter',
    'Sea Salt/Maris Sal/Sel Marin': 'Sea Salt',
    'Parfum/Fragrance': 'Fragrance|Perfume|Parfum',
    ', Fragrance': ', Fragrance|Perfume|Parfum',
    }
# 조건을 처리하는 함수 정의
def process_ingredients(ingredient, replace_str_dict):
    # 조건 1: 맨 끝의 마침표 제거
    if ingredient.endswith('.'):
        ingredient = ingredient[:-1]
    
    # 조건 2: '. May Contain' 이후 데이터 제거
    if '. May Contain' in ingredient:
        ingredient = ingredient.split('. May Contain')[0]
    
    # 조건 3: replace_str_dict에 따른 문자열 변경
    for key, value in replace_str_dict.items():
        ingredient = ingredient.replace(key, value)
    
    return ingredient

# 'Ingredients' 컬럼에 조건을 적용
df_target['Ingredients'] = df_target['Ingredients'].apply(lambda x: process_ingredients(x, replace_str_dict))

df_target


문제 2-2) Target DataFrame 중 'Ingredients' Column Data 변환하기(10점)

  • 다음 조건에 맞게 앞서 수정한 'Ingredients' Column의 데이터를 변환하여 'Ingredients List' Column에 입력하세요.

    • 조건1: 'Ingredients' Column의 각 데이터를 ', '(쉼표+띄어쓰기)로 분리하여 List로 변환하세요

      • ex) ' Algae (Seaweed) Extract, Sea Salt ' -> [' Algae (Seaweed) Extract', ' Sea Salt ']
    • 조건2: 조건1에서 변경한 list의 각 Element 앞뒤의 공백이 있다면 공백을 삭제하세요

      • ex) [' Algae (Seaweed) Extract', ' Sea Salt '] -> ['Algae (Seaweed) Extract', 'Sea Salt']
    • 조건3: 'Ingredients List' Column을 새로 생성하여 조건1과 조건2에서 만든 list를 각 행에 맞게 입력하세요

  • 완료 후 결과 dataframe 변수를 check_02_02 함수에 입력하여 채점하세요.


# 조건 1: 각 데이터를 ', '(쉼표+띄어쓰기)로 분리
df_target['Ingredients List'] = df_target['Ingredients'].apply(lambda x: x.split(', '))

# 조건 2: 각 Element 앞뒤의 공백이 있다면 공백을 삭제
df_target['Ingredients List'] = df_target['Ingredients List'].apply(lambda x: [ingredient.strip() for ingredient in x])

df_target


3단계: 성분사전(Ingredients Dictionary)을 이용하여 Mapping하기

문제 3-1) Target DataFrame 의 'Ingredients List' Column를 Mapping하여 'Code List' Column 만들기(15점)

  • (아래 예시를 참고)성분사전(Ingredients Dictionary)를 이용하여 Target DataFrame의 'Ingredients List'를 각 성분에 Mapping되는 'Code List'로 만들고, 'Code List' Column을 만들어 Code List를 각 행에 맞게 입력하세요.

    - 조건1: Ingredients List에 대응하는 Code List의 순서는 같아야 합니다.

    • hint1: '표준 영문명'에서 찾지 못한다면 '구영문명'으로도 찾아보세요

    • hint2: 대문자-소문자 차이가 있을 수 있습니다.

    • 참고: 'Code'는 성분사전(Ingredients Dictionary)의 '성분코드'를 의미합니다.(Index가 아닙니다)

  • 완료 후 결과 dataframe 변수를 check_03_01 함수에 입력하여 채점하세요.


def map_code_list(ingredients_list):

    code_list = []

    # 표준영문명 또는 구영문명에 해당하는 성분코드 조회
    for ingredient in ingredients_list:
        code = ingredients_df[(ingredient.lower() == ingredients_df['표준 영문명'].str.lower()) | 
                              (ingredient.lower() == ingredients_df['구영문명'].str.lower())]['성분코드'].values
        
        # 성분코드 존재 시 code list에 'code'추가
        if len(code) > 0:
            code_list.append(code[0])
        # 성분코드 미존재 시 code list에 'none'추가
        else:
            code_list.append(None)

    return code_list
    
df_target['Code List'] = df_target['Ingredients List'].apply(lambda x: map_code_list([ingredient.strip() for ingredient in x]))

df_target


문제 3-2) 다음 조건을 만족하는 code들을 찾아 그 code들에 해당하는 DataFrame을 구하세요(15점)

  • (아래 예시를 참고) Target DataFrame의 Code List를 각 행 내에서 중복 없이 모두 합쳐 두 번 나온 수를 오름차순으로 정렬하고, 첫번째부터 다섯번째까지의 수들을 찾아 성분사전(Ingredients Dictionary)를 이용하여 해당 Code들의 DataFrame을 구하세요.

    • 예시 DataFrame

    • 이 DataFrame에서 나타난 Code 중 각 행 내에서 중복 없이 두 번 나온 Code는 [3, 9, 23, 34, 57, 234] 이고, 이 중 오름차순으로 첫번째부터 다섯번째까지의 수는 [3, 9, 23, 34, 57] 입니다.

      • 참고
        • '3'의 경우 2번 행과 3번 행에서 나왔습니다.
        • 0번 행의 '12'의 경우 중복하여 나왔으므로, 한 번 나온 것으로 count 합니다.
    • 성분사전(Ingredients Dictionary)에서 해당 code들의 DataFrame은 아래와 같습니다.

    • 조건1: 중복 code는 없어야 합니다.

    • 조건2: '두' 번 나온 수, 오름차순, 첫번째부터 다섯번째까지 다시 한번 확인하세요.

    • hint1: sorted(list, key=lambda x: func(x))

    • hint2: set()

    • hint3: list.count()

    • 참고: 'Code'는 성분사전(Ingredients Dictionary)의 '성분코드'를 의미합니다.(Index가 아닙니다)

  • 완료 후 결과 dataframe 변수를 check_03_02 함수에 입력하여 채점하세요.


from collections import Counter

# 1단계: 각 행의 'Code List'에서 중복 값 제거
def remove_duplicates(code_list):
    return list(set(code_list))

df_target['Code List'] = df_target['Code List'].apply(remove_duplicates)

# 2단계: 모든 Code List에서 등장한 코드들의 출현 횟수 세기
def get_repeated_codes(df_target):
    # 모든 코드 목록 추출 후 등장 횟수 계산
    all_codes = [code for sublist in df_target['Code List'] for code in sublist]
    code_counts = Counter(all_codes)
    
    # 두 번 나온 코드만 필터링(None 제외)
    repeated_codes = [code for code, count in code_counts.items() if count == 2]
    repeated_codes = [code for code in repeated_codes if code is not None]
    
    # 상위 5개의 코드 추출 
    repeated_codes.sort()
    return repeated_codes[:5]

top_five_codes = get_repeated_codes(df_target)

# 3단계: 상위 5개의 코드에 해당하는 성분을 성분사전에서 찾기
result_df = ingredients_df[ingredients_df['성분코드'].isin(top_five_codes)]

result_df

0개의 댓글