파이썬, 트위터 API를 활용한 트윗 패턴 시각화 (코드 예제)

최근에 pxd Story라는 pxd 블로그에서 트위터 사용자 유형 – 데이타 기반 퍼소나라는 재밌는 글을 보았다. (2016년도 글이다.)

트위터 사용자의 트윗 패턴을 가지고 시각화한 후, 그 패턴에 따라 유형화하는 방법을 소개하고 있다.

예를 들어 이렇게 특정 사용자의 트윗을 시각화 해서

출처: https://story.pxd.co.kr/1166

트윗(파랑), RT(초록), 멘션(보라) 등으로 구분하여 패턴을 발견하는 거다.

이런 방식으로 애니프사, 아이돌 팬덤, 인스타그램 연동 계정 등의 패턴을 발견한 결과물이 매우 흥미로웠다.

그래서 나도 내 트윗 데이터를 가지고 연습을 해보기로 했다. 나름 트위터 시작한지 10년 넘은 고인물이니까 요즘은 내가 트위터 어떻게 하나~ 보고 싶기도 했고.

사실 시계열 데이터를 제대로 다뤄본 적이 없기도 하고, 시각화 연습도 할 겸 해서 진행한 일종의 카피 프로젝트다. 일단 흉내를 내보는 데 의의를 두려 한다.


0. 트위터 데이터 수집 및 불러오기

나는 파이썬을 사용하기 때문에 아래와 같이 준비를 했다. (이전 포스팅 참고)

우선 데이터를 수집해서 json으로 저장해보자. 참고로 이렇게 공개된 API를 사용하면 최대 200개만 가져올 수 있다. 시간이 좀 걸려도 제대로 싹 가져오고 싶다면 셀레니움 같은 걸로 크롤링을 해야 할 텐데, 이번엔 연습으로 하는 거니까 일단 빠르게 API를 써보기로 했다.

import twitter

twitter_api = twitter.Api(consumer_key=twitter_consumer_key,
                          consumer_secret=twitter_consumer_secret, 
                          access_token_key=twitter_access_token, 
                          access_token_secret=twitter_access_secret)

account = "@아이디"
statuses = twitter_api.GetUserTimeline(screen_name=account, count=200, include_rts=True, exclude_replies=False)

output_file_name = "twitter_get_user_timeline_result.txt"
with open(output_file_name, "w", encoding="utf-8") as output_file:
    for status in statuses:
        print(status, file=output_file)

이제 필요한 파이썬 라이브러리를 import해서 데이터를 살펴보자.

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.read_json("twitter_get_user_timeline_result.txt", lines=True)

# df.head()
# df.info()
# df.dtypes

pd.read_json()에서 처음에 에러가 났는데, lines=True 명시해주면 끝나는 문제였다.

아무튼 요리조리 뜯어보자. 습관처럼 결측치도 확인해보고.

# df.head()
# df.info()
# df.dtypes

sns.heatmap(df.isnull(), cbar=False)
plt.show()

참고로 나는 다른 유저들과 대화를 한다든가, 리트윗을 열심히 한다든가, 그런 타입이 아니다. 그냥 혼잣말만 하는. 그래서 고작 200개 가지고 뭐가 나올런지 모르겠지만 일단 살펴보자.

1. 요일별 트윗 개수 확인

무슨 요일에 제일 많이 했나 후딱 확인해보기 위해서는 created_at라는, 트윗이 작성된 시간 변수를 다뤄야 했다.

df.dtypes 찍어봤을 때 datetime64[ns, UTC]라는 타입이라는 걸 알고 있었다. UTC(협정 세계표준시) 기준이었기 때문에 대한민국 시간으로 바꿔주고 싶었는데, 단순히 9시간 더하는 게 아니라 로컬 시간대로 좀 더 세련되게(?) 변환해보았다.

import datetime
from dateutil import tz

from_zone = tz.tzutc()
to_zone = tz.tzlocal()

def convert_time(utc):
    utc = utc.replace(tzinfo=from_zone)
    return utc.astimezone(to_zone)

df["created_at_KR"] = df["created_at"].apply(lambda x: convert_time(x))

어쨌든 이걸 가지고 시간 데이터에서 요일 정보만 추출하여 변수를 생성했고.

day_string = ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN", ]
df["created_at_KR_DAY"] = df["created_at_KR"].apply(lambda x: day_string[x.weekday()])

sns.countplot으로 후딱 그려보았다.

sns.countplot(df["created_at_KR_DAY"], order=[day_string[-1]]+day_string[:-1])

일요일부터 시작하도록 하기 위해 order를 조금 손봤다.

일요일엔 트위터 거의 안 했네.

2. 시간대/요일별 트윗 분포 확인

날짜는 무시하고 0시부터 23시59분까지 시간 안에서 언제 트윗을 주로 하는지 살펴보았다.

역시나 여기서도 created_at 변수를 조금 손봐야 했는데, 아까 datetime 불러왔기 때문에 아래와 같이 해결했다.

df["created_at_KR_hour_min"] = df["created_at_KR"].apply(lambda x: datetime.datetime.time(x).hour + datetime.datetime.time(x).minute/60)

그리고 이번엔 sns.stripplot을 사용해서 그려봤다.

plt.figure(figsize=(12,1))

sns.stripplot(df["created_at_KR_hour_min"], alpha=0.3, jitter=True, size=10, dodge=True, marker="o")

plt.xticks(range(0,25))
plt.xlabel("")
sns.despine(left=True, bottom=True)
plt.show()

제대로 안 보이긴 하는데, 아무튼 밤 12시 넘어서도 꽤 많이 하고, 점심 먹고 좀 했다가, 퇴근하고 좀 하고… 모르겠다.

요일별로 나눠서 찍어보자. 아까 요일만 따로 추출해놨으니까 ^^

plt.figure(figsize=(12,5))

sns.stripplot(data=df, x="created_at_KR_hour_min", y="created_at_KR_DAY",
              alpha=0.4, jitter=False, size=10, dodge=True, marker="o", order=day_string )

plt.xticks(range(0,25))
plt.xlabel("")
plt.ylabel("")

sns.despine(left=True, bottom=True)

plt.show()

3. 시간대/날짜별 트윗 분포 확인

이전에 제대로 된 날짜/시간 데이터를 제대로 다뤄본 적이 없었기 때문에 이걸 똑같이 구현해보는 게 목적이었다.

우선 날짜 정보만 따로 담아놓았고,

df["created_at_KR_day"] = df["created_at_KR"].apply(lambda x: x.date)

나중에 눈금과 레이블에 활용하기 위해 리스트 형태로 준비해놓았다.

from datetime import date, timedelta

d1 = df['created_at_KR_day'].min()
d2= df['created_at_KR_day'].max()
delta = d2-d1

datelist =[]
for i in range(delta.days+1):
    datelist.append(d1+timedelta(days=i))

datelist_label = [x.strftime("%m-%d") for x in datelist]

이걸 굳이 해야 하나 싶을 수도 있는데, 이렇게 내가 원하는 범위만큼 눈금을 지정하지 않으면 말도 안 되는 날짜 범위에서 데이터가 그려지더라. 그래서 추후에 이 방법을 써서 보완했다. 아무튼.

RT나 다른 사람에게 reply는 거의 안 하는 패턴이라 그나마 쓸만한 게 정보가 미디어 포함 여부였다. 트윗할 때 가끔 사진을 함께 올리기 때문이다. 그래서 미디어 포함 여부를 미리 체크해놓고

df["media_isnull"] = df["media"].isnull()

이제 그려보면

plt.figure(figsize=(16,12))
ax = sns.scatterplot(data=df, 
                x="created_at_KR_hour_min", 
                y="created_at_KR_day", s=50,
                hue="media_isnull", hue_order=[True, False])

ax.xaxis.set_ticks_position('top')
ax.xaxis.set_label_position('top')
ax.grid(False, axis="x")

plt.xticks(range(0,25), fontsize=12)
plt.xlim(0, 24)
plt.yticks(datelist, datelist_label, fontsize=6)
plt.ylim(df['created_at_KR_day'].min()-timedelta(days=1), df['created_at_KR_day'].max()+timedelta(days=1))

sns.despine(right=True, left=True, top=True, bottom=True)
ax.legend_.remove()

plt.xlabel("TIME")
plt.ylabel("DATE")
plt.show()

미디어가 첨부된 트윗은 주황색으로 표시되도록 했다.

그러고 보니 1,2월엔 트위터를 많이 안 했네.

아무튼 여차저차 의도한 대로 흉내를 내보긴 했다. 성공?!

파이썬 더 잘하고 싶다.

추천 글

댓글 남기기