본문 바로가기

python

[코딩] 카카오톡 채팅방 분석, 빈도수 높은 단어

728x90

개발 목표 : 카카오톡 대화 파일에서 빈도수가 높은 명사 추출

개발 환경 : COLAB

개발 시간 : 약 3일

 


사전 준비

 1. konlpy 모듈을 설치. 참고로 자연어 처리 모듈로 여러가지 자연어 처리 모델이 들어있다.

!pip install konlpy

 

 2. 코랩에서 구글 드라이브 연동하는 코드. 코랩에서 쓸 거 아니면 그냥 파일 경로 쓰면 된다. 

from google.colab import drive
drive.mount('/drive')

 - 코랩에서 파일 경로는 사진과 같이 복사할 수 있다.

3. 카카오톡 대화 파일

 세줄=> 대화내용=> 대화 내보내기

 


전체 코드 

import pandas as pd
from konlpy.tag import Okt
import operator

file="파일 경로"
df=pd.read_csv(file, sep="\t", encoding="utf-8")
data=[]
okt=Okt()
words_freq={}

#문자열에서는 삭제할 문자가 없어도 에러 안뜨는데 리스트는 뜬다
deleteS=['ㅜ', 'ㅠ','ㅋ','ㅇ','𐌅','ㅗ']  #문자
deleteL=['이모티콘','삭제된','사진','동영상']   #문자열

#데이터 정제- 필요없는 데이터들 지우기
for i in range(len(df)):
  l=df.iloc[i,0].split('] ')  #리스트로 반환
  if len(l)>1:
    l.pop(0); l.pop(0)  #날짜, 사용자 정보 제거
    print(l)

    for d in deleteL: 
      if d in l: l.remove(d)
        
    if len(l)!=0:
      result = str(l[0])
      for d in deleteS: result=result.replace(d,'')

    if not result.isspace() and len(result)!=0: data.append(result) 

#명사만 추출+빈도수 확인
for d in data:
  n_list=okt.nouns(d) 
  for n in n_list:
    if len(n)>1: 
      if n not in list(words_freq.keys()): words_freq[n]=1
      else: words_freq[n]=words_freq[n]+1

sdict= sorted(words_freq.items(), key=operator.itemgetter(1),reverse=True) #내림차순 정렬 딕셔너리=>리스트, value값 기준 정렬
for s in sdict:
  print(s)

 


코드 설명

 편의상 데이터 정제하는 구간이랑 명사 추출+빈도수 검사하는 구간으로 나눠서 설명하겠다.

 

데이터 정제

 판다스로 파일을 읽어온다. 이때 데이터는 '데이터 프레임'이라고 판다스에서 사용하는 데이터 단위이다. 일단 리스트나 딕셔너리처럼 어떤 데이터 타입도 아니다. 한줄 씩 읽을 때 비로소 안에 들어있는 값(숫자가 들어있으면 int나 double, 문자열이 들어있으면 str 등)이 나온다. 

file="/drive/MyDrive/test2.txt"
df=pd.read_csv(file, sep="\t", encoding="utf-8")
data=[]

 

 데이터에서 쓸모없는 문자들을 저장한 리스트다. 처음에 문자열만 만들었더니 'ㅋ'이나 'o'같은 글자가 너무 많아서 문자 리스트도 하나 만들었다. 

#문자열에서는 삭제할 문자가 없어도 에러 안뜨는데 리스트는 뜬다
deleteS=['ㅜ', 'ㅠ','ㅋ','ㅇ','𐌅','ㅗ']  #문자
deleteL=['이모티콘','삭제된', '메시지','사진', '동영상']   #문자열

 


 

 

 데이터 프레임은 이렇게 생겼는데 한줄 씩 읽어오면 문자열로 읽힌다. 그런데 일단 문자열로 읽어오면 데이터 정제가 어려워서 리스트로 받아왔다. 

#데이터 정제- 필요없는 데이터들 지우기
for i in range(len(df)):
  l=df.iloc[i,0].split('] ')  #리스트로 반환

 

 

원래 텍스트 파일은 아래와 같은데 이러면 입력 데이터를 두 개로 나눌 수 있다.

  1. ====날짜====
  2. [사용자 이름] [시간] [대화 내용]

 

 split을 했을 때 다행히 ']'가 없어도 오류가 안 뜬다. 이러면 데이터를 간단하게 처리할 수 있다.

  1. 리스트에 들어가면 길이가 1인 리스트가 된다. 그래서 len(1)>1을 쓰면 불필요한 1번 입력데이터를 배제할 수 있다.
  2. 우리가 필요한 데이터는 [대화 내용] 뿐이다. 그러므로 리스트 0번 1번을 지워주면 된다. 

  if len(l)>1:
    l.pop(0); l.pop(0)  #날짜, 사용자 정보 제거

 

 

 불필요한 문자열을 삭제한다. 이때 모두 사라지면 길이가 0이 될텐데 그럴 경우를 제외하고 리스트의 요소를 문자열로 변환한다. 이러면 문자 단위로 불필요한 데이터를 제거할 수 있다. 

    for d in deleteL: 
      if d in l: l.remove(d)
        
    if len(l)!=0:
      result = str(l[0])
      for d in deleteS: result=result.replace(d,'')

 

 마지막으로 불필요한 데이터를 제거한 결과가 비어있지 않다면 변수에 추가한다. 

    if not result.isspace() and len(result)!=0: data.append(result)

 

 여기까지하면 txt파일에서 불필요한 정보들을 제거한 문자열의 리스트가 나온다.

유니 과대 당선 축하해~


명사 추출 + 빈도수 검사

 konlpy에는 굉장히 다양한 모듈이 있는데 각각의 특징이 있으므로 적절한 모델을 사용하면 된다. 나의 경우 그냥 빠른 게 좋아서 okt를 사용했다. 

 


 

 nouns()는 문자열에서 명사만 추출해서 리스트로 반환한다. 그러다보니 명사가 없는 문자열의 경우가 생기는데, 그러면 for문 자체가 안 돌아가는 듯, 오류는 나지 않으니까 신경쓰지 말자. 

#명사만 추출+빈도수 확인
for d in data:
  n_list=okt.nouns(d)

 

길이를 1로 설정한 이유는 의미 없는 단어 다수가 상위권에 위치했기 때문이다. 

 

 빈도수는 딕셔너리를 이용했다. 딕셔너리 키값 안에 단어가 없으면 키값을 추가하고 1로 초기화한다. 있으면 그냥 해당 키값의 value를 +1을 해준다.

  for n in n_list:
    if len(n)>1: 
      if n not in list(words_freq.keys()): words_freq[n]=1
      else: words_freq[n]=words_freq[n]+1


 딕셔너리 정렬을 해주고 출력해준다. 

sdict= sorted(words_freq.items(), key=operator.itemgetter(1),reverse=True) #내림차순 정렬 딕셔너리=>리스트, value값 기준 정렬
for s in sdict:
  print(s)

 


추가

 업데이트 전 카톡 파일을 받았는데, 파일을 살펴보니 처음부터 최신(업데이트 이후 카톡 파일)까지 전부 업데이트 전 파일 형태를 띄고 있었다. 그래서 코드를 살짝 수정했다. 

 

#업데이트 전 카톡 분석
import pandas as pd

file="/drive/MyDrive/KakaoTalkChats.txt"
df=pd.read_csv(file, sep="\t", encoding="utf-8")
data=[]

#문자열에서는 삭제할 문자가 없어도 에러 안뜨는데 리스트는 뜬다
deleteS=['ㅜ', 'ㅠ','ㅋ','ㅇ','𐌅','ㅗ']  #문자
deleteL=['이모티콘','삭제된', '메시지','사진', '동영상']   #문자열
name="쏘융 삐쟁이"

#데이터 정제- 필요없는 데이터들 지우기
for i in range(len(df)):
  l=df.iloc[i,0].split(', ')  #리스트로 반환
  if len(l)>1:
    del l[0]  #날짜 정보 제거
    if len(l)>=1:
      l_str = str(l[0])
      l2=l_str.split(' : ')
    
    if l2[0] == name:  #특정 사용자만
      #불필요한 문자 삭제
      for d in deleteL: 
        if d in l2: l2.remove(d)

      if len(l2)>1:
        result = str(l2[1])
        for d in deleteS: result=result.replace(d,'')

      if not result.isspace() and len(result)!=0: data.append(result)  #그래도 공백..

# for d in data:  #리스트
#   print(d)

#명사만 추출, 빈도수 계산
from konlpy.tag import Okt
import operator

okt=Okt()
words_freq={}

#명사만 추출+빈도수 확인
for d in data:
  n_list=okt.nouns(d) 
  for n in n_list:
    if len(n)!=1: 
      if n not in list(words_freq.keys()): words_freq[n]=1
      else: words_freq[n]=words_freq[n]+1

sdict= sorted(words_freq.items(), key=operator.itemgetter(1),reverse=True) #내림차순 정렬 딕셔너리=>리스트, value값 기준 정렬
for s in sdict:
  print(s)

필요한 개선 사항

1. 최빈다 단어 에 자리한 '사진'은 사실 데이터 정제가 덜 된 거다. 리스트 단위로 문자열을 처리해서 생긴 문제인데, 대게 단순한 문제인데 어떻게 처리할까 고민 중이다.

2. 코드가 좀 지저분하다. 좀 예쁘게 정리할 수 있을 것 같은데 좀 귀찮음..

3. 사진이랑 동영상 단어를 지우는데, 사진이랑 동영상을 실제로 보내는 게 아니라 업무?때문에 그런 단어를 자주 사용하는 경우에는 최빈다 단어로 쓸 수 없다. 이런 경우 어떡할까.