3
\$\begingroup\$

I have created a Reddit bot that goes through an "x" amount of posts. For each post it pulls all the comments from that post into a list then a data frame and then it loops over a CSV look for words that match a ticker in CSV file and then finally it spits out a sorted data frame.

is there anything I could improve / more object-oriented code?

autho_dict structure

autho = {
'app_id': '',
'secret': '',
'username': '',
'password': '',
'user_agent': ""
}

The rest of the code

from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
import re
import os
import praw
import pandas as pd
import datetime as dt


class WallStreetBetsSentiment:
    def __init__(self, autho_dict, posts):
        self.__authentication = autho_dict
        self.__posts = posts
        self.__comment_list = []
        self.__title_list = []
        self.__ticker_list = pd.read_csv(
            os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "\\dependencies\\ticker_list.csv")
        self.__sia = SentimentIntensityAnalyzer()

    @property
    # creates instance of reddit using authenticaton from app.WSBAuthentication
    def __connect(self):
        return praw.Reddit(
            client_id=self.__authentication.get("app_id"),
            client_secret=self.__authentication.get("secret"),
            username=self.__authentication.get("username"),
            password=self.__authentication.get("password"),
            user_agent=self.__authentication.get("user_agent")
        )

    @property
    # fetches data from a specified subreddit using a filter method e.g. recent, hot
    def __fetch_data(self):
        sub = self.__connect.subreddit("wallstreetbets")  # select subreddit
        new_wsb = sub.hot(limit=self.__posts)  # sorts by new and pulls the last 1000 posts of r/wsb
        return new_wsb

    @property
    # saves the comments of posts to a dataframe
    def __break_up_data(self):
        for submission in self.__fetch_data:
            self.__title_list.append(submission.title)  # creates list of post subjects, elements strings
            submission.comments.replace_more(limit=1)

            for comment in submission.comments.list():
                dictionary_data = {comment.body}
                self.__comment_list.append(dictionary_data)
        return pd.DataFrame(self.__comment_list, columns=['Comments'])

    # saves all comments to a csv document saved in 'logs'
    def debug(self):
        save_file = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "\\logs"
        return self.__break_up_data.to_excel(save_file + "\\log-{}.xlsx".format(
            dt.datetime.now().strftime("T%H.%M.%S_D%Y-%m-%d")), sheet_name='Debug-Log')

    # loops though comments to find tickers in self.ticker_list
    def parser(self, enable_debug=bool):
        ticker_list = list(self.__ticker_list['Symbol'].unique())
        # titlelist = list(df2['Titles'].unique())
        comment_list = list(self.__break_up_data['Comments'].unique())
        ticker_count_list = []

        for ticker in ticker_list:
            count = []
            sentiment = 0
            for comment in comment_list:
                # count = count + re.findall((r'\s{}\s').format(ticker), str(comment))
                count = count + re.findall((' ' + ticker + ' '), str(comment))

                if len(count) > 0:
                    score = self.__sia.polarity_scores(comment)
                    sentiment = score['compound']  # adding all the compound sentiments
            if len(count) > 0:
                ticker_count_list.append([ticker, len(count), (sentiment / len(count))])

        if enable_debug is True:
            self.debug()

        else:
            pass
        # ISSUE: the re.findall function would return match on AIN if someone says PAIN
        df4 = pd.DataFrame(ticker_count_list, columns=['Ticker', 'Count', 'Sentiment'])
        df4 = df4.sort_values(by='Count', ascending=False)
        return df4
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Underscores

Double-underscores, as in

    self.__authentication = autho_dict
    self.__posts = posts
    self.__comment_list = []
    self.__title_list = []

are not a good idea. At most, use one underscore to indicate a private member variable (though this is not enforced in Python). Read https://stackoverflow.com/a/1301369/313768 for more detail.

System-specific paths

save_file + "\\log-{}.xlsx".format(

makes it so that only Windows users can run this script. Consider Path(save_file) / f'log-{now}.xlsx' instead, and put now as a variable before your format call.

Backwards ISO8601?

T%H.%M.%S_D%Y-%m-%d

is a curious format. It's non-sortable, non-standard and surprising to everyone who understands ISO8601. If at all possible, use ISO format instead.

Redundant else

Delete this:

    else:
        pass
\$\endgroup\$
2
  • \$\begingroup\$ Thank you for your thoughts. I was not aware of the standard practice for date/time. In terms of private variables should I only use them when I am using multiple classes that have identical function names? \$\endgroup\$ Nov 24, 2020 at 10:28
  • 1
    \$\begingroup\$ Private variables are not the solution to name collision concerns. If you have multiple classes, the only time there will be a name collision is if there is an inheritance relationship between them, in which case the name overlap would be deliberate. \$\endgroup\$
    – Reinderien
    Nov 24, 2020 at 15:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.