0. 목표
어절 단위로 형태소분석 되어있는 텍스트파일로 corpus 만든 후 TF-IDF 작성
column은 ㄱㄴㄷ순으로 오름차순 정렬할 것
각 문서를 벡터로 변환한 후 SVM 모델로 카테고리 분류
1. 카테고리 값을 string으로 갖는 list category list 생성
.txt 파일들의 이름이 사전식 정렬을 하면 제대로 정렬되지 않아서 natural sort 함수를 사용했다.
# cell 1
import re
def atoi(text):
return int(text) if text.isdigit() else text
def natural_keys(text):
'''
alist.sort(key=natural_keys) sorts in human order
http://nedbatchelder.com/blog/200712/human_sorting.html
(See Toothy's implementation in the comments)
'''
return [ atoi(c) for c in re.split(r'(\d+)', text) ]
출처:
https://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside
How to correctly sort a string with a number inside?
I have a list of strings containing numbers and I cannot find a good way to sort them. For example I get something like this: something1 something12 something17 something2 something25 something29 ...
stackoverflow.com
path에 제일 상위 디렉토리를 입력하고 하위 폴더들의 이름을 읽어 category list에 저장한 후 정렬한다.
# cell 2
import os
def readCategory(directory):
"""
카테고리를 포함하는 폴더의 directory를 입력받아 오름차순으로 정렬 후 카테고리 list를 반환
ex) readCategory('2022_Fall_Student_Data/8/Corpus/Input_Data')
args:
directory`string`: 읽을 파일의 디렉토리
return:
category`str list`: 카테고리이름 list
"""
folder_list = os.listdir(directory)
category = []
# 카테고리 string 저장할 list
for i in folder_list:
if not i.endswith('.DS_Store'):
category.append(i)
# .DS_Store 빼고 category에 추가
category.sort(key=natural_keys)
return category
category = readCategory('/Users/Input_Data')
print(category)
# cell 3
def readFileName(directory):
"""
읽을 파일의 디렉토리를 입력받아 DataFrame으로 반환
ex) readFileName('2022_Fall_Student_Data/8/Corpus/Input_Data')
arg:
directory`string`: 읽을 파일의 디렉토리
return:
fileName2D`str 2D list`: 읽은 파일명 저장된 table (row: categoryIdx, col: 파일 이름)
"""
fileName2D = []
# row: categoryIdx, column: 해당 카테고리의 파일 이름
for categoryIdx in range(len(category)):
path = directory+'/'+category[categoryIdx]
file_list = os.listdir(path)
#해당 카테고리 안에 있는 파일명이 담긴 string list
txt_list = []
for i in file_list:
if i.endswith('.txt') and not i.endswith('.DS_Store'):
#파일 형식이 .txt로 끝나는 파일 이름만 배열에 추가
txt_list.append(i)
txt_list.sort(key=natural_keys)
# natural sort
fileName2D.append(txt_list)
# print("카테고리마다의 파일 개수")
# for i in fileName2D:
# print(len(i), end=" ")
return fileName2D
print(fileName2D)
2. 파일 읽은 후 DTM DataFrame 생성
다음으로는 리스트의 각 파일을 읽어 중복되지 않게 리스트에 추가하여 corpus를 만들어야 한다.
문서는 위와 같이 생겼는데, 형태소 분석이 되어있는 '\t' 기준 뒷 부분만 사용할 것이다.
명사류 형태소는 NNG, NNP만 고려할 것이기 때문에 '+'를 기준으로 뒷 부분은 버린다.
# cell 4
import pandas as pd
import numpy as np
def makeTrainDTM(directory, category, fileName2D):
DTM = pd.DataFrame(columns=range(1))
DTM.columns = ['category']
# term table
docNum = 0
#문서 번호
for categoryIdx in range(len(category)):
for fileIdx in range(len(fileName2D[categoryIdx])):
file = open(directory+'/'+category[categoryIdx]+'/'+fileName2D[categoryIdx][fileIdx], "r")
lines = file.readlines()
#파일을 한 줄씩 읽어서 string으로 list에 저장
docRow = "Doc"+str(docNum)
#row 이름 ex) Doc3
DTM.loc[docRow] = [0 for i in range(len(DTM.columns))]
# 새로운 row를 만들어 0으로 채움
# ex)Doc3 = [0, 0, 0, 0](열 개수만큼 0을 만들어 list로 반환)
DTM.loc[docRow, 'category'] = categoryIdx
# 해당 문서의 카테고리 값에 categoryIdx 저장
for line in lines:
morp = line.split('\t')[-1].split('+')[0].strip('\n')
# '\t'를 기준으로 뒤, '+'를 기준으로 앞의 string을 잘라 list에 저장
if morp == '':
continue
# 빈 string이면 무시
if morp in DTM.columns:
DTM.loc[docRow,morp] += 1
else:
DTM[morp] = 0
DTM.loc[docRow,morp] += 1
# DTM columns에서 중복 확인, 중복 있으면 값만 1 증가시킴, 없으면 열 추가 후 값 1 대입
docNum += 1
return DTM
#28분
DTM = makeTrainDTM('Input_Data', category, fileName2D)
DTM
파일 경로에 파일명을 붙일 때 파일명 앞에 /를 붙여야 제대로 된 디렉토리 주소가 된다.
파일을 한 줄씩 읽어서 string list로 저장해주는 .readlines()를 사용하였는데,
.readlines() 함수는 '\n'까지 스트링에 저장해주기 때문에 strip('\n')으로 삭제했다.
DTM DataFrame을 만들때 column을 하나 만들어주었는데
빈 DataFrame을 생성했을 때 행과 열이 둘 다 없으면 데이터를 추가하거나 수정할 수 없기 때문이다.
최소한 열 정보라도 갖고 있어야 행 데이터를 추가할 수 있기 때문에 첫 column인 category를 추가해주었다.
제일 앞에 있는 column은 카테고리를 표시하는데, train 시킬 때 label의 역할을 할 것이다.
매 문서마다 새로운 row를 만들어주면서
corpus에 동일한 형태소가 없으면 column을 추가하는 방식이다.
그래서 대략적으로 왼쪽 위부터 계단식으로 내려오는 형태를 띄는 걸 볼 수 있다.
코드: