본문 바로가기
study

카카오톡 채팅 분석

by 나는야오리 2020. 3. 10.

본인을 포함한 대학동기 4명이 속한 카톡 단체 채팅방의 대화 내용을 분석해보았다. 동의를 구하고 익명처리하여 정리해보고자 한다. 간단한 데이터 형태와 분석 순서는 아래와 같다.

기간 : 2017-01-19 ~ 2020-02-27
대화 : 149,208건

1. 데이터 수집 및 가공
2. 시각화를 통한 EDA
3. 불용어 제거 및 konlpy를 통한 전처리
4 LDA 토픽 모델링

1. 데이터 수집 및 가공

1-1. 데이터 수집

"채팅방 -> 우측 상단 햄버거바 -> 대화 내용 -> 대화 내보내기"를 하면 해당 채팅방의 채팅이 txt파일로 다운받아진다. 휴대폰과 맥북의 경우 csv파일로 저장되니 참고하면 된다. 다만 csv파일의 경우 초기 데이터를 전처리 과정이 생략된다.

# 코드 인용 https://github.com/ssooni/data_mining_practice
import re
import pandas as pd
import datetime as dt

def read_kko_msg(filename):
    with open(filename, encoding='utf-8') as f:
        msg_list = f.readlines()
    return msg_list

def apply_kko_regex(msg_list):
    kko_pattern = re.compile("\[([\S\s]+)\] \[(오전|오후) ([0-9:\s]+)\] ([^\n]+)")
    kko_date_pattern = re.compile("--------------- ([0-9]+년 [0-9]+월 [0-9]+일) ")

    emoji_pattern = re.compile("["u"\U0001F600-\U0001F64F"  # emoticons
                               u"\U0001F300-\U0001F5FF"  # symbols & pictographs
                               u"\U0001F680-\U0001F6FF"  # transport & map symbols
                               u"\U0001F1E0-\U0001F1FF"  # flags (iOS)
                               "]+", flags=re.UNICODE)

    kko_parse_result = list()
    cur_date = ""

    for msg in msg_list:
        # 날짜 부분인 경우
        if len(kko_date_pattern.findall(msg)) > 0:
            cur_date = dt.datetime.strptime(kko_date_pattern.findall(msg)[0], "%Y년 %m월 %d일")
            cur_date = cur_date.strftime("%Y-%m-%d")
        else:
            kko_pattern_result = kko_pattern.findall(msg)
            if len(kko_pattern_result) > 0:
                tokens = list(kko_pattern_result[0])
                # 이모지 데이터 삭제
                tokens[-1] = re.sub(emoji_pattern, "", tokens[-1])
                tokens.insert(0, cur_date)
                kko_parse_result.append(tokens)

    kko_parse_result = pd.DataFrame(kko_parse_result, columns=["Date", "Speaker", "timetype", "time", "contents"])
    kko_parse_result.to_csv("kko_regex.csv", index=False)

    return kko_parse_result

if __name__ == '__main__':
    msg_list = read_kko_msg("C://Users//user//Github/Project_Kakao chat/카톡대화.txt")
    apply_kko_regex(msg_list)
    
df = pd.read_csv("C://Users//user//Github/Project_Kakao chat/kko_regex.csv")
df.head()

1-2. 데이터 가공

# 날짜로 변환
df.Date = pd.to_datetime(df.Date)

# 파생변수 추가
## 년월일,요일 
df["year"] = df['Date'].dt.strftime('%Y')
df["month"] = df['Date'].dt.strftime('%m')
df["day"] = df['Date'].dt.strftime('%d')
df["weekday"] = df['Date'].dt.weekday_name

## 24시간제 표기
df["24time"] = df["timetype"] + " " + df["time"]
df["24time"] = df["24time"].map(lambda x : x.replace("오전","AM"))
df["24time"] = df["24time"].map(lambda x : x.replace("오후","PM"))

temp = []
transform_time = []
for i in range(len(df)) :
    time = df["24time"][i]
    temp.append(dt.datetime.strptime(time,"%p %I:%M"))
    transform_time.append(temp[i].time())

df["24time"] = transform_time

## 글자 수
title_len = []

for i in range(len(df)):
    ttl = len(df['contents'][i])
    title_len.append(ttl)

df['length'] = title_len
df.head()

## 계절 설정
quarter = []
for i in range(len(df)) :
    a = int(df["month"][i])
    if a >= 1 and a <= 3 :
        quarter.append("1q")
    if a >= 4 and a <= 6 :
        quarter.append("2q")
    if a >= 7 and a <= 9 :
        quarter.append("3q")
    if a >= 10 and a <= 12 :
        quarter.append("4q")

df["quarter"] = quarter

# 데이터 확인
df.head()

"24time" 칼럼을 통해 "hh","mm" 칼럼 추가했고 내용은 생략한다.

2. 시각화를 통한 EDA

앞서 정제한 데이터와 아래 코드를 활용하여 간단한 시각화를 진행하였다.

# 그래프 모듈 설치
import seaborn as sns

# 그래프 삽입 모듈 및 그래프 내 한글 폰트 삽입 코드
import matplotlib.pyplot as plt
plt.rc('font', family='NanumGothic')
print(plt.rcParams['font.family'])

fig, ax = plt.subplots(figsize=(10,5))

g = sns.kdeplot(df['hh'][(df['Speaker'] == '친구1') & (df["hh"].notnull())], color="Black", shade = True)
g = sns.kdeplot(df['hh'][(df['Speaker'] == '친구2') & (df["hh"].notnull())], ax =g, color="Blue", shade= True)
g = sns.kdeplot(df['hh'][(df['Speaker'] == '친구3') & (df["hh"].notnull())], ax =g, color="Gray", shade= True)
g = sns.kdeplot(df['hh'][(df['Speaker'] == '친구3') & (df["hh"].notnull())], ax =g, color="Green", shade= True)

g.set_xlabel("viewCount")
g.set_ylabel("Frequency")
plt.title("시간별 채팅 비율")
g = g.legend(["친구1","친구2","친구3","친구4"])

좌측 상단부터 시계방향 - 시간별 채팅 비율, 월별 채팅 비율, 시간별 채팅 수, 인당 보낸 카톡 수

분석 관련 구글링을 하다가 재밌는 분석이 있어 추가했다. 누가 대화를 얼어붙게 만드는가에 대한 간단한 분석이다.

# 코드 인용 https://github.com/ShinJJang/analyze_kakao_talk
# 파생 변수 추가
date = []
time = []
for i in range(len(df)) :
    dd = str(df["Date"][i]).split(" 00:")[0]
    tt= str(df["24time"][i]).split(":00")[0]
    
    date.append(dd)
    time.append(tt)
    
d = pd.DataFrame(date)
t = pd.DataFrame(time)

df["Datetime"] = d + " " + t
df.Datetime = pd.to_datetime(df.Datetime)

# 채팅간 시간 간격 10분 이상 30분 이하인 행 추출
df['delta'] = (df['Datetime'].shift(-1)-df['Datetime']).fillna(0)
df['is_indifferent'] = ((df['Speaker'] != df['Speaker'].shift(-1)) & ((df['delta'].dt.seconds//60 >= 10)&(df['delta'].dt.seconds//60 <= 30)))

df.head()

대화를 얼어붙게 만든 횟수와 ice_age지수

횟수만 보고 대화를 얼어붙게 만드는(이하 갑분싸) 눈치없는 사람으로 판단하기는 어렵다. 따라서 새로운 인덱스를 만들어보았다. 식은 간단하다. Ice_age_index  =  갑분싸 비중 / 대화 비중  =  (개인당 갑분싸횟수 / 전체 갑분싸 횟수) / (채팅 수 / 전체 채팅 수)

추가로 2019년 6월 이후 취업과 이사, 워홀 등으로 각자의 라이프스타일이 급격히 변화기 시작했다. 당시를 기준으로 대화 비중을 비교해보았다. 그래프는 groupby매소드를 활용하여 개인당 대화 수를 추출하여 진행하였다. 이 외에도 누가 가장 장문으로 쓰는지 또는 건성으로 대화를 하는지도 분석하였으나 내용이 길어지므로 생략한다.

취업과 워홀을 떠난 이들의 변화가 뚜렸했다.

3. 불용어 제거 및 konlpy를 통한 전처리

3-1. 불용어 제거

줄바꿈 문자와 특수문자, 공백과 의미없는 불용어를 제거해주었다.

# 데이터 전처리
def preprocessing(text):
    # 개행문자 제거
    text = re.sub('\\\\n', ' ', text)
    # 특수문자,자음 제거
    text = re.sub('[.,;:\)*?!~`’^\-_+<>@\#$%&=#}※ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎㅠㅜ]', '', text)
    # 중복 생성 공백값
    text = re.sub(' +', ' ', text)
    return text


# 불용어 제거
def remove_stopwords(text):
    tokens = text.split(' ')
    stops = ['ㅎㅎㅎ','ㅎㅎ','ㅋㅋ','ㅋ','ㅎㅎ','ㅎ','ㅋㅋㅋ','ㅋㅋ','ㅠㅠㅠㅠ','ㅠㅠ',
            "그냥","거기","지금","이제","우리","일단","한번","나도","하는","그게","약간","그거","해서","재미","뭔가","이모티콘",
            "존나", "누가", "하기", "하는데", "거의", "할게", "이번", "이건", "사실", "정도", "갑자기", "혹시", "보고","하노"]
    meaningful_words = [w for w in tokens if not w in stops]
    return ' '.join(meaningful_words)
    
df['new'] = df['contents'].apply(preprocessing)
df['nnew'] = df['new'].apply(remove_stopwords)

3-2. konlpy를 통한 전처리

LDA 모델링을 위해서는 명사와 동사로 이루어진 문장이 필요하다. 따라서 예전에 사용해본 okt모듈을 사용하여 텍스트를 구분하고 동사를 추출하여 명사와 결합한다. 

# 한글 형태소분석기 모듈 설치
!pip install konlpy
from konlpy.tag import Okt

# okt패키지 사용
okt = Okt()
nouns = []
morphs = []
pos = []

for i in range(len(df)):
    no = okt.nouns(str(df['nnew'][i]))
    mo = okt.morphs(str(df['nnew'][i]))
    po = okt.pos(str(df['nnew'][i]))
    nouns.append(no)
    morphs.append(mo)
    pos.append(po)

# 파생변수 추가
df['nouns'] = nouns
df['morphs'] = morphs
df['pos'] = pos

## 동사 추출
verbs1 = df['pos']
dict_verb = []

for i in range(len(verbs1)):
    vb = dict(verbs1[i])
    dict_verb.append(vb)

df['dic_verb'] = dict_verb

## 동사 추출
verb_2 = []

for i in range(len(dict_verb)):
    verb_3 = [str(key) for (key, value) in dict_verb[i].items() if value == 'Verb']
    verb_2.append(verb_3)

df['verb'] =verb_2 

## 문장 생성
df['sentence'] = df['nouns'] + df['verb']
df.head()

tokenized_doc = df['sentence']
detokenized_doc = []

for i in range(len(df)):
    t = ' '.join(tokenized_doc[i])
    detokenized_doc.append(t)
df['headline_text'] = detokenized_doc

 

4 LDA 토픽 모델링

LDA 모델링에 사용되는 파라미터에 대한 설명은 정리가 되는대로 따로 업로드할 생각이다. 친구가 알려준 코드로 간단히 토픽 모델링을 해보았다. 그리곤 데이터를 제공한 친구들과 나름대로 의미를 해석해보았다. 

# 분석할 문장의 불용어 제거 및 벡터화
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(stop_words='english',max_features=1000)
X = vectorizer.fit_transform(df['headline_text'])
X.shape

# LDA 설치
from sklearn.decomposition import LatentDirichletAllocation
lda_model = LatentDirichletAllocation(n_components=10, learning_method='online', random_state=777, max_iter=1)
lda_top = lda_model.fit_transform(X)

# terms는 피쳐로 지정된 단어, 최대 1000로 한정
terms = vectorizer.get_feature_names() 
df = []
def get_topics(components, feature_names, n=10):
    for idx, topic in enumerate(components):
        df.append([(feature_names[i]) for i in topic.argsort()[:-n -1:-1]])
get_topics(lda_model.components_, terms)

# 데이터 프레임화
df = pd.DataFrame(df)
df.columns = ['topic1','topic2','topic3','topic4','topic5','topic6','topic7','topic8'
              ,'topic9','topic10']
df.reset_index(drop=True)

마치며

친구 말로는 konlpy에서 제공하는 mecab이라는 패키지가 속도가 빠르다고한다. 다음번에는 연산속도를 비교해보면서 진행해보면 좋을듯하다. 연산속도 챙겨야겠다. 추가로 대화가 가장 활발했던 날의 토픽을 따로 분석해보아도 좋을 것 같다. 그리고 만약 같은 내용으로 분석을 다시하게된다면 불용어 제거를 좀 더 제대로 하면 좋을 것 같다. 

'study' 카테고리의 다른 글

아파트 분양가격 동향  (0) 2020.03.19
스타벅스 이디야 매장 수 비교  (2) 2020.03.19
러닛 - 우주 최초 플립러닝 플랫폼  (0) 2020.03.16
Database 기초  (0) 2020.03.14
[Kaggle] Survival on the Titanic  (0) 2020.03.11
카카오톡 채팅 분석  (0) 2020.03.10

댓글0