데이터 원본 출처
Target Data(CSV): 화장품의 종류, 브랜드, 제품명, 성분명 등이 포함된 csv 파일
- Source: Kaggle
- DownLoad: archive.zip
성분 사전: 화장품 전성분의 한글명, 표준영문명, 구명칭, 구 영문명이 포함된 pdf 파일
- Source: 대한 화장품협회
- DownLoad: 별첨1. 표준화명칭목록_220530.pdf
참고사항
- pdf를 Pandas DataFrame으로 변환하기 위해 Tabula 등의 Library를 사용할 수 있음.
- 혹, 설치시 오류가 발생하거나 코드 실행에 어려움을 겪을 경우 해당 pdf를 읽어 만든 pickle 파일 제공(ingredients_list.pkl)
- 해당 pickle파일은 List형태로 되어 있으며, List 내의 Elements는 각 페이지별 표가 dataframe 형태로 되어 있음
- ex: [df1, df2, df3, ...]
작업절차
- 파이썬으로 PDF 파일을 읽은 후, pickle 이라는 포맷의 데이터로 저장합니다.
- 파이썬으로 PDF 파일을 읽기 위해 필요한 모듈
pip install tabula-py
- pickle 파일이란?
- pickle은 파이썬에서 사용하는 딕셔너리, 리스트, 클래스 등의 자료형을 변환 없이 그대로 파일로 저장하고 이를 불러올 때 사용하는 모듈
- 설명출처: https://wikidocs.net/110788
import tabula
import pickle
# 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)
# Pickle File 불러오기
import pickle
with open('./datas/ingredients_list.pkl', 'rb') as f:
ingredients_list_pkl = pickle.load(f)
import pandas as pd
ingredients_df = pd.concat(ingredients_list_pkl, ignore_index=False)
ingredients_df
import re
# \r 바꾸는 함수
def changeR(text, lang):
text = str(text)
if lang == 'kor':
text = text.replace('\r', '')
return text
elif lang == 'eng':
pattern = r'\r[A-Z]' # \r대문자 형태는 띄어쓰기로 바꿈
match = re.search(pattern, text)
if match:
text = text.replace('\r', ' ')
text=text.replace('\r', '')
return text
'\r'뒤에 대문자가 오는 형태는 공백을 넣어야 하고, 뒤에 한글이나 특수문자(괄호)가 오는 형태는 아예 지워야 한다. 특수문자가 오는 형태를 힌트에서 명시하지 않아서 헷갈렸는데, output을 보면서 유추해서 조건을 찾았다.
check = kor[kor['표준 성분명'].str.contains('\r')]
check
# 결과 통해 \r 뒤에 괄호가 오는 형태 확인함
check = olEng[olEng['구영문명'].str.contains('\r')]
#check = pd.set_option('max_colwidth', 1000)
# 구영문명 바꾸기
olEng['구영문명'] = [changeR(value, 'eng') for value in olEng['구영문명']]
# 바뀐 것 확인
result = olEng['성분코드'] == 230
olEng[result]
result = olEng['성분코드'] == 3424
olEng[result]
# 나머지 바꾸기
stEng['표준 영문명'] = [changeR(value, 'eng') for value in stEng['표준 영문명']]
ingredients_df['표준 성분명'] = [changeR(value, 'kor') for value in ingredients_df['표준 성분명']]
ingredients_df['구명칭'] = [changeR(value, 'kor') for value in ingredients_df['구명칭']]
del ingredients_df['표준 영문명']
del ingredients_df['구영문명']
ingredients_df = pd.merge(ingredients_df, olEng, how='outer', on='성분코드')
ingredients_df = pd.merge(ingredients_df, stEng, how='outer', on='성분코드')
order = ['성분코드', '표준 성분명', '표준 영문명', '구명칭', '구영문명']
ingredients_df = ingredients_df[order]
replace_dict = {...}
ingredients_df['표준 영문명'] = ingredients_df['표준 영문명'].replace(replace_dict)
import re
# 수정 함수
def changeTar(text):
# condition 1
pattern1 = r'[.]$'
match1 = re.search(pattern1, text)
if match1:
text = text[:-1]
# condition 2
pattern2 = r'[.] May Contain.'
match2 = re.search(pattern2, text)
if match2:
n = match2.start()
text = text[:n]
return text
df_target['Ingredients'] = [changeTar(value) for value in df_target['Ingredients']]
# contidion 3
replace_str_dict = {...}
for old, new in replace_str_dict.items():
for i, ingredient in enumerate(df_target['Ingredients']):
df_target.loc[i, 'Ingredients'] = ingredient.replace(old, new)
target = pd.DataFrame()
target['Ingredients'] = df_target[['Ingredients']]
import re
# \r 바꾸는 함수
def changeR(text, lang):
text = str(text)
if lang == 'kor':
text = text.replace('\r', '')
return text
elif lang == 'eng':
pattern = r'\r[A-Z]' # \r대문자 형태는 띄어쓰기로 바꿈
match = re.search(pattern, text)
if match:
text = text.replace('\r', ' ')
text=text.replace('\r', '')
return text
def bracket(df, column):
inBracket = [[] for _ in range(len(df))]
for idx, _ in enumerate(inBracket):
inBracket[idx].append(df[column][idx])
inBracket = [[item] for item in inBracket]
return inBracket
targetLists = bracket(target, 'Ingredients')
ingredients_list = pd.DataFrame(targetLists, columns=['Ingredients List'])
df_target['Ingredients List'] = ingredients_list['Ingredients List']
(아래 예시를 참고)성분사전(Ingredients Dictionary)를 이용하여 Target DataFrame의 'Ingredients List'를 각 성분에 Mapping되는 'Code List'로 만들고, 'Code List' Column을 만들어 Code List를 각 행에 맞게 입력하세요.
조건1: Ingredients List에 대응하는 Code List의 순서는 같아야 합니다.
hint1: '표준 영문명'에서 찾지 못한다면 '구영문명'으로도 찾아보세요
hint2: 대문자-소문자 차이가 있을 수 있습니다.
# 표준 영문명 & 성분코드
stEngDict = ingredients_df.set_index('성분코드')['표준 영문명'].dropna().to_dict()
# 구영문명 & 성분코드
olEngDict = ingredients_df.set_index('성분코드')['구영문명'].dropna().to_dict()
# 모두 대문자로 바꾸기
def toUpper(dict):
return {key: value.upper() for key, value in dict.items()}
pd.set_option('max_colwidth', 10000)
df_target[['Ingredients List']]
olEngDict = toUpper(olEngDict)
stEngDict = toUpper(stEngDict)
splPattern = r'(?<!1,2-), \s*'
for iList in ingredients_list['Ingredients List']:
seperate = [item.strip() for item in re.split(splPattern, iList[0])]
iList[0] = seperate
# 모두 대문자로 바꾸기 - 리스트
def toUpperList(li):
return [item.upper() for item in li]
lists = []
for iList in ingredients_list['Ingredients List']:
upperSep = toUpperList(iList[0])
lists.append(upperSep)
print(lists)
# 표준 영문명에 있음
for code, name in stEngDict.items():
if name in upperSep:
index = upperSep.index(name)
upperSep[index] = code
# 구영문명에 있음
for code, name in olEngDict.items():
if name in upperSep:
index = upperSep.index(name)
upperSep[index] = code
print('-'*20)
for inlist in lists:
print(inlist)
# split 했던 것 다시 join
def joinList(spList):
return ', '.join(spList[0])
ingredients_list['Ingredients List'] = ingredients_list['Ingredients List'].apply(joinList)
# 원래 형태로
a = bracket(ingredients_list, 'Ingredients List')
a = pd.DataFrame(a, columns=['Ingredients List'])
df_target['Ingredients List'] = a['Ingredients List']
for inlist in codelist:
print(inlist)
df_target['Code List'] = [codelist[0], codelist[1], codelist[2], codelist[3], codelist[4]]
df_target
import copy
codelist3 = copy.deepcopy(codelist)
for codes in codelist3:
# 오름차순
codes.sort()
# 중복 제거
codes = list(set(codes))
codeDict = {key: 0 for key in codelist3[0]}
print(codeDict)
>>> {9: 0, 20: 0, 37: 0, 46: 0, 47: 0, 86: 0 ...
for key, value in codeDict.items():
for codes in codelist3:
for code in codes:
if code == key:
codeDict[key] += 1
print(codeDict)
>>> {9: 2, 20: 2, 37: 2, 46: 2, 47: 1, 86: 1, 317: 1, ...
finalDict = {key: value for key, value in codeDict.items() if value == 2}
finalCodes = []
for key in finalDict.keys():
finalCodes.append(key)
print(finalCodes)
finalCodes = finalCodes[:5]
print(finalCodes)
final_df = pd.DataFrame()
for code in finalCodes:
new_row = ingredients_df['성분코드'] == code
final_df = pd.concat([final_df, ingredients_df.loc[new_row]])