본문 바로가기

study

카카오톡 채팅 분석

본인을 포함한 대학동기 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' 카테고리의 다른 글

스타벅스 이디야 매장 수 비교  (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) 2019.12.28