Text 전처리(preprocessing)- english 영어편
*본 게시물은 21-2학기 연세대학교 일반대학원 온라인데이터수집과분석(이상엽 교수님) 수업 내용을 정리한 것입니다.
앞선 포스트에서 잠시 text 전처리는 영어와 한국어가 다르다고 말씀드렸었습니다. 언뜻 생각해도 영어는 알파벳이고 한글은 한글자모이니 다를 것 같기도 한데... 정확히 이야기하라고 하면 어렵습니다.ㅠㅠ
우선 텍스트 전처리가 무엇인지부터 설명하겠습니다.
텍스트 전처리
텍스트 전처리란, 텍스트 분석에 적합한 형태로 텍스트를 준비하는 것을 말합니다. 전처리를 거치게 되면 조금 더 정확한 분석 결과 도출이 가능합니다.
전처리 이후의 결과물은 '불용어가 제거된 특정한 품사들의 단어(ex. 명사)입니다. 비단 명사뿐만이 아니라도, 최종적으로 선택되는 단어들은 해당 문서의 특성을 잘 나타내는 것이어야 합니다. 따라서 최종적으로 선택되는 단어들은 분석 목적에 따라 달라지기도 합니다. 가령 topic 분석은 명사가 주, 감성분석은 형용사, 부사가 주가 됩니다.
전처리, 특히 영어의 전처리 주요 과정은 다음과 같습니다. 총 7개의 과정을 거치게 됩니다.
단계 | 설명 | nltk 예시 |
1. 정제 (cleaning) |
불필요한 기호(! . ' " ;...), 표현 제거 | .replace('!', '') re.sub('[^\w\d\s]','',content) |
2. 대소문자 변환 (case conversion) |
소문자 ⇔ 대문자 변환(영어만 해당) ∵분석 정확도를 높이기 위해서는 컴퓨터가 동일한 단어로 인식할 수 있게 해야하기 때문 |
.lower() .upper() |
3. 토큰화 (tokenization) |
단어, 혹은 토큰 단위로 자르기 영어의 경우 띄어쓰기 기준의 '단어'가 토큰입니다. 영어는 .split()으로 쉽게 할 수도 있습니다. |
.split() nltk.word_tokenize(content) |
4. 품사 태깅 (part-of-speech tagging) |
단어로 쪼갠 후에는 단어의 품사를 찾아줌 ∵ 원하는 품사의 단어들만 선택할 경우 분석 결과의 정확도가 향상됨 |
nltk.pos_tag(word_tokens) |
5. 원하는 품사만 찾기 | 원하는 품사의 단어들만 선택할 경우 분석 결과의 정확도가 향상됨 | wlem = nltk.WordNetLemmatizer() #생성자 함수를 통해 객체를 생성함 lemmatized_words = [] for word in NN_words: new_word = wlem.lemmatize(word) lemmatized_words.append(new_word) |
6. lemmatization, stemming | 원형(lemma) 혹은 어근(stem)을 찾음 | wlem = nltk.WordNetLemmatizer() new_word = wlem.lemmatize(word |
7. 불용어(stopword) 제거 | nltk는 기본적으로 언어에 따른 불용어 사전 제공 customized된 사전도 사용 가능 |
from nltk.corpus import stopwords stopwords_list = stopwords.words('english') customized_stopwords = ['str'] |
텍스트 전처리 단계에 따른 실제 코드
위의 총 7개 코드는 다음 예제 코드들로 확인하실 수 있습니다.
우선, nltk 및 txt 파일을 불러옵니다.
1
2
3
4
5
6
7
|
import nltk # natural language tool kit # 영어 전처리 모듈 불러오기
#nltk.download('all') # nltk의 모든 패키지를 다운받을 수 있습니다. 오류 생기면 이 코드를 돌리세요. # 0. 텍스트 파일 불러오기
with open('nytimes.txt', 'r', encoding='utf8') as f:
content = f.read()
print(content) # 기사 원문 확인
dir(content) # 본문(str 타입)에 적용 가능한 문자열 함수 리스트 확인
|
cs |
그 다음 1. 정제(cleaning)를 진행해보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 1. 정제(cleaning) 진행: 불필요한 symbols과 marks 제거하기
#1.1. 아주 기초적인 정제
content.replace('!','') # 불러온 txt 파일 본문에 있는 !들이 모두 없어집니다
cleaned_content = content.replace('.','').replace(',','').replace('!','') #순차, 반복적으로 replace() 적용도 가능
#그으런데 정제를 위해 계속 저렇게 번잡스럽게 replace를 붙일 수는 없습니다 ㅠㅠ
# 즉 replace()는 상당히 비효율적입니다.
# 또한 한번에 하나씩만의 기호가 삭제되기 때문에 삭제하는 기호가 무엇인지를 먼저 알아야 삭제가 가능합니다.
#1.2. regex를 이용한 정제
import re # 정제의 효율성을 위해 정규표현식(regex)를 사용해야 합니다.
cleaned_content = re.sub('[^\w\d\s]','',content) # 바꾸려는 대상, 바꿔지는 것, 본문 순으로 괄호 안 작성
print(cleaned_content)
|
cs |
정제를 진행한 이후에는 2. 대소문자 바꾸기를 진행합니다.
1
2
3
4
5
6
7
|
# 2. Case conversion; 대문자를 소문자로 바꾸기
cleaned_content = cleaned_content.upper() #모두 대문자로 변경
cleaned_content = cleaned_content.lower() #모두 소문자로 변경
#string은 immutable하기 때문에 새로운 변수에 저장해야 합니다.
# 새 변수에 저장하지 않을 경우 날아갑니다 ㅠㅠ
print(cleaned_content)
|
cs |
여기서부터는 tokenizing이 진행됩니다. 3. 토큰화를 해보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 3. Word tokenization(여기서부터 nltk 본격적 사용)
word_tokens = nltk.word_tokenize(cleaned_content) #토크나이징 결과가 리스트로 나옴
print(word_tokens)
'''
['hurray', 'for', 'the', 'hotblack', 'coffee', 'cafe',
'in', 'toronto', 'for', 'declining', 'to', 'offer'...]
'''
print(len(word_tokens)) # 토크나이징 된 단순한 언어 ㅜ
print(len(set(word_tokens))) # 중복을 없애서 unique한 단어 수(type)를 구해줌
# set은 중복을 지워줄 수 있는 자료형입니다!
|
cs |
토큰화된 단어들에 대해 POS를 태깅하는 4. 품사 태깅을 진행하겠습니다.
품사 태깅 결과는 튜플(단어, pos tag) 형태로 반환이 되며, 이를 활용하여 5. 원하는 품사만 리스트에 골라 담을 수도 있습니다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
# 4. POS tagging
'''
nltk가 제공하는 품사 정보 확인하기
Tag의 품사 정보 확인하기
여기에서도 확인 가능: https://www.ling.upenn.edu/courses/Fall_2003/ling001/penn_treebank_pos.html
nltk.help.upenn_tagset()
'''
# 영어의 경우는 nltk에서 제공하는 pos_tag() 함수를 사용해서 품사 태깅을 할 수 있습니다.
# pos_tag()의 입력값으로는 단어의 리스트가 들어가야 합니다.
# 즉 아까 토크나이징된 결과를 그대로 가져다 쓸 수 있습니다.
tokens_pos = nltk.pos_tag(word_tokens) #pos_tag function을 사용해서 pos태깅(단어를 가지는 리스트를 인자로 함)
print(tokens_pos)
# 5 명사 단어만 추출하기(regex 사용; NN.*?)
#tokens_pos는 튜플을 담은 리스트, 튜플이기 때문에 튜플의 (앞원소, 뒤원소)를 담는 2개의 변수가 필요함
# 때문에 for문에 word, pos가 들어간 것을 확인하실 수 있습니다.
NN_words = []
for word, pos in tokens_pos:
if 'NN' in pos:
NN_words.append(word)
print(NN_words)
|
cs |
품사 태깅을 진행한 후에는 6. lemmatization, stemming을 진행하게 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 6. Lemmatization, stemming
# nltk에서 제공되는 WordNetLemmatizer을 이용하는 경우
# 명사의 경우는 보통 복수 -> 단수 형태로 변형
# for more details, please see https://textminingonline.com/dive-into-nltk-part-iv-stemming-and-lemmatization
#생성자 함수(nltk.WordNetLemmatizer())를 통해 우선적으로 객체(wlem)를 생성함
wlem = nltk.WordNetLemmatizer()
lemmatized_words = []
for word in NN_words:
new_word = wlem.lemmatize(word)
lemmatized_words.append(new_word)
# lemmatized_words = [wlem.lemmatize(word) for word in NN_words]
print(lemmatized_words) # 단수로 다 바뀜
|
cs |
마지막으로는 7. stopword를 지정해서 불용어 처리를 진행합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
# 7. 불용어 ㅓ리
from nltk.corpus import stopwords # nltk가 불용어 사전을 제공
# 분석의 목적과 상관없이 일반적으로 불용어로 간주되는 단어들(a, the, this)
#분석의 목적에 따라서 불용어로 간주되는 단어들 : customizing 필요
stopwords_list = stopwords.words('english')
print(stopwords_list)
len(stopwords_list)
# 기본사전으로 불용어 제거: nltk에서 제공하는 불용어사전을 이용해서 불용어를 제거할 수 있습니다.
# 분석의 목적과 상관없이 일반적으로 불용어로 간주되는 단어들(a, the, this)이 포함됩니다.
from nltk.corpus import stopwords
stopwords_list = stopwords.words('english') #nltk에서 제공하는 불용어사전 중 영어 불용어사전 이용
#print('stopwords: ', stopwords_list)
unique_NN_words = set(lemmatized_words)
final_NN_words = lemmatized_words
# 불용어 제거2
for word in unique_NN_words:
if word in stopwords_list:
while word in final_NN_words:
final_NN_words.remove(word)
print(final_NN_words)
'''
['hurray', 'hotblack', 'coffee'...]
'''
# 제거하거자 하는 단어가 nltk에서 제공되는 사전에 포함되어 있지 않은 경우에, 아래와 같이 직접 만들어 사용할 수도 있습니다.
#분석의 목적에 따라서 불용어로 간주되는 단어들 : customizing 필요
customized_stopwords = ['be', 'today', 'yesterday', "it’s", "don’t", "new", "york", "time"] # 직접 만든 불용어 사전
unique_NN_words1 = set(final_NN_words)
for word in unique_NN_words1:
if word in customized_stopwords:
while word in final_NN_words: final_NN_words.remove(word)
print(final_NN_words)
|
cs |
이렇게 텍스트 데이터 분석을 위한 준비가 완료되었습니다.
텍스트 데이터 분석은 2가지 방법으로 이루어집니다. 그 중 하나인 ML 알고리즘을 사용하기 위해서는 vectorize가 필요하나, 다른 방법인 ML 알고리즘을 사용하지 않는 분석 방법은 벡터화를 필요로 하지 않습니다.
대표적인 방법 중 하나는 앞 포스트에서도 다뤘듯이 빈도분석입니다. 빈도분석은 연구 문제를 해결하기 위한 키워드 파악이 강하다는 점에서 중요한 분석입니다.
지금까지의 순서는 텍스트 데이터 준비(스크래핑 결과) > 전처리를 통해 불용어가 제거된 특정 품사/형태소들 >빈도분석입니다. 빈도분석을 위해서는 Counter를 주로 많이 사용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
from collections import Counter
# 빈도 세기
c = Counter(final_NN_words) # input type should be a list of words (or tokens)
print(c)
'''
Counter({'health': 11, 'people': 11, 'researcher': 7,...})
'''
# 상위 빈도 단어+빈도수 출력
print(c.most_common(10)) # .most_common(): #빈도수 기준 상위 k개 단어 출력
'''
[('health', 11), ('people', 11), ('researcher', 7)...]
'''
#빈도를 바탕으로 한 워드클라우드 생성
from wordcloud import WordCloud
import matplotlib.pyplot as plt
# total_words = ''
# for word in final_NN_words:
# total_words = total_words+' '+word
total_words = ' '.join(final_NN_words) #리스트 안에 있는 원소들이 '구분자'를 넣어서 하나의 string으로 합쳐집니다.
wordcloud = WordCloud(max_font_size=50, relative_scaling=.9)
wordcloud.generate(total_words)
plt.figure()
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis("off")
plt.show()
|
cs |
ㅇ
ㄴ
ㅇ
ㄴㅇ
ㄹㄴ
ㄹㄹ
ㄴㄹ