Pandas로 Reddit 댓글 트렌드 플로팅

이봐!

데이터 분석을 위한 Python 라이브러리인 Pandas에 대해 더 알고 싶었기 때문에 실험을 위한 미니 프로젝트에 착수하기로 결정했습니다. Reddit을 사용하는 경우 아래와 같은 일련의 댓글을 본 적이 있을 것입니다.



분명히 찬성 수에 패턴이 있음을 알 수 있습니다. 상위 댓글에 대한 모든 답글은 이전 답글에 비해 적은 찬성 투표를 받습니다. 상위 댓글이 받은 찬성 수, 첫 번째 회신이 받은 찬성 수, 두 번째 회신이 받은 찬성 수, ..., nth 답장을 받았습니다. Reddit의 API와 Pandas를 사용하여 이를 수행하는 프로그램을 구현했습니다. GitHubhere에서 찾을 수 있습니다.

먼저 다음을 가져와야 합니다.

import praw
import pandas as pd
import matplotlib.pyplot as plt
import sys
import math
from concurrent.futures import ThreadPoolExecutor

PRAW는 Reddit의 API용 Python 래퍼입니다. matplotlib는 Python에서 정적, 애니메이션 및 대화형 시각화를 만들기 위한 라이브러리입니다. 이러한 가져오기는 계속 진행하면서 더 명확해질 것입니다.

다음으로 Reddit의 API에 대한 인증을 정의해야 합니다. PRAW를 사용하면 OAuth2를 사용하여 매우 쉽게 이 작업을 수행할 수 있습니다.

# Authenticate using our client secret
def authenticate():
    with open('client_secret.txt', 'r') as f:
        data = f.read().split()
        CLIENT_ID = data[0]
        CLIENT_SECRET = data[1]

    return praw.Reddit(client_id=CLIENT_ID,
                    client_secret=CLIENT_SECRET,
                    user_agent=USER_AGENT)

이 경우 클라이언트 ID와 클라이언트 암호는 client_secret.txt라는 파일에 저장됩니다. 이를 읽고 Reddit의 API와 상호 작용할 수 있는 PRAWReddit 클래스의 인스턴스를 반환합니다.

이제 해야 할 일을 논리적 덩어리로 세분화합니다. 먼저 각 게시물 아래의 댓글을 볼 수 있도록 제출 목록을 가져와야 합니다. PRAW를 사용하면 r/all에서 가장 인기 있는 제출물 목록을 얻을 수 있습니다. 이는 기본적으로 서로 다른 하위 레딧에서 제출된 여러 제출물을 합친 것입니다.

def get_submission_upvotes(reddit, m, n):
    upvotes_list = []
    submissions = list(reddit.subreddit('all').hot(limit=m))
    with ThreadPoolExecutor() as executor:
        futures = [executor.submit(count_upvotes, submissions[i], n) for i in range(0, m)]
        [upvotes_list.append(future.result()) for future in futures]
    return upvotes_list

우리가 concurrent.futures를 사용하는 이유는 검토 중인 댓글 체인의 길이에 따라 한 제출에 대한 찬성 투표를 수집하는 데 매우 오래 걸리지만 다른 제출에 대한 찬성 투표를 수집하는 데 매우 짧은 시간이 걸릴 수 있기 때문입니다. count_upvotes 함수가 다음에 수행하는 작업). 여기서 m는 검토할 제출 수를 나타내고 n는 검토할 댓글 체인의 댓글 수를 나타냅니다.

다음으로 각 제출의 댓글 체인에 있는 각 댓글의 찬성 수를 계산해야 합니다. 이는 다음과 같이 수행할 수 있습니다.

def count_upvotes(submission, n):
    upvotes = []
    submission.comments.replace_more(limit=0)

    count = 0
    comment = submission.comments[0]
    while count < n:
        upvotes.append(comment.score)
        count += 1
        if len(comment.replies) > 0:
            comment = comment.replies[0]
        else:
            break

    while len(upvotes) < n:
        upvotes.append(float('nan'))
    return upvotes

본질적으로 우리는 최고의 댓글을 받고 n 댓글을 받거나 댓글 체인이 끝날 때까지(길이가 n 인 댓글 체인을 더 많이 얻으려면 m 늘릴 수 있음) 각 댓글을 계속 검토합니다. 댓글 체인이 조기에 종료되면 nan (Not a Number)를 추가하여 해당 댓글이 존재하지 않기 때문에 댓글 체인의 특정 댓글에 대해 데이터가 수집되지 않았음을 나타냅니다(댓글 체인이 종료되어 더 이상 찬성 투표가 없음). 계산하기).

상위 댓글 체인에서 각 댓글의 찬성 수를 계산한 후 데이터를 처리하기만 하면 됩니다. 먼저, 상위 댓글, 첫 번째 답글, 두 번째 답글 등에 대한 평균 찬성 수를 계산합니다.

def compute_data(upvotes_list):
    avg_upvotes = []
    for col in range(0, len(upvotes_list[0])):
        avg = 0.0
        n = len(upvotes_list)
        for row in range(0, len(upvotes_list)):
            if math.isnan(upvotes_list[row][col]) == False:
                avg += upvotes_list[row][col]
            else:
                n -= 1
        if n > 0:
            avg_upvotes.append(avg / n)
        else:
            avg_upvotes.append(0)
    return avg_upvotes

우리는 avg_upvotes 라는 목록을 반환합니다. 여기에는 상위 댓글, 첫 번째 댓글, 두 번째 댓글 등에 대한 평균 찬성 투표 수가 포함되어 있습니다. 이제 이 목록을 막대 그래프로 변환합니다.

def render_data(avg_upvotes, n):
    # Solution from https://stackoverflow.com/questions/9647202/ordinal-numbers-replacement
    ordinal = lambda n: "%d%s" % (n,"tsnrhtdd"[(n//10%10!=1)*(n%10<4)*n%10::4])
    x = [str(ordinal(i)) for i in range(0, n)]
    df = pd.DataFrame({'Comment Reply Number': x, 'Average Upvotes': avg_upvotes})
    df.plot.bar(x='Comment Reply Number', y='Average Upvotes', rot=0, legend=False)
    plt.xlabel('Comment Reply Number')
    plt.ylabel('Average Upvotes')
    plt.xticks(size=4)
    plt.yticks(size=4)
    plt.show()

이제 우리가 해야 할 일은 다음과 같이 조합하는 것입니다.

if __name__ == '__main__':
    if len(sys.argv) < 3:
        print('./comment_analyzer.py [number of submissions to analyze] [number of comments]')
        exit()
    m = int(sys.argv[1])
    n = int(sys.argv[2])
    reddit = authenticate()
    upvotes_list = get_submission_upvotes(reddit, m, n)
    avg_upvotes = compute_data(upvotes_list)
    render_data(avg_upvotes, n)

이 프로그램은 두 가지 매개 변수를 사용합니다. 검토할 제출 수와 검토해야 하는 댓글 체인의 "깊이"입니다.

프로그램을 실행하면(Python 3.6+에 필요한 라이브러리가 설치되어 있다고 가정) 다음과 같은 막대 그래프가 나타납니다.



("0번째"댓글은 맨 위 댓글을 나타냅니다).

위의 그래프를 통해 Reddit의 댓글에 대한 찬성 분포를 시각화할 수 있습니다. 분명히 분포는 오른쪽으로 치우쳐 있습니다. 응답 체인이 더 깊어질수록 찬성 수는 줄어듭니다. 상단 댓글이 먼저 표시되는 경향이 있기 때문에 이는 예상할 수 있습니다.

Pandas 및 PRAW와 같은 도구를 사용하여 데이터를 수집하고 시각화하는 방법을 볼 수 있기를 바랍니다.

좋은 웹페이지 즐겨찾기