


말뭉치 >> 문서 >> 문단 >> 문장 >> 단어 >> 형태소
→ 텍스트를 데이터 관점에 바라보면 크기 순으로 문서(document), 단락(paragraph), 문장(sentence), 단어(word), 형태소(morpheme)로 분류할 수 있다.
더 알아보기
대개의 경우 토큰은 단어를 의미하기도 하지만 하나의 글자 단위일 수도 있고 형태소 단위일 수도 있다. 토큰은 통계적인 기법을 적용하여 빈도를 분석하여 전체적인 맥락을 분석하던지, 문서에 나타난 주제(topic)나 감정(Sentiment)을 추정하는 기법들이 개발되어 있다.
→ 탐색적 접근방법으로 형태소 분석기로 명사들을 추출하여 특정 단어들의 빈도를 카운트하여 시각화해서 보여주는 워드클라우딩 기법 등을 통하여 전체 문서의 맥락이나 말뭉치를 파악하는 방법들을 사용하기도 한다.
텍스트 수집 ⇨ 전처리 ⇨ 토큰화 ⇨ 특성 추출 ⇨ 데이터 분석
기준은 "내가 어떤 데이터를 분석하느냐"에 따라 다름!
텍스트 데이터를 분석하려면 무조건 "전처리 - 토큰화 - 수치화" 과정 거쳐야 함 (필수)
나눈다 == 토큰화한다, 하나의 벡터로 변환 == 수치화한다






단어사전은 항상 만들어야 함!



phrases: 구(의미 있는 단위) / clause: 절(주어와 동사가 같이 있는 문장 성분)



# 라이브러리 불러오기
import numpy as np
import pandas as pd
# 데이터 불러오기
text_train = pd.read_csv("./data/ratings_train.txt", delimiter="\t")
text_test = pd.read_csv("./data/ratings_test.txt", delimiter="\t")
# 데이터 크기 확인
text_train.shape, text_test.shape
((150000, 3), (50000, 3))
# 데이터 정보 확인
text_train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150000 entries, 0 to 149999
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 150000 non-null int64
1 document 149995 non-null object
2 label 150000 non-null int64
dtypes: int64(2), object(1)
memory usage: 3.4+ MB
text_test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 50000 entries, 0 to 49999
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 50000 non-null int64
1 document 49997 non-null object
2 label 50000 non-null int64
dtypes: int64(2), object(1)
memory usage: 1.1+ MB
# 정보를 확인했을 때 train에 5개의 결측치, test에 3개의 결측치 확인 → 결측치 제거: dropna
text_train.dropna(inplace=True)
text_test.dropna(inplace=True)
text_train.shape, text_test.shape
((149995, 3), (49997, 3))
# 리뷰 데이터 추출
rv_train = text_train["document"]
# 토큰화 작업: 띄어쓰기 단위로 모든 문장을 쪼개서 리스트에 담아주자!
temp = [i.split(' ') for i in rv_train]
# CounterVectorize를 위해 2차원 리스트를 1차원으로 변경
token_list = []
for i in temp:
token_list += i
# Counter 기능을 활용하여 단어 빈도 측정
from collections import Counter
cnt = Counter(token_list)
# 의미 있는, 자주 등장하는 단어 확인: 상위 40개 정도
cnt.most_common(40)
[('영화', 10825),
('너무', 8239),
('정말', 7791),
('진짜', 5929),
('이', 5059),
('영화.', 3598),
('왜', 3285),
('더', 3260),
('이런', 3249),
('그냥', 3237),
('수', 2945),
('영화를', 2759),
('잘', 2644),
('다', 2615),
('보고', 2557),
('좀', 2449),
('영화는', 2426),
('그', 2421),
('영화가', 2418),
('본', 2298),
('최고의', 2219),
('ㅋㅋ', 2019),
('내가', 2000),
('없는', 1957),
('이건', 1889),
('이렇게', 1828),
('완전', 1780),
('평점', 1760),
('봤는데', 1746),
('있는', 1739),
('좋은', 1726),
('이거', 1710),
('이게', 1676),
('보는', 1600),
('평점이', 1595),
('내', 1595),
('다시', 1583),
('그리고', 1547),
('참', 1508),
('많이', 1478)]
# 워드 클라우드 설치
!pip install wordcloud
# 시각화를 위한 한국어 설치: 설치 후 반드시 세션 다시 시작해야 함
!apt-get install -y fonts-nanum*
!sudo fc-cache -fv
!rm ~/.cache/matplotlib -rf
# 세션 다시 시작할 때에는 위 코드 주석 처리하기!
from wordcloud import WordCloud
import matplotlib.pyplot as plt
# 워드 클라우드 객체 생성
wc = WordCloud(
background_color="white"
, font_path="/usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf"
, random_state=23
)
# 워드 클라우드는 한 문장을 받아 띄어쓰기 기준으로 알아서 분석하기 때문에 token_list 형태를 바꿔야 함
one_str = ' '.join(token_list)
cloud = wc.generate_from_text(one_str)
plt.imshow(cloud)
plt.axis("off")
plt.show()

my_list = [[1, 2], [3, 4], [5]]
flattened_list = sum(my_list, [])
print(flattened_list) # 출력: [1, 2, 3, 4, 5]
my_list = [[1, 2], [3, 4], [5]]
flattened_list = [item for sublist in my_list for item in sublist]
print(flattened_list) # 출력: [1, 2, 3, 4, 5]
import itertools
my_list = [[1, 2], [3, 4], [5]]
flattened_list = list(itertools.chain(*my_list))
print(flattened_list) # 출력: [1, 2, 3, 4, 5]
import numpy as np
my_list = [[1, 2], [3, 4], [5]]
flattened_array = np.array(my_list).flatten()
print(flattened_array.tolist()) # 출력: [1, 2, 3, 4, 5]
def flatten_list_recursive(data):
result = []
for item in data:
if isinstance(item, list):
result.extend(flatten_list_recursive(item))
else:
result.append(item)
return result
my_list = [[1, [2, 3]], [4, [5, [6]]]]
flattened_list = flatten_list_recursive(my_list)
print(flattened_list) # 출력: [1, 2, 3, 4, 5, 6]
→ 위에서 소개된 방법 외에도 다양한 방법으로 리스트의 차원을 축소할 수 있습니다. 상황에 맞게 적절한 방법을 선택하여 사용하면 됩니다.


| 토큰화 도구 | 설명 | 장점 | 단점 | 사용하면 좋은 글 유형 |
|---|---|---|---|---|
| KoNLPy - Okt | 트위터에서 개발한 한국어 형태소 분석기 | - 속도가 빠름 - 줄임말, 신조어에 강함 - 명사, 동사, 형용사 태깅 지원 | - 분석 정확도가 상대적으로 낮음 - 긴 문장에서 오탈자나 오류 발생 가능 | - SNS 댓글, 트위터, 카톡 대화 등 구어체 데이터 |
| KoNLPy - Mecab | 일본 Mecab을 한국어에 맞게 수정한 형태소 분석기 | - 속도가 가장 빠름 - 비교적 높은 정확도 - 메모리 사용량 적음 | - 윈도우 환경에서 설치가 까다로움 - 사용자 사전 추가 필요 | - 뉴스, 문서 요약, 법률 문서 등 정형 데이터 |
| KoNLPy - Kkma | 서울대에서 개발한 형태소 분석기 | - 구문 분석 기능 포함 - 문장 분리 기능 제공 | - 속도가 느림 - 명사 추출 시 과도한 분할 발생 가능 | - 학술 논문, 문서 분석, 보고서 |
| KoNLPy - Komoran | Shineware에서 개발한 형태소 분석기 | - 딥러닝 기반으로 정확도 높음 - 사용자 사전 확장 가능 | - 속도가 느린 편 | - 뉴스 기사, 공식 문서, 챗봇 |
| KoNLPy - Hannanum | KAIST에서 개발한 한국어 형태소 분석기 | - 어절 분석 및 띄어쓰기 보정 기능 제공 - 구문 분석 가능 | - 속도가 느림 - 비교적 오래된 기술로 최신 트렌드 반영 부족 | - 논문, 신문 기사, 공식 문서 |
| Kiwi | 한국어 형태소 분석기 (KyTea 기반) | - 최신 기술 적용 - 속도와 정확도 균형 잡힘 - 신조어, 띄어쓰기 보정 기능 제공 | - 비교적 새로운 도구라 참고 자료 부족 | - 검색 엔진, 챗봇, 감성 분석 |
# 한국어 형태소 분석기 -> 코엔엘파이(KoNLPy): 분석기 모음집
!pip install konlpy
# 다양한 형태소 분석기 도구 사용해보기: Kiwi는 KoNLPy 안에 없어서 설치해야 함
!pip install kiwipiepy
# Mecab 설치하기: KoNLPy에 있지만 사용하려면 추가 설치 필요
!git clone https://github.com/SOMJANG/Mecab-ko-for-Google-Colab.git
!bash ./Mecab-ko-for-Google-Colab/install_mecab-ko_on_colab_light_220429.sh
# 다양한 형태소 분석기 불러오기 (분석 용도에 맞게 사용)
from kiwipiepy import Kiwi
from konlpy.tag import Kkma
from konlpy.tag import Okt
from konlpy.tag import Mecab
# kiwi → 띄어쓰기 교정이 가능!
kiwi = Kiwi()
kiwi.space("띄어쓰기없이작성된텍스트입니다.키위형태소분석기의띄어쓰기성능을파악해보겠습니다.")
띄어쓰기 없이 작성된 텍스트입니다. 키위 형태소 분석기의 띄어쓰기 성능을 파악해 보겠습니다.
# 첫 번째 리뷰 데이터를 가지고 간단하게 확인
rv_train[0]
아 더빙.. 진짜 짜증나네요 목소리
kiwi.tokenize(rv_train[0])

# 유의미한 품사들만 리스트에 담기
from tqdm import tqdm
from kiwipiepy.utils import Stopwords # 키위가 제공하는 불용어 사전 활용
stopwords = Stopwords()
total = []
for doc in tqdm(rv_train):
result = kiwi.tokenize(doc, stopwords=stopwords)
temp = [] # 일반 명사, 형용사, 동사만을 담아줄 리스트
for token in result:
if token.tag in ["NNG", "VV", "VA"]:
temp.append(token.form) # 토큰화된 텍스트 데이터 누적 저장
total.append(temp)
# 워드 클라우드 생성을 위하여 하나의 문자열로 변경
token_list = []
for token in total:
token_list += token # token_list.append(" ".join(token))
cnt = Counter(token_list)
cnt.most_common(40)
[('영화', 58375),
('좋', 12535),
('재밌', 9031),
('있', 7438),
('연기', 7192),
('나오', 7030),
('만들', 6937),
('최고', 6634),
('평점', 6302),
('주', 5544),
('생각', 5519),
('스토리', 5350),
('드라마', 5235),
('배우', 4928),
('감동', 4877),
('나', 4772),
('가', 4342),
('내용', 4238),
('알', 4227),
('재미', 4169),
('감독', 4165),
('재미있', 3992),
('시간', 3647),
('쓰레기', 3554),
('재미없', 3427),
('사랑', 3420),
('모르', 3234),
('보이', 3043),
('작품', 2984),
('정도', 2778),
('마지막', 2736),
('액션', 2692),
('기대', 2596),
('남', 2561),
('많', 2502),
('장면', 2439),
('느끼', 2437),
('처음', 2275),
('최악', 2261),
('돈', 2260)]
# 워드 클라우드 객체 생성
wc = WordCloud(
background_color="white"
, font_path="/usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf"
, random_state=23
)
one_str = ' '.join(token_list)
cloud = wc.generate_from_text(one_str)
plt.imshow(cloud)
plt.axis("off")
plt.show()

# 형태소 분석기 객체 생성
kkma = Kkma()
okt = Okt()
mecab = Mecab()
# 문장 내에서 품사 분류 진행(각 형태소 분석기마다 알아서 추출)
# morphs() 함수: 입력 값을 형태소 단위로 토크나이징
kkma.morphs("아버지가 방에 들어가신다.")
# 높임을 나타내는 선어말어미 '-시-'까지 쪼갬 → 잘게 쪼개는 대신 연산 속도가 느리다.
['아버지', '가', '방', '에', '들어가', '시', 'ㄴ다', '.']
okt.morphs("아버지가 방에 들어가신다.")
['아버지', '가', '방', '에', '들어가신다', '.']
mecab.morphs("아버지가 방에 들어가신다.")
['아버지', '가', '방', '에', '들어가', '신다', '.']
# 토큰화된 단어들 품사 확인
kkma.pos("아버지가 방에 들어가신다.")
[('아버지', 'NNG'),
('가', 'JKS'),
('방', 'NNG'),
('에', 'JKM'),
('들어가', 'VV'),
('시', 'EPH'),
('ㄴ다', 'EFN'),
('.', 'SF')]
okt.pos("아버지가 방에 들어가신다.")
[('아버지', 'Noun'),
('가', 'Josa'),
('방', 'Noun'),
('에', 'Josa'),
('들어가신다', 'Verb'),
('.', 'Punctuation')]
mecab.pos("아버지가 방에 들어가신다.")
[('아버지', 'NNG'),
('가', 'JKS'),
('방', 'NNG'),
('에', 'JKB'),
('들어가', 'VV'),
('신다', 'EP+EF'),
('.', 'SF')]
임베딩(Embedding): 자연어를 벡터로 바꾼 결과 → 문맥 간 의미를 파악해 수치화
# 단순 카운트 기반 벡터화 도구
from sklearn.feature_extraction.text import CountVectorizer
# 객체 생성
cv = CountVectorizer()
test_list = [
"안녕하세요 저는 최영화입니다"
, "오늘 점심은 미니돈까스입니다."
, "오늘 날씨가 너무 좋아요!"
, "즐거운 월요일이지만 집에 가고 싶어요"
, "수업이 너무 재미있어요 즐거워용~"
]
# 토큰화(띄어쓰기(단어) 단위로 토큰화), 단어사전 구축
cv.fit(test_list)
# 단어사전 확인 → 띄어쓰기로 토큰화 한 후 한 개의 토큰에 1개의 인덱스 부여
cv.vocabulary_
# 문맥 무시
{'안녕하세요': 6,
'저는': 10,
'최영화입니다': 16,
'오늘': 7,
'점심은': 11,
'미니돈까스입니다': 3,
'날씨가': 1,
'너무': 2,
'좋아요': 12,
'즐거운': 13,
'월요일이지만': 8,
'집에': 15,
'가고': 0,
'싶어요': 5,
'수업이': 4,
'재미있어요': 9,
'즐거워용': 14}
# 단어 사전을 기반으로 벡터화
test_transform = cv.transform(test_list)
test_transform
<Compressed Sparse Row sparse matrix of dtype 'int64'
with 19 stored elements and shape (5, 17)>
# 텍스트 데이터 벡터화 결과
test_transform.toarray()
array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0],
[0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0]])

# 'rv_cv' 이름을 가지는 벡터화 도구 생성
rv_cv = CountVectorizer()
# 단어사전 구축
rv_cv.fit(rv_train)
train_dic = rv_cv.vocabulary_
len(train_dic)
# 약 30만 개의 단어 사전 구축
# 벡터화
X_train = rv_cv.transform(text_train["document"])
X_test = rv_cv.transform(text_test["document"]) # test 데이터에만 있는 단어는 모두 0으로 처리됨
# 정답 데이터
y_train = text_train["label"]
y_test = text_test["label"]
print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)
(149995, 293366) (149995,)
(49997, 293366) (49997,)
.shapeX_train
<Compressed Sparse Row sparse matrix of dtype 'int64'
with 1074805 stored elements and shape (149995, 293366)>
X_train.toarray()
array([[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]])
# 분류 모델(긍정/부정) → 이진분류
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score # 교차 검증 진행 ★★★
# 모델 객체 생성
model = LogisticRegression(max_iter=1000)
# max_iter: 최대 반복 횟수 → 기본 100, 데이터가 많을 경우 제대로 학습 X
# max_iter = 1000으로 늘려서 학습
# 모델 학습
model.fit(X_train, y_train)

# 교차검증 → 평가
cv_score = cross_val_score(model, X_train, y_train, cv=5)
cv_score, cv_score.mean()
(array([0.81332711, 0.81222707, 0.8089603 , 0.80932698, 0.81622721]),
np.float64(0.8120137337911263))
# 평가 → 정확도 확인~
model.score(X_test, y_test)
0.814868892133528
# 우리가 작성한 리뷰를 통한 확인
test = [
"즐거운 월요일 행복하고 재밌는 수업 중입니다~"
, "힘듭니다"
, "너무 좋습니다"
]
# 수치화
sample = rv_cv.transform(test)
# 0: 부정, 1: 긍정
model.predict(sample)
array([1, 0, 1])
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
train = pd.read_csv("./data/unsmile_train_v1.0.tsv", sep="\t")
test = pd.read_csv("./data/unsmile_valid_v1.0.tsv", sep="\t")
train.shape, test.shape
((15005, 12), (3737, 12))
github에서 바로 불러오는 것도 가능합니다.
# 학습 데이터 다운로드 & 불러오기 train_url = "https://raw.githubusercontent.com/smilegate-ai/korean_unsmile_dataset/main/unsmile_train_v1.0.tsv" train = pd.read_csv(train_url, sep='\t') # 검증 데이터 다운로드 & 불러오기 valid_url = "https://raw.githubusercontent.com/smilegate-ai/korean_unsmile_dataset/main/unsmile_valid_v1.0.tsv" valid = pd.read_csv(valid_url, sep='\t')
train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 15005 entries, 0 to 15004
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 문장 15005 non-null object
1 여성/가족 15005 non-null int64
2 남성 15005 non-null int64
3 성소수자 15005 non-null int64
4 인종/국적 15005 non-null int64
5 연령 15005 non-null int64
6 지역 15005 non-null int64
7 종교 15005 non-null int64
8 기타 혐오 15005 non-null int64
9 악플/욕설 15005 non-null int64
10 clean 15005 non-null int64
11 개인지칭 15005 non-null int64
dtypes: int64(11), object(1)
memory usage: 1.4+ MB
test.info()
0초
test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3737 entries, 0 to 3736
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 문장 3737 non-null object
1 여성/가족 3737 non-null int64
2 남성 3737 non-null int64
3 성소수자 3737 non-null int64
4 인종/국적 3737 non-null int64
5 연령 3737 non-null int64
6 지역 3737 non-null int64
7 종교 3737 non-null int64
8 기타 혐오 3737 non-null int64
9 악플/욕설 3737 non-null int64
10 clean 3737 non-null int64
11 개인지칭 3737 non-null int64
dtypes: int64(11), object(1)
memory usage: 350.5+ KB
!pip install emoji
import emoji
emoji.demojize("DM으로 연락 바랍니다🙏🙏")
DM으로 연락 바랍니다:folded_hands::folded_hands:
emoji.replace_emoji("DM으로 연락 바랍니다🙏🏻🙏")
DM으로 연락 바랍니다
emoji.demojize("너무 재미있었어요 😊🤣😍")
너무 재미있었어요 :smiling_face_with_smiling_eyes::rolling_on_the_floor_laughing::smiling_face_with_heart-eyes:
emoji.replace_emoji("너무 재미있었어요 😊🤣😍")
너무 재미있었어요
X_train = train["문장"]
X_test = test["문장"]
y_train = train.loc[:, "여성/가족":"clean"].values.argmax(axis=1)
y_test = test.loc[:, "여성/가족":"clean"]
X_train.shape, y_train.shape # 머신러닝: 출력 값 1이어야 함 → 10을 1로 줄여주기
((15005,), (15005, 10))
y_train = train.loc[:, "여성/가족":"clean"].values.argmax(axis=1) # 최댓값을 가지는 인덱스 번호 반환
y_test = test.loc[:, "여성/가족":"clean"].values.argmax(axis=1) # 정답 데이터를 1차원으로 변경
print(y_train)
X_train.shape, y_train.shape, X_test.shape, y_test.shape
[9, 6, 9, ..., 0, 8, 4]
((15005,), (15005,), (3737,), (3737,))
# 형태소 분석 도구 불러오기
!pip install konlpy
# Mecab 설치하기
!git clone https://github.com/SOMJANG/Mecab-ko-for-Google-Colab.git
!bash ./Mecab-ko-for-Google-Colab/install_mecab-ko_on_colab_light_220429.sh
from konlpy.tag import Kkma, Okt, Mecab
kkma = Kkma()
okt = Okt()
mecab = Mecab()
okt.morphs(X_train[0])
['일안하는', '시간', '은', '쉬고싶어서', '그런게', '아닐까']
okt.morphs(X_train[0], norm=True, stem=True)
['이다', '시간', '은', '쉬다', '그렇다', '아니다']
from tqdm import tqdm
X_train_morphs = []
for doc in tqdm(X_train):
result = okt.morphs(doc, norm=True, stem=True)
X_train_morphs.append(result)
100%|██████████| 15005/15005 [01:55<00:00, 129.46it/s]
X_test_morphs = [] # 형태소 분리가 끝난 문장이 담길 리스트
for doc in tqdm(X_test):
result = okt.morphs(doc, norm=True, stem=True)
X_test_morphs.append(result)
100%|██████████| 3737/3737 [00:25<00:00, 144.92it/s]
정규 표현식(Regular Expression) 위키독스
?: -(하이픈)이 있을수도 없을수도 있다 (선택적){숫자}: 숫자만큼 반복[ ]: 대괄호 안의 문자 하나와 매칭# 정규 표현식 사용을 도와주는 라이브러리
import re
# 규칙 설정
p = re.compile("010-?[0-9]{4}-?[0-9]{4}") # ?: 앞의 문자가 존재할 수도 있고, 존재하지 않을 수도 있습니다. (문자가 0개 또는 1개)
# 해당 문자열 내에 포함되어 있는 모든 일지하는 패턴 추출
p.search("내 전화번호가 궁금하니? 내 전화번호는 010-0000-0000이야")
<re.Match object; span=(22, 35), match='010-0000-0000'>
p.search("내 전화번호가 궁금하니? 내 전화번호는 010-00000000야")
<re.Match object; span=(22, 34), match='010-00000000'>
p.search("내 전화번호가 궁금하니? 내 전화번호는 010-00000000야") # 마지막 숫자가 3개뿐이라서 작동 안 함
✅ 정규 표현식 문법
| 특수 문자 | 설명 |
|---|---|
. | 한 개의 임의의 문자 (단, 줄바꿈 문자 \n 제외) |
? | 앞의 문자가 있을 수도, 없을 수도 있음 (0개 또는 1개) |
* | 앞의 문자가 0개 이상 반복됨 |
+ | 앞의 문자가 1개 이상 반복됨 |
^ | 문자열의 시작을 의미 |
$ | 문자열의 끝을 의미 |
{숫자} | 정확히 해당 숫자만큼 반복함 |
{숫자1, 숫자2} | 숫자1 이상 숫자2 이하 만큼 반복함 |
{숫자,} | 숫자 이상 반복함 |
[...] | 대괄호 안의 문자들 중 하나와 매치. 예: [abc] → a 또는 b 또는 c |
[a-z] | 소문자 알파벳 범위와 매치 |
[a-zA-Z] | 모든 알파벳과 매치 |
[^문자] | 해당 문자를 제외한 문자와 매치 |
A\|B | A 또는 B (논리적 OR) |
\\\ | 역 슬래쉬 문자 자체를 의미 |
\\d | 모든 숫자를 의미 == [0-9] |
\\D | 숫자를 제외한 모든 문자를 의미 == [^0-9] |
\\s | 공백을 의미 == [ \t\n\r\f\v] |
\\S | 공백을 제외한 문자를 의미 == [^ \t\n\r\f\v] |
\\w | 문자 또는 숫자를 의미 == [a-zA-Z0-9] |
\\W | 문자 또는 숫자가 아닌 문자를 의미 == [^a-zA-Z0-9] |
포인트:
- 파이썬에서 정규식 패턴을 문자열로 쓸 때 역슬래시를 올바르게 표현해야 합니다.
- 보통
r"\s"처럼 raw string(앞에 r을 붙임) 형식으로 쓰는 게 좋습니다.
- raw string을 쓰면 역슬래시를 두 번 안 써도 돼서 헷갈림이 적어요.
pattern = re.compile("\\s")처럼 써도 작동하지만, 추천하는 방법은re.compile(r"\s")입니다.
정리:- 공백(스페이스, 탭, 줄바꿈 등)을 찾고 싶으면 re.compile(r"\s") 사용!
✅ 정규 표현식 모듈 함수
| 함수 | 설명 |
|---|---|
re.compile() | 정규표현식을 컴파일하는 함수입니다. 패턴이 빈번하게 사용될 경우 미리 컴파일하여 속도와 편의를 높일 수 있습니다. |
re.search() | 문자열 전체에 대해 정규표현식과 매치되는지를 검색합니다. |
re.match() | 문자열의 시작 부분이 정규표현식과 매치되는지를 검색합니다. |
re.split() | 정규표현식을 기준으로 문자열을 분리하여 리스트로 반환합니다. |
re.findall() | 문자열에서 정규표현식과 매치되는 모든 경우의 문자열을 리스트로 반환합니다. 없다면 빈 리스트를 반환합니다. |
re.finditer() | 문자열에서 정규표현식과 매치되는 모든 경우의 문자열에 대한 이터레이터 객체를 반환합니다. |
re.sub() | 문자열에서 정규표현식과 일치하는 부분을 다른 문자열로 대체합니다. |
re.compile()과re.search()는 꼭 기억해 주세요.
# 패턴 생성 → 한 개의 자음이 반복되는 패턴 삭제
s = re.compile("[ㅋㅎㄷㅇㅠㅜ?.,!~><^\'\"-_)(*@ㄱ-ㅎㅏ-ㅣa-z0-9]+")
# []: 대괄호 내부에 포함된 문자 중 하나와 일치한다면
# +: 대괄호 내의 문자들이 한 번 이상 반복된다면
# ㅋㅎㄷㅇ: 하나의 자음
# 0-9, a-z, ㄱ-ㅎ: 범위를 주어 해당 데이터를 추출
s.search("안녕하세요ㅎㅎㅎ")
<re.Match object; span=(5, 8), match='ㅎㅎㅎ'>
# cf. findall() 함수: 전체 다 찾음 (.search()는 처음 나온 것만 찾음)
s.findall("ㅋㅋㅋ알겠습니다.... *^^*")
['ㅋㅋㅋ', '....', '*^^*']
# 위의 패턴에 맞춰 전처리 후 깨끗한 문장 만들기
X_train_clean = []
for doc in X_train_morphs: # 형태소 단위로 분리된 데이터를 반복
temp = []
for token in doc:
if len(token)<2: # 한 글자의 데이터
continue
if s.search(token): # 제거할 패턴에 매칭이 되는지 검출
continue
temp.append(token) # 정상적인 토큰만 임시 리스트에 추가
X_train_clean.append(' '.join(temp)) # 다시 하나의 문자열로 만들어 전체 리스트에 추가
# 위의 패턴에 맞춰 전처리 후 깨끗한 문장 만들기
X_test_clean = []
for doc in X_test_morphs: # 형태소 단위로 분리된 데이터를 반복
temp = []
for token in doc:
if len(token)<2: # 한 글자의 데이터
continue
if s.search(token): # 제거할 패턴에 매칭이 되는지 검출
continue
temp.append(token) # 정상적인 토큰만 임시 리스트에 추가
X_test_clean.append(' '.join(temp)) # 다시 하나의 문자열로 만들어 전체 리스트에 추가