Data Science

SNS 텍스트 분석(konlpy) 및 워드클라우드 - 모양, 대용량 개선 본문

데이터분석

SNS 텍스트 분석(konlpy) 및 워드클라우드 - 모양, 대용량 개선

shinho0902 2021. 11. 8. 15:16

 

 

 

 

sns 텍스트 분석

 

필요한 모듈/라이브러리 불러오기

In [ ]:
import csv
import pandas as pd
import tweepy
import konlpy
import sys
import numpy as np
import os
from konlpy.tag import Okt
from PIL import Image
from collections import Counter
from wordcloud import STOPWORDS
from wordcloud import WordCloud
 

데이터 불러오기

In [ ]:
# 데이터 불러오기
df = pd.read_csv("sns_data_namyangju.csv",encoding='utf-8') #전체
df.head(5)
Out[ ]:
  원시데이터인덱스 원시데이터키워드명 원시데이터제목 원시데이터내용 원시데이터URL
0 1211 _남양주_ 남양주오리고기맛집 : 진미식당 '◡'✿ (가성비 최고) 남양주오리고기맛집 : 진미식당 '◡'✿ (가성비 최고) 안녕하세요 단미입니다... ... https://blog.naver.com/july16_blossomdays?Redi...
1 1212 _남양주_ 남양주브런치카페 맛있는 별내 카페4040 남양주로 이사를 가야할 정도로 자주 출몰하는 1인 이번에 정말 맛 걱정 주차 걱정 ... https://blog.naver.com/badanol?Redirect=Log&lo...
2 1213 _남양주_ 남양주다산동하수구 배관공사 잘하는곳 남양주다산동하수구 배관공사 잘하는곳 안녕하세요. 어느덧 2020년 새해가... ㅎㅎ... https://blog.naver.com/ehgml8ki?Redirect=Log&l...
3 1214 _남양주_ 남양주진접 브런치 카페 마당넓은 카펜트리 여긴 꼭 가야해 #남양주카페 #브런치카페 #카펜트리 카펜트리 경기남양주시 진접읍 경... https://blog.naver.com/ssilly?Redirect=Log&log...
4 1215 _남양주_ (남양주/북한강) 두물머리_운길산장어 #남양주11월 #북한강11월 11월 & 12월 겨우 한 달 차이인데 풍경의 ... https://blog.naver.com/boniemom?Redirect=Log&l...
In [ ]:
print(df.columns.tolist())
 
['원시데이터인덱스', '원시데이터키워드명', '원시데이터제목', '원시데이터내용', '원시데이터URL']
In [ ]:
df_copy = df
 

 

다시 실행(중단점)

In [ ]:
df_all = df_copy
 

'제목' 속성만 불러오기
내용으로 불러오려 했지만 각종 광고글과 의미없는 단어들이 다량 추출되어 키워드가 많이 포함될 수 있는 제목 속성을 활용하기로 결정함

In [ ]:
df_all = df_all.loc[:,['원시데이터제목']]
df_all.head(5)
Out[ ]:
  원시데이터제목
0 남양주오리고기맛집 : 진미식당 '◡'✿ (가성비 최고)
1 남양주브런치카페 맛있는 별내 카페4040
2 남양주다산동하수구 배관공사 잘하는곳
3 남양주진접 브런치 카페 마당넓은 카펜트리
4 (남양주/북한강) 두물머리_운길산장어
 

형태소 분석시 반복문을 시행함에 있어서
인덱스는 1부터 시작되는게 유리함

In [ ]:
# 인덱스 조정 (1부터 시작)
df_all.index = df_all.index + 1
df_all.head(5)
Out[ ]:
  원시데이터제목
1 남양주오리고기맛집 : 진미식당 '◡'✿ (가성비 최고)
2 남양주브런치카페 맛있는 별내 카페4040
3 남양주다산동하수구 배관공사 잘하는곳
4 남양주진접 브런치 카페 마당넓은 카펜트리
5 (남양주/북한강) 두물머리_운길산장어
In [ ]:
len(df_all) # 전체 데이터 행수
Out[ ]:
720195
 

 

형태소 분석 및 카운팅

 

이 작업은 작동시간이 오래 걸리는 부분

처음에 전체를 한꺼번에 돌리려고 시도했으나
데이터양이 많아 시간이 오래걸리고 메모리 오류가 발생

 

문제해결방법

  • 10만개(10만행)까지는 무난하게 한 번에 분석이 가능한것으로 확인
  • 전체 데이터를 N등분하여 형태소를 분석
  • 반복이 한 번 돌때마다 n만개씩 분석
  • n만개 분석결과를 csv 파일로 저장
  • 첫번째 반복 후 다음 반복을 실행시, 기존 csv 파일에 append 를 함
  • append 가 됐다면, 동일 명사가 반복되므로 groupby 함수를 이용하여 명사 기준으로 카운트를 합침
In [ ]:
# 원래 파일이 있다면 반드시 삭제후 실행
filename = 'SNS_NAMYANGJU_COUNT.csv' # 계속 append 될 카운트 csv 파일

if os.path.isfile(filename):
    os.remove(filename)
    print('File has been removed')
else:
    print('Not exist')
 
File has been removed
In [ ]:
all_len = len(df_all) # 총 데이터 개수 
part = 30000 # 몇개씩 끊을건지 입력 - 3만개

# 전체를 n만개씩 반복수행
for i in range(0,all_len,part):

    print("Loop {} ........".format( i//part )) # 진행 확인
    
    if i+part >= all_len:
        df = df_all.iloc[i+1:all_len,:]
        print("Complete!!") # 완료 확인
        break
    
    df = df_all.iloc[i+1:i+part,:]        
    
    
    df_list = df.values.tolist() # 데이터프레임을 해제하여 리스트로 변환
    df_list = np.array(df_list).flatten() # 이중리스트를 단일리스트로 변환
    df_str = "".join(df_list) # 리스트를 해제 시키고 문자열 형태로 변환
    
    words = df_str 

    # okt 객체 생성
    okt = Okt() 
    noun = okt.nouns(words) 

    for i,v in enumerate(noun): 
        if len(v)<2: 
            noun.pop(i) 

    count = Counter(noun) 
    

    # 명사 빈도 카운트
    noun_list = count.most_common() # 전체 다 카운트함. 값넣으면 상위 n 개만 추출.
    
    #for v in noun_list:
        #print(v)
        
        
    # csv 파일에 저장 및 추가(append)
    with open("SNS_NAMYANGJU_COUNT.csv","a", newline='',encoding='cp949') as f:
        csvw = csv.writer(f)
        for v in noun_list:
            csvw.writerow(v)
  
   # 초기화 (하지 않으면 메모리가 과누적되어 오류발생)
    okt = 0
    noun = 0
    noun_list = 0
 
Loop 0 ........
Loop 1 ........
Loop 2 ........
Loop 3 ........
Loop 4 ........
Loop 5 ........
Loop 6 ........
Loop 7 ........
Loop 8 ........
Loop 9 ........
Loop 10 ........
Loop 11 ........
Loop 12 ........
Loop 13 ........
Loop 14 ........
Loop 15 ........
Loop 16 ........
Loop 17 ........
Loop 18 ........
Loop 19 ........
Loop 20 ........
Loop 21 ........
Loop 22 ........
Loop 23 ........
Loop 24 ........
Complete!!
In [ ]:
# append 되어 생성된 csv 파일
df_c = pd.read_csv("SNS_NAMYANGJU_COUNT.csv",encoding='cp949',header=None)
df_c.head(10)
Out[ ]:
  0 1
0 남양주 8208
1 카페 1339
2 아파트 1276
3 맛집 1254
4 구리 1196
5 분양 969
6 다산 842
7 판매 838
8 가입 832
9 인사 653
In [ ]:
# 속성명 부여
df_c.columns = ['명사','카운트']
df_c
Out[ ]:
  명사 카운트
0 남양주 8208
1 카페 1339
2 아파트 1276
3 맛집 1254
4 구리 1196
... ... ...
381554 지스타 1
381555 오퍼스 1
381556 강상면 1
381557 보전 1
381558 프리즈 1

381559 rows × 2 columns

 

 

동일 명사가 반복되므로 groupby 함수를 이용하여 명사 기준으로 카운트를 합침

In [ ]:
df_c = df_c.groupby('명사').sum()
df_c = df_c.sort_values('카운트',ascending=False)
df_c
Out[ ]:
  카운트
명사  
남양주 209620
판매 45559
카페 35336
맛집 35309
분양 32856
... ...
복걸 1
복구펌 1
복국 1
복기왕 1
1

63149 rows × 1 columns

 

최종 명사별 카운팅된 파일저장

In [ ]:
# csv 파일에 저장
df_c.to_csv("SNS_NAMYANGJU_COUNT_result.csv",encoding='euc-kr',header=None,sep=',')
 

 

워드클라우드 생성

 

불용어 처리

In [ ]:
# 작성한 불용어 텍스트 파일을 부름
filename = "data/stopwords.txt" 

with open(filename,encoding='utf-8') as f:
    lines = f.read().splitlines()

#lines.sort()
#print(lines)

stopwords = set(lines) # set을 이용하여 혹시 모를 중복을 피함
len(stopwords)
Out[ ]:
547
In [ ]:
# 워드클라우드에 사용할 모양 불러오기
mask_image = Image.open('phploeBuh.png')
mask = np.array(mask_image)
In [ ]:
# 워드클라우드 생성
filename = "SNS_NAMYANGJU_COUNT_result.csv"
f = open(filename,'r',encoding='cp949')	
words = f.read()

wc = WordCloud(font_path='data/MALGUNBD.ttf', \
    background_color="white", \
    width=500, \
    height=500, \
    max_words=100, \
    max_font_size=140, \
    stopwords=stopwords, \
    mask = mask )
    
wordcloud_words = wc.generate(words)
wc.to_file('wordcloud_제목_남양주.png') # 이미지 파일로 저장
Out[ ]:
<wordcloud.wordcloud.WordCloud at 0x1db9ef44cd0>
In [ ]:
Image.open('wordcloud_제목_남양주.png')
Out[ ]:
In [ ]:
 
 

추후 개선해야 할 점 / 한계점

 
  • 반복문을 돌리는 과정에서 루프안의 코드들을 함수/모듈화해서 작성할 필요가 있음
  • 불용어 처리시 각종 광고물에 대한 처리를 주관적 판단에 따라 제거했으나 광고를 필터링 할 수 있는 대책 고안
Comments