Yours Ever, Data Chronicles
파이썬 자연어 처리(NLP) - 문장 수치화하기(One-hot Encoding) / 파이썬 데이터 분석 실무 테크닉 100 본문
파이썬 자연어 처리(NLP) - 문장 수치화하기(One-hot Encoding) / 파이썬 데이터 분석 실무 테크닉 100
Everly. 2022. 5. 18. 12:13저번 포스팅에서는 sur 데이터의 comment(의견) 컬럼을 konlpy를 활용해 형태소 분석해보았습니다.
그래서 자주 나온 명사를 살펴보았는데, 사실 이 단어가 긍정적인지, 부정적인지 모른다는 문제가 있었죠?
오늘은 이 단어와 고객만족도를 함께 살펴보고, 텍스트 데이터를 수치화하는 방법까지 알아보겠습니다 :)
✔Table of Contents
Tech 98. 자주 나오는 단어와 고객 만족도 간 관계를 알아보자. (groupby agg)
sur.head()
sur 데이터에서 comment와 satisfaction만 따로 뽑아봅니다.
여기서 comment는 앞의 포스팅에서 한 것처럼, 명사만 뽑겠습니다. 물론 stopwords인 '더', '수', '좀' 도 제거합니다.
앞의 포스팅에서 쓴 코드를 살짝만 변형합니다.
# 아까 썼던 코드를 살짝만 변형해서, 해당 단어의 행일 때의 satisfaction을 리스트에 담자.
all_word = []
stop_words = ['더', '수', '좀']
satis = []
for n in range(len(sur)):
text = sur['comment'].iloc[n]
words = twt.pos(text)
for i, j in words:
if j == 'Noun':
#stop_words 안의 단어라면 포함 안되게 하기
if i not in stop_words:
all_word.append(i)
satis.append(sur['satisfaction'].iloc[n])
# print(all_word)
# print(satis)
이렇게 하여, comment 열의 명사는 'all_word' 리스트에, 이 단어에 해당하는 만족도 값은 'satis' 리스트에 담겼습니다.
이 둘을 묶어 데이터프레임으로 만들면 되겠죠?
#이제 2개 리스트를 묶어 데이터프레임으로 만든다!
newdf = pd.DataFrame({'words': all_word, 'satis': satis, 'count': len(all_word)*[1]})
newdf.head()
그리고 words별 satis(만족도)는 평균값, count(개수)는 횟수로 세어주겠습니다.
즉, words별 groupby를 해줍니다.
이렇게 두 개의 다른 열에 각각 그룹바이를 해주려면 groupby의 agg 메서드를 사용하면 됩니다.
#satis와 count 컬럼 값을 연산해주자.
## words별로, satis (만족도)의 평균, count (단어 등장 횟수)
newdf = newdf.groupby('words').agg({'satis': ['mean'], 'count': ['count']})
newdf.head()
이렇게 groupby agg function을 사용하게 되면, 그룹바이를 2가지 컬럼에 대해 했기 때문에 멀티인덱스(MultiIndex)가 설정됩니다.
newdf.columns
(참고용) 이렇게 컬럼에 멀티인덱스가 설정되어 있으면 reset_index로 인덱스를 풀어줄 수 없습니다.
sy = newdf.reset_index()
sy.columns
※ MultiIndex Indexing
그래서 멀티인덱스에서 인덱싱을 할 땐, 원랜 하나의 컬럼만 쓰면 됐는데 2개의 컬럼을 써줘야 합니다.
말로 하면 어려우니 직접 해봅시다.
count 가 3 이상인 데이터만 뽑아서, 고객만족도(satis) 값을 내림차순 & 오름차순으로 5개씩 나열해봅시다.
(여기서 내림차순은 satis 값이 높은 순서, 오름차순은 satis 값이 낮은 순서대로 나열하는 것을 말합니다.)
## 내림차순: 만족도 높은순
newdf2 = newdf.loc[newdf[('count','count')] >= 3]
newdf2.sort_values(by = ('satis', 'mean'), ascending=False).head(5)
## 오름차순: 만족도 낮은순
newdf2.sort_values(by = ('satis', 'mean'), ascending=True).head(5)
만족도가 높고/낮은 순서대로 살펴보니,,, 뭔가가 보이죠!
만족도 높은 단어엔 '육아', '관공서' 등이 있는 데 비해,
만족도가 낮은 단어엔 '주차장', '역앞' 이런 단어가 있습니다.
만족도가 높은 단어에 대해선 육아하기 좋다, 관공서가 가까이 있어서 편리하다~ 라는 긍정적인 의견인 반면,
주차장 자리가 부족하다거나, 역앞엔 사람이 많아서 부정적으로 체크한 것이 아닌가 하는 감을 잡을 수 있습니다.
※ 참고로 저는 groupby agg 함수를 사용해 멀티인덱스를 사용했지만,
책에선 멀티인덱스 사용을 피하기 위해 satis, count 컬럼 각각 따로 groupby를 사용해 데이터프레임을 만들고 이를 결합했습니다. 밑의 코드를 참고하세요:
newdf = pd.DataFrame({'words': all_word, 'satis': satis, 'count': len(all_word)*[1]})
newdf.head()
# satis, count 각각 따로 groupby 연산해서 데이터프레임을 만들고 이를 결합!
df_satis = newdf.groupby('words')['satis'].mean()
df_count = newdf.groupby('words')['count'].count()
#두개 결합
newdf = pd.concat([df_satis, df_count], axis= 1)
newdf
멀티인덱스를 사용하지 않아서 좀 더 연산이 간단합니다.
위에서 한 거랑 똑같이 만족도(satis)를 내림차순, 오름차순으로 나타내보면
## 내림차순: 만족도 높은순
newdf2 = newdf.loc[newdf['count'] >= 3]
newdf2.sort_values(by = 'satis', ascending=False).head(5)
## 오름차순: 만족도 낮은순
newdf2.sort_values(by = 'satis', ascending=True).head(5)
이렇게 결과는 같답니다!
Tech 99. 의견을 수치화하자. (One-Hot Encoding)
이렇게 하여, 비교적 간단한 방법을 사용해 의견과 만족도를 파악해봤습니다.
딱 의견(comment)에서 명사만 뽑아서, 만족도와의 관계를 뽑아봤죠.
좀 더 깊이 있는 분석을 하기 위해선 의견을 하나하나 살펴봐야 합니다.
인상적인 의견이 있을 땐 비슷한 의견이 있는지, 그 때의 만족도는 어떤지 등, 좀 더 자세히 살펴보는 게 필요합니다.
하지만 설문조사 결과가 수백개, 수천개면 일일이 눈으로 다 읽어보긴 힘들 것입니다.
이를 위해 문장의 특징을 표현하고, 그 표현을 바탕으로 비슷한 문장을 특정해보는 방법을 배워봅니다.
여기서 문장의 특징을 표현한다는 것은 의견을 "수치화" 한다는 것입니다.
sur
다시 sur 데이터를 봅시다.
의견을 수치화하는 방법으로는 가장 기본적인 숫자 기반 방법을 사용하겠습니다.
즉 "어떤 단어가 포함되어 있는가?" 를 특징으로 정의할 것입니다. 이를 원-핫 인코딩(One-Hot Encoding)이라고 하는데요!
예를 들어 sur 데이터의 0번 인덱스를 보면 '역앞에 젊은이들이 모여있다' 라는 의견에 대해,
'역앞', '젊은이' 라는 명사만 따로 뽑고, 이 2개의 명사에 플래그(flag)를 세웁니다.
즉, 0번 인덱스의 의견에 대해선 '역앞', '젊은이' 라는 컬럼을 만들고, 여기에 1 값을 넣습니다.
그렇다면 1번 인덱스는 '역앞', '젊은이' 라는 단어가 없으므로 두 컬럼에 대해선 NaN 값이 들어가겠죠?
다시 말해서 의견(comment)의 명사 187개를 컬럼화하고, 각 인덱스별 의견에 대해 해당 명사가 있으면 1, 아니면 NaN으로 처리합니다.
이를 코드로 구현하면:
all_word = pd.DataFrame()
for n in range(len(sur)):
text = sur['comment'].iloc[n]
words = twt.pos(text)
#얘가 핵심! 각 인덱스별 개별 빈 데이터프레임 words_df를 만들기
words_df = pd.DataFrame()
for i, j in words:
#해당 의견에서 Noun만 뽑고, 이를 컬럼으로 만듦 -> 값을 1로 넣기
if j == 'Noun':
words_df[i] = [1]
#인덱스마다 만들어진 words_df들을 모두 조인시키기 (axis=0이 디폴트.가로로 붙임)
all_word = pd.concat([all_word, words_df], ignore_index = True)
all_word.head()
all_word.shape
이렇게 결과가 만들어졌습니다. 총 84개 인덱스에, 187개의 명사로 컬럼이 만들어졌네요.
너무 결측치가 많으니 결측치엔 0을 대입합니다.
#너무 결측치가 많으니 결측치는 0을 대입
all_word.fillna(0, inplace=True)
all_word.head()
이렇게 문장을 숫자로 바꾸는 수치화까지 진행해보았습니다.
텍스트 데이터는 비정형 데이터이기 때문에 반드시 수치로 변환을 시켜줘야 합니다. 그래야 그 이후의 다른 연산이나, 머신러닝 모델에 적용하는 등의 방법을 쓸 수 있습니다.
문장을 수치화하는 방법은 이렇게 단순하게 원-핫 인코딩 방식으로 문장을 단어 단위로 나누는 방법 말고도, 다양한 방법이 많습니다. 대표적 방법들을 설명하자면:
- 원-핫 인코딩(One-Hot Encodding) : 문장을 단어 단위로 나누고, 각 문장에 등장하는 단어는 1, 아니면 0으로 나타내는 방식
- (참고) sklearn의 CountVectorizer
- TF : 앞서 원-핫 인코딩 방법으로 구한 문장별 0, 1 값을 더해서 만듭니다. (잘 사용하지 않는 방법)
- TF-IDF 방법: 다른 문서에서 흔하게 등장하는 단어의 수치를 TF에, 역문서 빈도 IDF(총 문서의 개수 N을 특정 단어 w가 등장하는 문서의 수로 나눈 후 log를 취한 값)를 구해 두 값을 곱해서 나타내는 방식
- (참고) sklearn의 TfidfVectorizer
이외에도 다양한 문장 수치화 기법이 있습니다!
오늘 포스팅은 여기서 마무리하고, 다음 포스팅에서는 이렇게 원핫인코딩으로 수치화한 문장을 가지고 유사한 문장을 구해보겠습니다.
감사합니다.
'Data Science > Analysis Study' 카테고리의 다른 글
파이썬 자연어 처리(NLP) - 코사인 유사도를 활용해 유사한 문장 찾기 & 책 마무리 (0) | 2022.05.19 |
---|---|
파이썬 자연어 처리(NLP) - konlpy를 활용한 형태소 분석 / 파이썬 데이터 분석 실무 테크닉 100 (0) | 2022.05.17 |
파이썬 자연어 처리(NLP) - 텍스트 데이터 전처리 / 파이썬 데이터 분석 실무 테크닉 100 (0) | 2022.05.16 |
Python OpenCV (5) - HOG 노이즈 제거하기 (이동평균 이용) (0) | 2022.05.14 |
Python OpenCV (4) - 동영상에서 사람 얼굴 인식하기, 타임랩스 만들기 (0) | 2022.05.14 |