Yours Ever, Data Chronicles

파이썬 자연어 처리(NLP) - 문장 수치화하기(One-hot Encoding) / 파이썬 데이터 분석 실무 테크닉 100 본문

Data Science/Analysis Study

파이썬 자연어 처리(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

     

    이외에도 다양한 문장 수치화 기법이 있습니다!

    오늘 포스팅은 여기서 마무리하고, 다음 포스팅에서는 이렇게 원핫인코딩으로 수치화한 문장을 가지고 유사한 문장을 구해보겠습니다.

    감사합니다.

     

     

    반응형