0% found this document useful (0 votes)
4 views73 pages

Reddit Social Media Analytics Report

The document outlines a comprehensive study conducted by Birmingham City University's School of Computing and Digital Technology, focusing on statistical analysis, text mining, and machine learning applications in social media analytics, particularly on Reddit. It includes sections on social media analytics, graph analysis, topic modeling, sentiment analysis, and extractive summarization, along with various appendices and visual aids. The content is structured into sections that detail methodologies, findings, and utility functions relevant to the analysis of social networks and text data.

Uploaded by

muhammad shoaib
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views73 pages

Reddit Social Media Analytics Report

The document outlines a comprehensive study conducted by Birmingham City University's School of Computing and Digital Technology, focusing on statistical analysis, text mining, and machine learning applications in social media analytics, particularly on Reddit. It includes sections on social media analytics, graph analysis, topic modeling, sentiment analysis, and extractive summarization, along with various appendices and visual aids. The content is structured into sections that detail methodologies, findings, and utility functions relevant to the analysis of social networks and text data.

Uploaded by

muhammad shoaib
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

BIRMINGHAM CITY UNIVERSITY

SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Contents
1 Introduction 5

2 Statistical Analysis 5
2.1 Social Media Analytics for sub-Reddit Feeds . . . . . . . . . . . . . . . . . 5
2.1.1 Reddit REST-full API Wrapper . . . . . . . . . . . . . . . . . . . 6
2.1.2 Statistical Analysis of the r/technology Feed . . . . . . . . . . . . 7
2.2 Graph Analysis of Social Networks . . . . . . . . . . . . . . . . . . . . . . 16
2.2.1 Centrality Measures in Social Network Graphs . . . . . . . . . . . 17
2.2.2 Community Detection in Social Network Graphs . . . . . . . . . . 19

3 Text Mining 22
3.1 Data Mining for Topic Modelling . . . . . . . . . . . . . . . . . . . . . . . 22
3.1.1 LDA Parameter Tuning . . . . . . . . . . . . . . . . . . . . . . . . 22
3.1.2 LDA vs LSI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.1.3 Topic Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.2 Machine Learning for Sentiment Analysis . . . . . . . . . . . . . . . . . . 38
3.2.1 Random Forest Multi-Label Classification . . . . . . . . . . . . . . 38
3.2.2 Random Forest Binary Classification . . . . . . . . . . . . . . . . . 45
3.2.3 Recurrent Neural Network Classification . . . . . . . . . . . . . . . 49
3.2.4 Application of Trained Models for Sentiment Analysis . . . . . . . 53
3.3 Extractive Summarisation . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

4 Appendix 60
4.1 Reddit API Wrapper Class . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.2 Radial Plot Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.3 Facebook Social Network Graph Analysis Reports . . . . . . . . . . . . . 65
4.3.1 Degree Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.3.2 Weighted Degree Report . . . . . . . . . . . . . . . . . . . . . . . . 66
4.3.3 Graph Distance Report . . . . . . . . . . . . . . . . . . . . . . . . 67
4.3.4 Modularity Report . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.3.5 Statistical Inference Report . . . . . . . . . . . . . . . . . . . . . . 68
4.4 Text Pre-Processing Utility Class . . . . . . . . . . . . . . . . . . . . . . . 68
4.5 Extractive Summarization Utility Functions . . . . . . . . . . . . . . . . . 71

List of Tables
1 Feature description for the extracted Reddit. . . . . . . . . . . . . . . . . 8
2 K-Fold validation scores for multi-label classification. . . . . . . . . . . . . 43
3 Validation run for the multi-class classifier. . . . . . . . . . . . . . . . . . 44
4 K-Fold validation scores for binary-label classification. . . . . . . . . . . . 47
5 Validation run for the bi-class model. . . . . . . . . . . . . . . . . . . . . . 48

Page 1
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

List of Figures
1 Connecting to the Reddit API via the wrapper class. . . . . . . . . . . . . 6
2 Extracting data for Reddit posts via the API wrapper class. . . . . . . . . 7
3 Reddit data-set cleaning. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4 Most popular source domains for the ”r/Technology” feed (at 26th March
2023). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5 Distribution listings per domain for the ”r/Technology” feed (at 26th
March 2023). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
6 Data extraction and normalization for comparing the top post on a sub-
reddit feed. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
7 Comparing the top posts on a subreddit feed. . . . . . . . . . . . . . . . . 13
8 Comparing methods for deciding the ”top” listing for a subreddit against
Reddits algorithm. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
9 Comparing topic occurrences for the ”r/Technology” feed (at 26th March
2023). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
10 Visualising number of posts tagged with the ”Business” topic in the
”r/Technology” subreddit per day. . . . . . . . . . . . . . . . . . . . . . . 16
11 Overview of the ego-Facebook graph. . . . . . . . . . . . . . . . . . . . . . 17
12 Graph properties calculations using the Gephi tools. . . . . . . . . . . . . 18
13 Graph visualisation - node size by betweenes centrality. . . . . . . . . . . 19
14 Community detection by modularity class. . . . . . . . . . . . . . . . . . . 20
15 Community detection by modularity class - Force Atlas adjusted by node
size. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
16 Community detection by modularity class - Force Atlas adjusted by node
size and filtered for a higher degree centrality range. . . . . . . . . . . . . 21
17 Word cloud resulting from the aggregated news headlines in the r technology new [Link]
data-set. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
18 Top coherence results for LDA parameters combinations. . . . . . . . . . . 24
19 Resulting data-frame for results comparison, per data-set, per pass. . . . . 26
20 Sample one CV coherence scores. . . . . . . . . . . . . . . . . . . . . . . . 27
21 Sample one UMass coherence scores. . . . . . . . . . . . . . . . . . . . . . 28
22 Sample two CV coherence scores. . . . . . . . . . . . . . . . . . . . . . . . 29
23 Sample two UMass coherence scores. . . . . . . . . . . . . . . . . . . . . . 30
24 Sample three CV coherence scores. . . . . . . . . . . . . . . . . . . . . . . 31
25 Sample three UMass coherence scores. . . . . . . . . . . . . . . . . . . . . 32
26 Sample four CV coherence scores. . . . . . . . . . . . . . . . . . . . . . . . 33
27 Sample four UMass coherence scores. . . . . . . . . . . . . . . . . . . . . . 34
28 Frequency distribution for the aggregated news headlines in the r technology new [Link]
data-set. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
29 Most important words per topic via LDA model. . . . . . . . . . . . . . . 36
30 Most important words per topic via LSI model. . . . . . . . . . . . . . . . 37
31 Comparing per topic coherence scores between the LDA and LSI modes
for the r technology new [Link] data-set. . . . . . . . . . . . . . . . . . . 37
32 Visualising topic data for the trained LDA model with the pyLDAvis library. 38
33 Loading the multi-class data-set. . . . . . . . . . . . . . . . . . . . . . . . 39

Page 2
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

34 Distribution of samples per sentiment in the multi-class data-set. . . . . . 40


35 Visual analysis for the multi-class data-set. . . . . . . . . . . . . . . . . . 41
36 Testing balance in label distribution in the train-test split for the multi-
class model. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
37 Hyper-parameter tuning with GridSearchCV for the multi-class classifier. 43
38 Confusion matrix on the testing set for the final multi-class RFC model. . 44
39 Most important features for the final multi-class RFC model. . . . . . . . 45
40 Loading the bi-class data-set. . . . . . . . . . . . . . . . . . . . . . . . . . 46
41 Distribution of samples per sentiment in the bi-class data-set . . . . . . . 46
42 Visual analysis for the bi-class data-set. . . . . . . . . . . . . . . . . . . . 47
43 Confusion matrix on the testing set for the final bi-class RFC model. . . . 48
44 Most important features for the final bi-class RFC model. . . . . . . . . . 49
45 RNN architecture for sentiment analysis. . . . . . . . . . . . . . . . . . . . 51
46 Training the RNN model on binary classification data. . . . . . . . . . . . 52
47 Classification report for the RNN model. . . . . . . . . . . . . . . . . . . . 52
48 Confusion Matrix for the trained RNN model. . . . . . . . . . . . . . . . . 53
49 Preparing pre-trained RFC models for sentiment analysis. . . . . . . . . . 54
50 Sentiment analysis on sample movie reviews with the multi-class RFC
model. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
51 Sentiment analysis on sample movie reviews with the bi-class RFC model. 56
52 Sentiment analysis on Reddit comments with the multi-class RFC model. 56
53 Sentiment analysis on Reddit comments with the bi-class RFC model. . . 57
54 Fetching the top post on ”r/WorldNes” then extracting the news body
via the BeautifulSoup library. . . . . . . . . . . . . . . . . . . . . . . . . . 58
55 Summarizing the target text. . . . . . . . . . . . . . . . . . . . . . . . . . 59
56 Degree distributions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
57 Weighted degree distributions. . . . . . . . . . . . . . . . . . . . . . . . . 66
58 Graph distances distributions. . . . . . . . . . . . . . . . . . . . . . . . . . 67
59 Communities size distribution by modularity class. . . . . . . . . . . . . . 68
60 Communities size distribution by statistical inference class. . . . . . . . . 68

Page 3
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Glossary
API Application Programming Interface
LDA Latent Dirichlet Allocation
LSI Latent Semantic Indexing
NLP Natural Language Processing
NN Neural Network
RFC Random Forest Classifier
RNN Recurrent Neural Network
SNA Social Network Analysis
TFIDF Term Frequency — Inverse Document Frequency

Page 4
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

1 Introduction
Given the rapid expansion of the Internet, social media has become an indispensable
part of our lives. Daily, billions of users around the world are sharing their experiences,
thoughts and opinions on various online platforms, creating digital content. The vast
amount of data generated by these interactions presents a vital opportunity for businesses
to gain insights into their customers preferences, behavior, and sentiment towards their
brand. As the potential of this information became apparent, a new field of study
specialised in extracting valuable insights from large social networks has emerged. This
sub-field is commonly known as social media analytics.
One of the key challenges in analyzing social media content is dealing with the vast
amount of unstructured data that is generated on these platforms. Natural Language
Processing (NLP), a sub-field of artificial intelligence that deals with the interaction
between computers and human language, has become an essential tool for analyzing
social media data.
This report explores the intersection of social media analytics and natural language
processing. The following sections discuss the various techniques and tools used in social
media analytics, followed by NLP methods such as topic modeling, sentiment analysis
and text summarisation.

2 Statistical Analysis
Social media analytics is the process of gathering and analyzing data from social networks
such as Facebook, Instagram, LinkedIn, or Twitter. It a specialised sub-field of analytics
focused on extracting valuable hidden insights from vast amounts of semi-structured
and unstructured social media data to enable informed and insightful decision making
(Sponder and Khan, 2017).
There are three main steps in analyzing social media:

1. Data Identification

2. Data Analysis

3. Information Interpretation

To maintain focus and thus maximizing the value derived at every point during the
process, analysts may define questions to be answered through the insight gained within
the data. These questions help in determining the proper data sources to evaluate, which
can affect the type of analysis that can be performed (Ganis and Kohirkar, 2015).

2.1 Social Media Analytics for sub-Reddit Feeds


The target platform for this analitical experiment is Reddit, an American social news
aggregation, content rating, and discussion website. Registered users can vote on, com-
ment or post on the site different content such as links, text posts, images, and videos.
Posts are organized by subject into user-created boards called ”communities” or ”sub-
reddits”, which are moderated both at a global level by Reddit administrators as well
as locally, by community-specific moderators.

Page 5
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

2.1.1 Reddit REST-full API Wrapper


As of the date of writing this report, Reddit provides free access to their API endpoints
that are used to populate the online platform with content. To streamline the process of
data extractions, a custom wrapper has been developed. The wrapper class handles con-
nection, allowing to safely store the authentication credentials in an ”.ini” file (Figure 1).
The entire Python code for the wrapper can be found in Appendix 4.1.

Figure 1: Connecting to the Reddit API via the wrapper class.

Extracting data for Reddit posts is done through the wrapper object by calling
its ”PullSubredditFeedRaw” and ”PullSubredditFeed” methods (Appendix 4.1). The
”PullSubredditFeed” is the primary method of fetching Reddit posts, since it has the
added utility of transforming the incoming data from a semi-structured JSON format
into a Pandas data-frame object (Figure 2). The parameters of these functions dictate
(in order as per Figure 2) the subreddit where the posts originate, the filtering method
(”top” or ”all”) and the limit of records to return.

Page 6
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 2: Extracting data for Reddit posts via the API wrapper class.

2.1.2 Statistical Analysis of the r/technology Feed


To experiment with various visualisation and analytical techniques, we target the ”r/Tech-
nology” subreddit and attempt to extract various information about trends and topics
pertinent to the context of the group. ”r/Technology” is a subreddit dedicated to the
news and discussions about the creation and use of technology and its surrounding is-
sues. The large majority of posts in this subreddit consists of news titles from external
sources and links to said sources, turning the feed into a convenient compilation of tech
news from various online outlets.
Following best practices for social media analytics (Ganis and Kohirkar, 2015), we
establish four core question about the chosen subreddit and aim to answer them by
analysing data extracted from its feed:
Q1 What is the most utilised/popular domain to gather news for this subreddit?
Q2 What is the most popular post within the gathered subreddit feed?
Q3 How does the top post on a subreddit (extracted via the corresponding API end-
point) compares to one manually selected from the historical feed?
Q4 What is distribution of post for a given topic over a time period?
The first step in the analytical process is to extract the relevant data and ensure its
integrity. We fetch the posts via the API wrapper then store them locally (Figure 2).
The stored data-sets serve to ensure consistency during the development of the analytical
pipeline and counts as training data for the NLP applications explored in Section 3.
The specific data used in this report section for analysis consists of two data-sets:
• r technology new [Link] - Contains all the extractable1 listings from the ”r/Tech-
1
The number of posts that can be extracted from a subreddit feed via the API is limited to a maximum
of 1000 listing. The resulting data is also conditioned by multiple chronological factors abstracted by
the Reddit server architecture, meaning that older listing will not be accessible even if within the 1000
results limit.

Page 7
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

nology” subreddit at the date of 26th of March, 2023, ordered chronologically from
the newest to the oldest post.

• r technology top [Link] - Contains the top2 listing from the ”r/Technology”
subreddit at the date of 26th of March, 2023, ordered chronologically from the
newest to the oldest post.

All datasets extracted via the API wrapper hold the same features structure, available
in Table 2.1.2.
Table 1: Feature description for the extracted Reddit.

Feature Name Data Type Feature Description


author String Online handle for the author of the listing.
subreddit String Subreddit in which the listing was posted.
title String Title of the listing.
selftext String Text body attached to the listing.
upvote ratio Float Up-vote ratio for a listing.
ups Integer Number of user up-votes for the listing.
downs Integer Number of user down-votes for the listing.
score Integer Popularity score.
link flair text String Custom tag attached by the author of the listing.
is original content Boolean Does the content of the listing belong to the au-
thor.
is video Boolean Is the listing a video.
post hint String The type of the post e.g. a link, a video, an
image etc.
ulr String URL to an external online resource that the post
represents.
created utc String Date and Time when the listing was posted.
id String Unique identifier of the listing within the feed.
kind String Meta-data tag classifier for the listing.
total awards received Integer Number of total awards received from other
users.
num comments Integer Number of user comments on the post.
num crossposts Integer Number of occurrences of the post in different
subreddits.
num reports Integer Number of user reports on the post.
domain String Online domain where the listing content origi-
nates from.

Question #1 (Q1): What is the most utilised/popular domain to gather


news for this subreddit?
Priming for answering the first analitical question, the r technology new [Link]
data-set is loaded (Figure 4). The data-set is first cleaned of invalid (empty) records and
2
The means of which Reddit servers discerns the ”top” post is abstracted, with only speculations
from the platforms users being available.

Page 8
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

then inspected using the built-in [Link] attribute and [Link]()


method.

(a) Loading saved subreddit feed data from local storage.

(b) Data-set before cleaning. (c) Data-set after cleaning.

Figure 3: Reddit data-set cleaning.

Question Q1 aims to identify which is the most influential online news outlet for
the ”r/Technology” subreddit. For this analysis we consider the degree of influence to
be directly correlated to the number of redistributed news within the feed in the recent
period of time at the time of data extraction. Meaning that we sum up individual domain
occurrences for the extracted feed and compare the results.
The following code is used to compute the total occurrences per each domain in the
data-set entries, then displays the bar plot in Figure 4:

1 # Find the values that occur more than 5 times


2 value_counts = reddit_data_cleaned['domain'].value_counts()
3 values_to_keep = value_counts[value_counts > 5].[Link]()
4 # Filter the dataframe to only include rows with those values
5 filtered_df =
,→ reddit_data_cleaned[reddit_data_cleaned['domain'].isin(values_to_keep)]
6 # Create a count plot using Seaborn
7 sbn.set_context("talk")
8 [Link](rc={'[Link]':(12,8.27)})

Page 9
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

9 [Link](font_scale=2)
10 [Link](data=filtered_df,
,→ y='domain').set(title='Most popular domain occurences', xlabel="Count",
,→ ylabel="News source domain" );

Figure 4: Most popular source domains for the ”r/Technology” feed (at 26th March
2023).

The r technology new [Link] sample presents a total of 176 individual domains as
sources for the listed tech news headlines. For a more focused visualisation, we choose
to display only the domains that occur in more than 5 listing, as per the previously
presented code snippet. From Figure 4 we can see that the two most popular news
sources in this subreddit are ”[Link]” and ”[Link]”, with the former
taking the first spot.
To obtain a better perspective on how the most popular news sources compare to the
ones that are used less, we aggregate all domains that occur less that 10 times within the
extracted feed. In Figure 5 we use a pie plot to visualise in which percentage of posts in
the entire feed does a domain occur. The data and plot for Figure 5 is generated using
the following Python code:
1 value_counts = reddit_data_cleaned['domain'].value_counts()
2 # Group values with less than 10 occurrences into an "Other" category
3 other_count = value_counts[value_counts < 10].sum()
4 other_values = list(value_counts[value_counts < 10].index)
5 value_counts = value_counts[value_counts >= 10]
6 value_counts['Other (Less than 10 occurences per domain)'] = other_count
7
8 # Create a pie chart using MatPlotLib

Page 10
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

9 fig, ax = [Link](figsize=(10, 10))


10 sbn.set_palette('colorblind')
11 [Link](font_scale=1.5)
12 [Link](value_counts.values, labels=value_counts.index, autopct='%1.1f%%',
,→ startangle=90)
13 ax.set_title('Distribution of domain occurences in the collected data-set.')
14 [Link]()

Figure 5: Distribution listings per domain for the ”r/Technology” feed (at 26th March
2023).

Question #2 (Q2): What is the most popular post within the gathered
subreddit feed?
The second question which this analytical pipeline covers is used to identify the
most popular (or ”top”) post within the extracted feed. Reddits ranking algorithms
for different categories (”best”, ”top”, ”rising”, and ”controversial”) are closed-source,
allowing the user-base of the platform to only speculate how these calculations are made.
This analysis attempts to classify the top post within a collection of listings without
relying on the ordering provided by the Reddit API endpoints. We do so by comparing
the most relevant post parameters that would dictate its popularity, namely the up-vote
ratio, number of up-votes, number of comments and number of cross-posts (Table 2.1.2).
The experiments for Q2 sees the analysis performed on the r technology top [Link]
data-set, which contains listings with higher degrees of interaction from the platform
users, giving us bigger data dimensions for the selected features so that we can visualise
differences more easily. The data-set is loaded from local storage and the numerical
features are then normalised in order to match the numerical dimension of the up-vote

Page 11
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

ratio (Figure 6), so that the plot visualisation will be more coherent. With the normalised
data, we use the following Python script to generate the plot in Figure 7:
1 # Plotting the figure
2 fig, ax = [Link](figsize=(10, 5))
3 pivot_data = reddit_data_top_subset.sample(5).melt(id_vars = 'title_truncated',
,→ value_vars=['upvote_ratio', 'ups', 'num_comments', 'num_crossposts'])
4 ax.tick_params(axis='x', rotation=45)
5 [Link](x='title_truncated', y='value', hue='variable', data=pivot_data,
,→ ax=ax);
6 sbn.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
7 [Link](fig)

Figure 6: Data extraction and normalization for comparing the top post on a subreddit
feed.

Looking at Figure 7, five random listing from the r technology top [Link] are being
compared. Based on the number of up-votes and the up-vote ratio, we could consider the
fifth post (left to right) to be more popular. Yet, given the social aspect of the platform,
it could be considered that stronger user interactions and outreach (user commenting on
the post and number of cross-posts respectively) would serve as more weighted metrics
for this comparison. Therefore, making the third listing the most popular one within the
subreddit feed. This proves that nominating a top post could be subjective to the party
that is performing the analysis, thus this experiments serves rather as demonstration,
more than directly answering question Q2.

Page 12
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 7: Comparing the top posts on a subreddit feed.

Question #3 (Q3): How does the top post on a subreddit (extracted via
the corresponding API end- point) compares to one manually selected from
the historical feed?
To complement the findings for Q2 and offer further insight for those tasked with
decision making duties, from the extracted feed, we compare top posts by different met-
rics with the true top post by Reddits standards. To do so, we generate a series of
radial plots (Figure 8), overlapping the parameters of the top Reddit post with those
of the per-metric top post, in different categories. To produce the plots in Figure 8
we use a function whose code is available in the Appendix section 4.2. Before we ap-
ply the function and produce the plots, we select the top posts per category from the
r technology top [Link] data-set using the following code:
1 # Getting the top entries from each daset and per category, respectively
2 top_post_subreddit = reddit_data_top.loc[:0]
3 top_post_ups = reddit_data_cleaned.sort_values(by=['ups'], ascending=False,
,→ ignore_index=True).loc[:0]
4 top_post_comments = reddit_data_cleaned.sort_values(by=['num_comments'],
,→ ascending=False, ignore_index=True).loc[:0]
5 top_post_crossposts = reddit_data_cleaned.sort_values(by=['num_crossposts'],
,→ ascending=False, ignore_index=True).loc[:0]
6 top_post_ratio = reddit_data_cleaned.sort_values(by=['upvote_ratio'],
,→ ascending=False, ignore_index=True).loc[:0]

The resulting radial plots in Figure 8 reveal that up-votes and cross-posts metrics com-
bined (Sub-figure 8a) offer the best overlap in parameters with the top post by Red-
dit algorithms. Therefore, it can be considered that up-votes and cross-posts numbers
weight more in interpreting which post is more popular within a subreddit feed, at least
by comparing to the coveted Reddit standards.

Page 13
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

(a) Top listing by up-votes and cross-posts. (b) Top listing by number of comments.

(c) Top listing by up-vote ratio.

Figure 8: Comparing methods for deciding the ”top” listing for a subreddit against
Reddits algorithm.

Question #4 (Q4): What is distribution of post for a given topic over a


time period?
The listings posted on the ”r/Technology” subreddit must have a certain ”link flair text”
label value (Table 2.1.2), as a group enforced rule. The label reflects the wider topic
that a news headline is about, such as business, economics, hardware, security, politics,
crypto etc. To answer Q4, we aim to produce a timeline visualisation for the number of
posts per day that have a certain topic. For the visualisation to be meaningful, we first
pick the most occurring topic within the r technology new [Link] data-set by using the
code bellow:

1 # First we look at the most occuring topic, to choose as point of interest


2 value_counts = reddit_data_cleaned['link_flair_text'].value_counts()
3 [Link](data=reddit_data_cleaned,
,→ y='link_flair_text').set(title='Topics occurences', xlabel="Count",
,→ ylabel="Topic");

Page 14
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 9: Comparing topic occurrences for the ”r/Technology” feed (at 26th March
2023).

As seen in Figure 9, the majority of listings posted in the ”r/Technology” subreddit


(at the date of extraction) are news about the business aspect of the tech field. This
topic then becomes the target for the occurrence visualisation, the plot for which is
produced with the code below:

1 # The we group the data and plot a line chart for the 'Business' topic
2 data = reddit_data_cleaned[['link_flair_text', 'created_utc']]
3 data = [Link](['link_flair_text', 'created_utc']).size()
4 data = [Link]([Link]["Business"], columns=['count']).reset_index()
5

6 # Plot the line


7 [Link](rotation=45)
8 sbn.set_context("talk")
9 [Link](rc={'[Link]':(12,8)})
10 [Link](font_scale=1.5)
11 [Link](x='created_utc', y='count', data=data, color="navy",
,→ linestyle='dashed', marker='o').set(title="Topic occurences per date",
,→ xlabel="Date Posted", ylabel="Count");

The resulting line plot can be seen in Figure 10. Based on the this visualisation
we can discern that, within the 15-25th March 2023 period, business content on the
”r/Technology” subreddit peaked on the 16th with 23 total post, decreasing over the
course of the next 3 days, then rising in frequency over the next 4 days, and then again
loosing popularity towards the last day in the interval.

Page 15
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 10: Visualising number of posts tagged with the ”Business” topic in the
”r/Technology” subreddit per day.

2.2 Graph Analysis of Social Networks


Social network analysis (SNA) is the process of investigating social structures through
the use of networks and graph theory. It involves identifying the structure of the network,
the patterns of connections between nodes, and the properties of individual nodes and
the network as a whole. This type of analysis is used to gain insights into a variety
of social phenomena, such as the spread of information or influence, the formation of
cliques or subgroups, and the emergence of social hierarchies.
SNA and social network graphs work together to provide a comprehensive under-
standing of a social network. A social network graph is composed of nodes, which
represent individual entities, and edges, which represent the connections or relationships
between the nodes. Social network graphs can be used to represent a variety of networks,
such as friendship networks, collaboration networks, and communication networks. So-
cial network graphs provide a visual representation of the network, while SNA provides
a quantitative analysis of the network’s structure and properties.
This section of the report presents different graph network exploration techniques
using the Gephi graph visualiser tool (Bastian, Heymann, and Jacomy, 2009). The data
used for the presented analysis is the ”ego-Facebook” data-set sourced from the Stanford
Network Analysis Project archive, originally published by Leskovec and Mcauley (2012).

Page 16
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

The network graph contains 4039 nodes with 88234 directed edges. To obtain an overview
of the whole graph network, we first use the Yifan Hu layout to distance the sub-graphs,
then apply the Force Atlas layout algorithm to further expand individual nodes per
cluster (Figure 11).

Figure 11: Overview of the ego-Facebook graph.

2.2.1 Centrality Measures in Social Network Graphs


Centrality measures are used to identify nodes that are highly connected or influential
in the network. The most commonly used centrality measures are degree centrality,
betweenness centrality, and eigenvector centrality.
Degree centrality measures the number of connections or edges that a node has.
Nodes with high degree centrality are highly connected and may be influential in spread-
ing information or influence through the network.
Betweenness centrality measures the number of shortest paths that pass through
a node. Nodes with high betweenness centrality are important for maintaining commu-
nication and information flow between different parts of the network.
Eigenvector centrality measures a node’s importance based on the importance of
the nodes it is connected to. Nodes with high eigenvector centrality are connected to
other highly important nodes, which makes them influential in the network.
These metrics can be calculated by using the extensive toolkit of the Gephi software,

Page 17
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

as seen in Figure 12. A complete series of visual reports for the graphs properties can
be found in the Appendix, Section 4.3.

Figure 12: Graph properties calculations using the Gephi tools.

For visualising the most important nodes in the target network, betweenes centrality
is used. Figure 13 displays the nodes with varying sized, depending on their degree of
betweenes centrality.

Page 18
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 13: Graph visualisation - node size by betweenes centrality.

2.2.2 Community Detection in Social Network Graphs


Community detection is the process of identifying groups or subgroups of nodes that are
densely connected within the network but less connected to nodes outside the group.
It is an useful technique for identifying subgroups within a network and understanding
how they interact with each other. There are different community detection algorithms
which rely on different techniques to identify subgroups, such as modularity optimization
and spectral clustering.
We base our community detection experiment on modularity classes, using Gephi
to calculate modularity between all nodes and display likely comunities (Figure 14).
The modularity optimisation used by the Gephi software is based on the algorithms
conceived by Blondel et al. (2008). The visualisation is further enhanced in Figure 15 by
reapplying the Force Atlas algorithm to the simulation, this time taking into account the
node size (which is dictated by betweenness centrality). Finally, we also limit the range
of displayed nodes based on their degree centrality, resulting the network visualisation
in Figure 16.

Page 19
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 14: Community detection by modularity class.

Figure 15: Community detection by modularity class - Force Atlas adjusted by node
size.

Page 20
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 16: Community detection by modularity class - Force Atlas adjusted by node
size and filtered for a higher degree centrality range.

Page 21
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

3 Text Mining
This section of the report records the application of different NLP techniques on posts
extracted from the feeds of multiple subreddits. The techniques detailed in the following
sections are:

• Topic Modelling - Topic modelling is a technique used in the field of text mining
to automatically identify topics present in a text object and to derive hidden
patterns. It is a type of statistical modelling for discovering the abstract ”topics”
that occur in a collection of documents, making it an effective tool for discovery
of hidden semantic structures.

• Sentiment Analyisis - Sentiment analysis is the process of detecting positive


or negative sentiment within text content. It is widely applied to voice-of-the-
customer-materials such as reviews and serves to reveal customers behavior and
sentiment towards a certain topic, product, entity etc.

• Text Summarization - Text summarization is a technique used to generate con-


cise and precise summaries of voluminous texts while focusing on the sections that
convey useful information without losing the overall meaning. While is widely im-
plemented via NLP techniques, certain computer vision algorithms are also used.

3.1 Data Mining for Topic Modelling


To demonstrate the capabilities of topic modeling in social media analytics, the following
experiment attempts to identify the primary discussion topics from the ”r/Technology”
feed. The modelling is applied on the headlines listed within the ”top” posts feed of the
subreddit. We compare both Latent Dirichlet Allocation (LDA) (Blei, Ng, and Jordan,
2003) and Latent Semantic Indexing (LSI) (Hofmann, 1999) based models in terms of
performance and apply them on the case study data-set.

3.1.1 LDA Parameter Tuning


Before any proper testing is carried out, we attempt to even the field between the two
models. We proceed with tuning the LDA model, the algorithm for which is provided
through the Gensim Python library, and has more parameter options that the LSI one
(from the same library).
The r technology new [Link] data-set is loaded into a Jupyter environment via Pan-
das, where all the listing titles are collected into a list. We the apply pre-processing to the
text data, by tokenizing it, removing stop words and punctuation and applying lemma-
tization. This process is carried out via a custom utility function ”process corpus”.
Similar to the API wrapper class (Appendix 4.1), a text utility class was developed in
order to more easily pre-process text for NLP and reduce code clutter. The full code for
the class and included methods can be found in Appendix 4.4. The following Python
code is used to load and pre-process text and display a word cloud (Figure 17) for the
title data in the target data-set:

1 # Importing saved data


2 df = pd.read_csv("./Data/r_technology_top_all.csv")

Page 22
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

3 documents = [Link]().values
4 print(f"{len(documents)} kept from a total of {len([Link])}")
5
6 # Pre-processing title data into tokens - stop-word removal; punctuation
,→ removal; lemmatization.
7 from [Link] import TextPreprocess
8 textPreprocess = TextPreprocess()
9 tokens_data = []
10 for document in documents:
11 tokens = textPreprocess.process_corpus(document,
,→ word_reduce_strategy='lemma')
12 tokens_data.append(tokens)
13
14 # Generating dictionary and corpus for LDA and LSI processing
15 dictionary = [Link](tokens_data)
16 corpus = [dictionary.doc2bow(token) for token in tokens_data]
17
18 # Flattening tokens list for word cloud
19 tokens_list = [token for sublist in tokens_data for token in sublist]
20 frequency_distribution = [Link](tokens_list)
21 # Showing word cloud
22 wordcloud = WordCloud(max_font_size=50, max_words=100,
,→ background_color="black").generate_from_frequencies(frequency_distribution)
23 [Link](figsize=(10,6))
24 [Link](wordcloud, interpolation="bilinear");

Figure 17: Word cloud resulting from the aggregated news headlines in the
r technology new [Link] data-set.

To quickly cover multiple parameter combinations, we grid-search through different


options for the ”iterations” and ”num topics” parameters of the LDA model. For each
topic option we retain the ”iterations” parameter value that resulted in the highest CV

Page 23
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

coherence score. Based on the resulting scores (Figure 18), on average, an ”iterations”
value of 40 would result in more optimal coherence for the targeted data-set. The
following Python is used to carry out the optimisation process:

1 iteration_options = [10,20,30,40,50,60,70,80,90,100]
2 num_topics_options = [2,4,6,8,10]
3
4 iter_coherence_scores = []
5 for num_topics_option in num_topics_options:
6 iteration_scores = []
7 for iteration_option in iteration_options:
8 lda_model = [Link](corpus, id2word=dictionary,
,→ iterations=iteration_option, num_topics=num_topics_option)
9 coherence_model_lda_cv = CoherenceModel(model=lda_model,
,→ texts=tokens_data, dictionary=dictionary, coherence='c_v')
10 coherence_score = coherence_model_lda_cv.get_coherence()
11 iteration_scores.append([iteration_option, coherence_score])
12
,→ print(f"For num_topics={num_topics_option} and iterations={iteration_option}
13 coherence score is => {coherence_score}")
14 best_iteration = sorted(iteration_scores, key=itemgetter(1),
,→ reverse=True)[0]
15 print(f" | => Best iterations number for {num_topics_option} topics is
16 {best_iteration[0]} with coherence of {best_iteration[1]}.")
17 iter_coherence_scores.append([num_topics_option, best_iteration[0],
,→ best_iteration[1]])

Figure 18: Top coherence results for LDA parameters combinations.

3.1.2 LDA vs LSI


To thoroughly and reliably compare the performance of the two algorithms based on
coherence scores, the resulting models are tested within the following parameters:

• Tested over four data-sets;

• Four test passes per data-set;

• Testing both UMass and CV coherence;

• Testing coherence scores for 1 to 10 topics per model;

Page 24
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

The data-sets correspond with the top listings from four different subreddits, namely
”r/News”, ”r/WordlNews”, ”r/Science” and ”r/Technology”. These additional subred-
dits show similar attributes to ”r/Technology”3 , utilised as a case study in the previous
sections of the report. All data was extracted within the same time-frame on the 1st of
May, 2023. For each testing sample, we test for both UMass and CV coherence scores,
comparing side-by-side the performance of the algorithms.
First, we load and pre-process all the title data while keeping track of its origin
data-set:
1 from [Link] import TextPreprocess
2 textPreprocess = TextPreprocess()
3

4 # Importing saved data


5 test_csvs = ["r_news_top_all_01_05_2023.csv",
6 "r_science_top_all_01_05_2023.csv",
7 "r_technology_top_all_01_05_2023.csv",
8 "r_worldnews_top_all_01_05_2023.csv"]
9

10 # Reading data and saving preprocessed versions into Python dictionaries


11 reddit_test_data = []
12 for csv_path in test_csvs:
13 df = pd.read_csv(filepath_or_buffer=("./Data/" + csv_path))
14 documents = [Link]().values
15

,→ print(f"{csv_path} => {len(documents)} kept from a total of {len([Link])}")


16
17 # Inner dictionary
18 title_data = {}
19 tokens_data = []
20 for document in documents:
21 tokens = textPreprocess.process_corpus(document,
,→ word_reduce_strategy='lemma')
22 tokens_data.append(tokens)
23 # Generating words dictionary and corpus for LDA and LSI processing
24 dictionary = [Link](tokens_data)
25 corpus = [dictionary.doc2bow(token) for token in tokens_data]
26
27 # Assembling inner Python dictionary per data-set
28 title_data['tokens'] = tokens_data
29 title_data['dictionary'] = dictionary
30 title_data['corpus'] = corpus
31 # Adding complete dictionary to list for future iterative testing
32 reddit_test_data.append(title_data)
Then, for each data-set, for each pass, we calculate the UMass and CV coherence scores
between the LDA and LSI models for the same number of topics, from 1 to 10 topics.
The results are collected in data-frames (Figure 19) by using the following code:
1 # reddit_test_data[0]
2 scores_data = []
3
Most crucial similarity between these case study subreddits is that all of them contain mostly re-
distributed content in the form of news headlines, sourced from different outlets, for different domains
depending on the context of the subreddit.

Page 25
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

3 for i in range(10):
4 print(f"Begining test iteration for num_topics={i+1}")
5 lda_model = [Link](iteration_sample_corpus,
,→ id2word=iteration_sample_dictionary, iterations=40, num_topics=i+1)
6 lsi_model = [Link](iteration_sample_corpus,
,→ id2word=iteration_sample_dictionary, num_topics=i+1)
7
8 coherence_model_lda_umass = CoherenceModel(model=lda_model,
,→ corpus=iteration_sample_corpus, dictionary=iteration_sample_dictionary,
,→ coherence='u_mass')
9 coherence_model_lsi_umass = CoherenceModel(model=lsi_model,
,→ corpus=iteration_sample_corpus, dictionary=iteration_sample_dictionary,
,→ coherence='u_mass')
10 coherence_model_lda_cv = CoherenceModel(model=lda_model,
,→ texts=iteration_sample_tokens, dictionary=iteration_sample_dictionary,
,→ coherence='c_v')
11 coherence_model_lsi_cv = CoherenceModel(model=lsi_model,
,→ texts=iteration_sample_tokens, dictionary=iteration_sample_dictionary,
,→ coherence='c_v')
12
13 scores_data.append(
14 [i+1,
15 # lda_model.log_perplexity(corpus),
16 # lsi_model.log_perplexity(corpus),
17 coherence_model_lda_umass.get_coherence(),
18 coherence_model_lsi_umass.get_coherence(),
19 coherence_model_lda_cv.get_coherence(),
20 coherence_model_lsi_cv.get_coherence()]
21 )
22
23 coherence_scores = [Link](data=scores_data,
,→ columns=["num_topics","lda_umass","lsi_umass","lda_cv","lsi_cv"])

Figure 19: Resulting data-frame for results comparison, per data-set, per pass.

Finally, the coherence scores calculation is done 16 times, four times for each testing
set. The resulting visualisations can be seen in Figures 20 through 27. Based on the

Page 26
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

experiment results, both algorithms performed similarly, with LDA being slightly more
coherent given the data used and domain off application. While LSI seems to have,
on average, a better CV coherence score, the LDA model maintained more consistent
CV trend lihes, and consistently better UMass. UMass score better reflects per-topic
coherence, which is more desirable given our type of targeted text format. In addition,
based on the optimisation process presented in Section 3.1.1 and the performance results
of both LDA and LSI models, the optimal number of topics which would offer best topic
coherence on Reddit news titles is four.

(a) First testing pass. (b) Second testing pass.

(c) Third testing pass. (d) Fourth testing pass.

Figure 20: Sample one CV coherence scores.

Page 27
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

(a) First testing pass. (b) Second testing pass.

(c) Third testing pass. (d) Fourth testing pass.

Figure 21: Sample one UMass coherence scores.

Page 28
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

(a) First testing pass. (b) Second testing pass.

(c) Third testing pass. (d) Fourth testing pass.

Figure 22: Sample two CV coherence scores.

Page 29
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

(a) First testing pass. (b) Second testing pass.

(c) Third testing pass. (d) Fourth testing pass.

Figure 23: Sample two UMass coherence scores.

Page 30
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

(a) First testing pass. (b) Second testing pass.

(c) Third testing pass. (d) Fourth testing pass.

Figure 24: Sample three CV coherence scores.

Page 31
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

(a) First testing pass. (b) Second testing pass.

(c) Third testing pass. (d) Fourth testing pass.

Figure 25: Sample three UMass coherence scores.

Page 32
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

(a) First testing pass. (b) Second testing pass.

(c) Third testing pass. (d) Fourth testing pass.

Figure 26: Sample four CV coherence scores.

Page 33
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

(a) First testing pass. (b) Second testing pass.

(c) Third testing pass. (d) Fourth testing pass.

Figure 27: Sample four UMass coherence scores.

3.1.3 Topic Analysis


The final application for the selected LDA and LSI models is to perform topic analysis
on the target data, namely the news titles within the r technology new [Link] data-set.
The data-set is loaded in a new Jupyter environment, and pre-processed in the exact
same manner as in Sections 3.1.1 and 3.1.2. In addition to the word cloud for this data-
set (Figure 17) we also visualise the frequency distribution of words for the title data, as
seen in Figure 28. This visualisation helps identifying early hints on the topics within
before we perform any NLP. For example, based on the frequencies alone, we can expect
”source code leak” to be one of the topics covered by the posted news, without having
to read any of the titles beforehand.

Page 34
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 28: Frequency distribution for the aggregated news headlines in the
r technology new [Link] data-set.

Two helper functions are used to format the extracted topics from the trained models:
1 # Helper functions
2 def format_topic(topic):
3 t = {}
4 t["id"] = topic[0]
5 a = topic[1].split(" + ")
6 t["words"] = {}
7 for i,m in enumerate(a):
8 k = [Link]("*")
9 if i == 0:
10 max_weight = float(k[0])
11 t["words"][k[1].replace('"','')]=float(k[0])/max_weight
12
13 return t
14

15 def get_topic_words(topic):
16 words = []
17 weights = topic[1].split(" + ")
18 t["words"] = {}
19 for i,m in enumerate(weights):

Page 35
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

20 k = [Link]("*")
21 [Link](k[1].replace('"',''))
22
23 return words

From Figures 29 and 30 we can see that the LDA model outperforms the LSI model
in the given scenario. The LDA topic output is more coherent and distinctive between
the four topic choices. Meanwhile, the LSI topic output tends to loose coherence between
the second and fourth topic, while also presenting similar topics.
The resulting topics and CV coherence scores in Figure 31 further reinforce the
previous affirmation about the performance of the two models. While the coherence
scores for the two models are fairly similar (approximately 0.015 difference), the per-
topic coherence of the LSI model is highly inconsistent, with higher highs and lower lows
for the presented topics.

(a) First topic. (b) Second topic.

(c) Third topic. (d) Fourth topic.

Figure 29: Most important words per topic via LDA model.

Page 36
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

(a) First topic. (b) Second topic.

(c) Third topic. (d) Fourth topic.

Figure 30: Most important words per topic via LSI model.

Figure 31: Comparing per topic coherence scores between the LDA and LSI modes for
the r technology new [Link] data-set.

Page 37
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 32: Visualising topic data for the trained LDA model with the pyLDAvis library.

3.2 Machine Learning for Sentiment Analysis


Sentiment analysis is a natural language processing technique that involves the use of
computational algorithms to identify and extract subjective information from textual
data. This technique is also known as opinion mining, and it is used to determine the
polarity (positive, negative, or neutral) of a given piece of text, such as a tweet or a
product review.
To perform sentiment analysis, we train three machine learning models on labeled
text data, where each label corresponds to a sentiment (positive, negative, or neutral).
The models created are based on the Random Forest Classifier (RFC) algorithms and a
custom built Neural Network (NN). For training data we use two movie reviews data-
sets, one with binary labels and one with multi-class labels. The RFC is an ensemble
model capable of being trained with both binary and multiple labels, resulting in two
different models depending on the data-set used. The NN model is trained only with
the binary label data-set. The trained models are optimised, evaluated and then used
to predict the sentiment of new, unseen text samples based on the patterns and features
learned from the training data-set. The new texts consists of live comments extracted
from top posts within the ”r/Technology” subreddit.

3.2.1 Random Forest Multi-Label Classification


The training data-set with multi-class labels contains 156060 records across five different
classes:
0 – negative

Page 38
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

1 - somewhat negative

2 – neutral

3 - somewhat positive

4 – positive

The structure of the data-set can be observed in Figure 33, where we can see that the re-
views corpus has been split into n-grams, organised by the ”PhraseId” and ”SentenceId”
columns. Sub-Figure 34a reveals a heavy imbalance between the classes. To adjust for
this imbalance and maximise the amount of usable data, we first merge the ”somewhat
positive” and ”somewhat negative” classes with ”positive” and ”negative” respectively
(Sub-Figure 34b). Then we further balance the labels by dropping 50% of the ”neutral”
data (Sub-Figure 34c). The final data-set now contains 116269 records across three
classes: 1 - negative; 2 – neutral; 3 - positive.

Figure 33: Loading the multi-class data-set.

Page 39
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

(b) Distribution of samples after label


(a) Original distribution of samples. merging.

(c) Distribution of samples after label merging


and reduction.

Figure 34: Distribution of samples per sentiment in the multi-class data-set.

Text analysis is performed on the original (pre-merge) data-set, selecting only the
text data that is not an n-gram. This results in 8529 records kept from a total of 156060.
The data is pre-processed into tokens and visually analysed using frequency distributions
and word clouds:

1 # Pre-processing title data into tokens - stop-word removal; punctuation


,→ removal; lemmatization.
2 tokens_data = []
3 for document in tqdm_notebook(documents, total=len(documents),
,→ desc="Processing corpus"):
4 # Using light-weight approach due to lenghty precessing time with the gensim
,→ pipeline (process_corpus ~= 2min per 100 records; clean_corpus ~= 1 sec per
,→ 100 records)
5 tokens = TextPreprocess.clean_corpus(document, extra_stop_words=['film',
,→ 'movie']) # removing expected clutter words
6 tokens_data.append(tokens)
7 tokens_list = [token for sublist in tokens_data for token in sublist]

Page 40
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

8 frequency_distribution = [Link](tokens_list)
9 # Plotting frequency distribution and showing word cloud
10 [Link](figsize=(12,8))
11 [Link]("Frequency Distribution of words", fontsize=20)
12 frequency_distribution.plot(30,cumulative=False)
13 TextPreprocess.generate_wordcloud_from_frequencies(frequency_distribution,"Word Cloud")

(a) Positive words word cloud. (b) Negative words word cloud.

(c) Frequency distribution of words.

Figure 35: Visual analysis for the multi-class data-set.

For machine learning applications it was preferred to use the ”clean corpus” method
form the TextPreprocess utility class (Appendix 4.4). This approach results in much
faster processing times4 for tokenizing, cleaning and lemmatizing text data.
After pre-processing, the analysis data is used to generate the plots in Figure 35.
The ”film” and ”movie” words are removed during pre-processing both for the analysis
and training data. Due to the nature of the training data-sets, these words appear with
4
The light-weight approach uses nltk processing methods, consuming on average one second per 100
records. The ”process corpus” method relies on the gensim processing pipeline, and while it is more
thorough, it averages at two minutes per 100 records.

Page 41
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

a frequency far above the top margin seen in Sub-Figure 42c, so they have been removed
in order to minimise noise in the trained models.
The text review data is is split, with 70% of the data-set being reserved for training
and 15% each for validation and testing. The split is performed with the stratify strategy,
ensuring the same distribution of target labels per split (Figure 36).

Figure 36: Testing balance in label distribution in the train-test split for the
multi-class model.

In order to be fitted on the RFC model, the feature sub-sets of the training, validation
and testing splits are vectorized using Term Frequency - Inverse Document Frequency
(TFIDF) algorithm:
1 # Vectorizer function for convenience
2 def vectorize(data,tfidf_vect_fit):
3 X_tfidf = tfidf_vect_fit.transform(data)
4 words = tfidf_vect_fit.get_feature_names_out()
5 X_tfidf_df = [Link](X_tfidf.toarray())
6 X_tfidf_df.columns = words
7 return(X_tfidf_df)
8
9 tfidf_vect = TfidfVectorizer(max_features=1000, analyzer=lambda x:
,→ TextPreprocess.clean_corpus(x, extra_stop_words=['film', 'movie']))
10 #tfidf_vect = TfidfVectorizer()
11 tfidf_vect_fit = tfidf_vect.fit(X_train['Phrase'])
12 X_train_vect = vectorize(X_train['Phrase'],tfidf_vect_fit)
13 X_val_vect = vectorize(X_val['Phrase'], tfidf_vect_fit)
14 X_test_vect=vectorize(X_test['Phrase'],tfidf_vect_fit)

The RFC model is then tested using K-Fold validation, default parameters and 5
folds. The resulting accuracy scores are recorded in Table 2. Then we perform hyper-
parameter tuning by using GridSearchCV with the following parameters: ”n estimators”:
[5,50,100]; ”max depth”: [2,10,20,None]. Based on the results (Figure 37) the opti-
mal parameters for the multi-class RFC model is a ”max depth” of ”None” and a
”n estimators” of 100. Based on the top three parameters combinations, three mod-

Page 42
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

els are then trained and tested on the validation set, measuring accuracy, recall and
precision.

Fold # R2 Score
1 0.62906991
2 0.63767048
3 0.62513822
4 0.63476071
5 0.63334767
Average 0.632

Table 2: K-Fold validation scores for multi-label classification.

Figure 37: Hyper-parameter tuning with GridSearchCV for the multi-class classifier.

Page 43
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Ma Depth # Number of Estimators Accuracy Precision Recall


20 100 0.758 0.612 0.758
None 100 0.892 0.701 0.892
None 5 0.714 0.648 0.712

Table 3: Validation run for the multi-class classifier.

The resulting measurements, recorded in Table 3, show that the top model from
the GridSearchCV run is indeed the most optimal. We encode and store the model via
pickling, then we proceed with the final testing run for the multi-class RFC. On the test
data-set we obtain a final measurement of 88% accuracy, 80% precision and 88% recall.
Figures 39 and 38 present the most important features for the sentiment analysis model
and the confusion matrix during classification.

Figure 38: Confusion matrix on the testing set for the final multi-class RFC model.

Page 44
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 39: Most important features for the final multi-class RFC model.

3.2.2 Random Forest Binary Classification


The training data-set for binary classification contains 50000 records across two classes:

0 – negative

1 - positive

Page 45
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 40: Loading the bi-class data-set.

Unlike the multi-label data-set (Section 3.2.1), the binary classification training data
contains only complete review phrases and no n-gram deconstructions (Figure 40). In
addition, the binary classification data-set is perfectly balanced in terms of class distri-
bution, as seen in Figure 41.

Figure 41: Distribution of samples per sentiment in the bi-class data-set

Page 46
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

We apply the same pre-processing on the complete review phrases in the bi-class
data-set and produced the analytical visualisations in Figure 42.

(a) Positive words word cloud. (b) Negative words word cloud.

(c) Frequency distribution of words.

Figure 42: Visual analysis for the bi-class data-set.

The text data within the bi-class data-set is pre-processed and vectorized in the
same manner as for the multi-label RFC model. Initial performance analysis on the
binary RFC model is carried out via cross-fold validation, with default parameters in
five folds. The resulting accuracy scores (Table 4) are much higher when compared to
the multi-label RFC model.
Fold # R2 Score
1 0.829
2 0.82571429
3 0.83314286
4 0.83071429
5 0.82628571
Average 0.829

Table 4: K-Fold validation scores for binary-label classification.

Page 47
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

The binary-classification data-set has fewer records and dimension than the multi-
label sample, allowing us to execute a wider GridSearchCV search with less compu-
tational resources. The following parameters are tested during the GridSearchCV op-
timisation: ”n estimators”: [5,10,25,50,75,100]; ”max depth”: [2,5,10,20,30,50,None].
Following the hyper-parameter tuning, the parameters that resulted in the most op-
timal model are ”None” for ”max depth” and 100 for ”n estimators”, similar to the
multi-label RFC model.
We compile results on the validation data-set (Table 5) to confirm that the afore-
mentioned parameters are most optimal and then store the produced model. Lastly,
we predict on the training set and obtain a final measurement of 92% accuracy, 93%
precision and 92% recall. Figures 43 and 44 present the most important features for
the binary sentiment analysis model and the confusion matrix after classifying on the
training set.

Ma Depth # Number of Estimators Accuracy Precision Recall


20 100 0.814 0.792 0.848
None 100 0.92 0.901 0.892
None 5 0.846 0.818 0.844

Table 5: Validation run for the bi-class model.

Figure 43: Confusion matrix on the testing set for the final bi-class RFC model.

Page 48
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 44: Most important features for the final bi-class RFC model.

3.2.3 Recurrent Neural Network Classification


The final Machine Learning experiment for NLP aims supplement the previously de-
veloped RFC models with a Recurrent Neural Network (RNN), as neural networks are
one of the most popular methods used in NLP due to their ability to learn patterns in
data and capture complex relationships between words. RNNs are considered to be par-
ticularly well-suited for NLP (Xiao and Zhou, 2020; Tarwani and Edem, 2017; Jelodar
et al., 2020), given they are designed to work with sequential data, such as sentences,
by processing each word in a sequence one at a time while maintaining a memory of the
previous words.
The training data for the RNN coincides with the data-set used for training the bi-
class RFC model (Section 3.2.2). The chosen model for our RNN is based on an 8-layer

Page 49
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

architecture, summarised in Figure 45 and assembled via the code bellow:


1 # Input Layer
2 inputs = Input(name='inputs',shape=max_len)
3 # Layer 2
4 layer = Embedding(2000,50,input_length=max_len)(inputs)
5 # Layer 3
6 layer = LSTM(64)(layer)
7 # Layer 4
8 layer = Dense(256,name='FC1')(layer)
9 # Layer 5
10 layer = Activation('relu')(layer)
11 # Layer 6
12 layer = Dropout(0.5)(layer)
13 # Layer 7 - classification
14 layer = Dense(1,name='out_layer')(layer)
15 # Layer 8 - output
16 layer = Activation('sigmoid')(layer)
17 # NN architecture assembly
18 model = Model(inputs=inputs,outputs=layer)

The training data is pre-processed in the same manner as for the other two models, with
the exception of excluding TFIDF vectorization, so that it would be compatible with
the input layer of our RNN model. The RNN model is trained for 6 epochs, with a 100
units batch size. Despite the very high accuracy scores reached during the training cycle
(Figure 46), the finished RNN model perform very poorly on testing data, as denoted in
the classification report in Figure 47.
The poor performance can be attributed to the pre-processing techniques usedand to
the interpretation of the predicted results. The RNN model returns a probability score
instead of the outright class, indicating which class is most likely to be. The current
interpretation strategy is to group all predictions with a probability higher than 0.5 as
”positive” sentiments and the opposite as ”negative” sentiments.
Proposed improvements for future iterations include:

• Refactoring the pre-processing stage so it would better fit a RNN model.

• Tuning of the individual layers of the model.

• A different interpretation strategy for the sentiment analysis results.

Page 50
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 45: RNN architecture for sentiment analysis.

Page 51
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 46: Training the RNN model on binary classification data.

Figure 47: Classification report for the RNN model.

Page 52
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 48: Confusion Matrix for the trained RNN model.

3.2.4 Application of Trained Models for Sentiment Analysis


We proceed to apply the trained RFC models for sentiment analysis on live comments
extracted from posts in the ”r/Technology” subreddit. Before the data is extracted, we
set up an utility function to vectorize text data directly before being fed into the RFC
models:

1 # Writing a function to quickly run predictions on pre-trained models


2 def sentiment_analysis_predict(target_text, trained_model, tfidf_vect_fit,
,→ labels):
3 """
4 Utility function to quickly run a sentiment analysis classification on a piece
5 of raw text.
6 :param target_text: Raw text to be classified
7 :type target_text: string[]
8 :param trainded_model: The trained classification model
9 :type trained_model: object
10 :param tfid_vect_fit: The fitted vectorizer used to encode the training set
11 for the given trained model
12 :type tfid_vect_fit: object
13 :type lables: List of custom labels for the sentiment classes; Match with the
14 prediction is index based
15 :type lables: string[]
16 """

Page 53
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

17 # Vectorizing target text based on the same schema used for training the
,→ given RFC model
18 vectorized_text = tfidf_vect_fit.transform(target_text)
19 words = tfidf_vect_fit.get_feature_names_out()
20 vectorized_text_df = [Link](vectorized_text.toarray())
21 vectorized_text_df.columns = words
22
23 predictions = binary_rfc.predict(vectorized_text_df)
24 return [labels[label_index] for label_index in predictions]

We load the optimised and trained RFC models from storage, and prime the TFIDF
vectorizers that will be used to individually fit test data to each classifier (Figure 49).

Figure 49: Preparing pre-trained RFC models for sentiment analysis.

We first asses the sentiment analysis capacity of the two models on actual movie
reviews, extracted from multiple sources. This test aims to prove that the models are
viable for text data that matches the same domain as their training data. The reviews
used of analysis are in order of prediction:

Review #1 - Positive:
”Great review. As a former mechanic and sales person for 12 years. I am a total need
when it comes to wrong audio being dubbed over cars and inaccuracies. Big budget
films must just get the directors cousin to be the automotive expert sometimes I swear.
So many inaccuracies of models, years or tech spec.”
Review #2 - Negative:

Page 54
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

”A negative rating for Negative. I’m not sure what accomplished director/producer/cin-
ematographer Joshua Caldwell was thinking taking on this project. This film has got to
be the epitome of terrible writing and should be a classroom example of ’what not to do’
when writing a screenplay. Why would Joshua take on (clearly) amateur writer Adam
Gaines script is beyond me. Even his good directing and excellent cinematography could
not save this disaster.”
Review #3 - Negative:
”No action there. Woman and dude take a car and drive away. Still nothing happens.
Half the movie, they finally met an old friend of the woman because of... convenient
script. A few stupid and useless speaking about food or dogs starts there. What’s the
point ? Guess it’s because amateurish script.”
Review #4 - Positive:
”But the clear highlight of an already-stellar cast (which also includes Noah Taylor and
Andy Nyman as fellow Annex residents) is Schreiber, whose Otto is the pillar upon
which the show truly rests. It’s a turn of noble resolve and determination, a quiet man
whose bravery spurs Miep to demonstrate some of her own. (Their scenes together, often
the quietest in the show, are some of the show’s best.) Schreiber’s always been a king
of speaking volumes through the quietest rumblings of his deep baritone, and this is a
stellar showcase for those qualities.”

The resulting sentiment classifications are visible in Figures 50 and 51. From the re-
sults, we can see that the binary model performs adequately, correctly identifying the
sentiment for each review. As for the multi-class model, the results hint that it may be
under-performing when discerning between positive and neutral sentiments.
Finally, the models are tested for their intended purpose, performing sentiment anal-
ysis on Reddit comments. From Figures 52 and 53 we can observe similar behaviours
as with the movie reviews. The multi-class RFC models correctly interprets negative
sentiments, but shows some bias towards the neutral status. The analysis is considered
to be fairly accurate, given that most of the comments analysed in the test sample are
either negative or neutral towards the topic of the post.
Looking at the sentiment analysis for the binary classifier, we can see good per-
formance in terms of predicting negative sentiments. We can also notice a tendency
of interpreting sarcastic or passive-aggressive comments as positive sentiments, which
could be considered correct given the tone of the discussion and that some of the tar-
geted comments are interpretable as jokes..

Figure 50: Sentiment analysis on sample movie reviews with the multi-class RFC
model.

Page 55
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 51: Sentiment analysis on sample movie reviews with the bi-class RFC model.

Figure 52: Sentiment analysis on Reddit comments with the multi-class RFC model.

Page 56
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 53: Sentiment analysis on Reddit comments with the bi-class RFC model.

Page 57
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

3.3 Extractive Summarisation


Text summarization is a sub-field of NLP that involves the process of generating a con-
cise and coherent summary of a longer piece of text, while retaining its key information
and meaning. Text summarization can be classified into two main types: extractive and
abstractive summarization. In this section of the report we experiment with extractive
summarization and showcase its practicality. We target news listed in the ”r/World-
News” subbreddit, a group dedicated to sharing major events from around the world,
excluding US-internal news.
Extractive summarization is often used for news articles, scientific papers, and other
documents that have a clear structure and contain factual information. Because these
subreddit posts mainly include only the news headline and the URL pointing to its
source, it marks thems as a prime candidate for our NLP application.
Extractive summarization involves selecting the most important sentences or phrases
from the original text to form a summary. To obtain our target text, we first fetch the
top post on ”r/WorldNews” via the API wrapper class (Appendix 4.1) then we use the
BeautifulSoup Python library to extract the news text at its source (Figure 54). All the
utility functions presented in this section of the report can be found in Appendix 4.5.
These functions are use both for extracting target corpus, as well as performing statistical
calculations and transformation on the text data to obtain a summarization.

Figure 54: Fetching the top post on ”r/WorldNes” then extracting the news body via
the BeautifulSoup library.

Page 58
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Extractive summarization techniques rely on statistical or machine learning models


to identify the most informative sentences or phrases from the source text. We make
use of word frequency, sentence position, and named entities, to score each sentence or
phrase and select the ones that best represent the main points of the text:
1 #Generate a frequency distribution based on previously acquired text
2 FreqTable = GenerateWordFrequencyDistribution(Text)
3 #Generate sentence values for each sentence in text back on frequency of work
,→ occurance.
4 sentScores, Sentences = CalculateSentenceImportance(Text, FreqTable)
5 #print(sentScores, Sentences)
6
7 #Calculate the average sentence importance
8 AverageValue = CalculateAverageSentenceImportance(sentScores)
9 print(f"Average sentence importance: {AverageValue}")
10 >> Average sentence importance: 83.36363636363636

Finally, we select the sentences that have an importance score greater than 1.5 times
over the average. The resulting summary can be seen in Figure 55.

Figure 55: Summarizing the target text.

Page 59
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

4 Appendix
4.1 Reddit API Wrapper Class
1 import requests
2 from pprint import pprint
3 import pandas as pd
4 import os
5 import configparser
6 from datetime import datetime
7
8 class RedditAPIHandler:
9 """
10 Wrapper for the Reddit API to simplify some of the calls, turning incoming
11 json to dataframes, and hiding authentication secrets for the Reddit app.
12 """
13 def __init__(self, path = '[Link]'):
14 """
15 Args:
16 path (String): Path to the .ini file to read the API authentication secrets.
17 Defaults to './[Link]'.
18 """
19 self._path = path # for debug purposes
20 self.__config = [Link]()
21 self.__config.read(self._path)
22
23 self.config_status = {
24 'date_modified': self.__config['DEFAULT']['datemodified'],
25 'grant_type': self.__config['DEFAULT']['granttype'],
26 'app_name': self.__config['[Link]']['app'],
27 'client_name': self.__config['[Link]']['username']
28 }
29
30 self.__headers = {'User-Agent': self.__config['[Link]']['app']}
31
32 [Link]()
33

34
35 def GenerateAuthToken(self, path = None):
36 """
37 Generate new authentication token for the API. The token expires every ~2 hours.
38
39 Args:
40 path (str, optional): Provide a new path for a .ini file containing the API
41 authentication secrets. Defaults to the .ini path provided on class
42 instantiation.
43 """
44 if(path):
45 self._path = path
46 self.__config.read(self._path)
47 auth = self.__app_auth()
48
49 # Setup login data

Page 60
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

50 data = {'grant_type': self.__config['DEFAULT']['granttype'],


51 'username': self.__config['[Link]']['username'],
52 'password': self.__config['[Link]']['secret']}
53 headers = {'User-Agent': self.__config['[Link]']['app']}
54

55 # Send request for an OAuth token


56 res = [Link]('[Link]
57 auth=auth, data=data, headers=headers)
58 if 'access_token' in [Link]():
59 TOKEN = [Link]()['access_token']
60 self.__headers = {**self.__headers, **{'Authorization':
,→ f"bearer {TOKEN}"}}
61
,→ print('Auth OK: New token available for {} seconds has been generated.'
62 .format([Link]()['expires_in']))
63 else:
64 print('Auth ERROR: ', [Link]()['error'])
65
66 def TestConn(self):
67 test = [Link]('[Link]
,→ headers=self.__headers)
68 if test.status_code == 200 and 'snoovatar_img' in [Link]():
69 return([Link]()['snoovatar_img'])
70 else:
71 return False
72
73 def __app_auth(self):
74 # Setup AUTH
75 CLIENT_ID = self.__config['[Link]']['client']
76 SECRET_TOKEN = self.__config['[Link]']['token']
77 return [Link](CLIENT_ID, SECRET_TOKEN)
78
79
80 def __df_from_response(self, res):
81 """
82 Args:
83 res (String/Json): Json response for [Link] API calls.
84
85 Returns:
86 [Link]: DataFrame format of the json response,
87 containing only relevant fields.
88 """
89 # initialize temp dataframe for batch of data in response
90 df = [Link]()
91
92 # loop through each post pulled from res and append to df
93 for post in [Link]()['data']['children']:
94 append_df = [Link]({
95 'author': post['data'].get('author'),
96 'subreddit': post['data'].get('subreddit'),
97 'title': post['data'].get('title'),
98 'selftext': post['data'].get('selftext'),

Page 61
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

99 'upvote_ratio': post['data'].get('upvote_ratio'),
100 'ups': post['data'].get('ups'),
101 'downs': post['data'].get('downs'),
102 'score': post['data'].get('score'),
103 'link_flair_text': post['data'].get('link_flair_text'),
104 'is_original_content': post['data'].get('is_original_content'),
105 'is_video': post['data'].get('is_video'),
106 'post_hint': post['data'].get('post_hint'),
107 'url': post['data'].get('url'),
108 'created_utc': post['data'].get('created_utc')),
109 'id': post['data'].get('id'),
110 'kind': post['kind'],
111 'total_awards_received':
,→ post['data'].get('total_awards_received'),
112 'num_comments': post['data'].get('num_comments'),
113 'num_crossposts': post['data'].get('num_crossposts'),
114 'num_reports': post['data'].get('num_reports'),
115 'domain': post['data'].get('domain'),
116 }, index=[0])
117 df = [Link]([df, append_df], ignore_index=True)
118
119 return df
120
121 def PullSubredditFeed(self, subreddit, sort_by = 'new', limit = 20):
122 """Fetch post data from a specified subreddit feed. Search for new
123 or popular threads.
124
125 Args:
126 subreddit (String): r/woosh The subreddit name, without the 'r/'.
127 sort_by (String): Enum, of 'new', 'hot', 'best', 'top', 'controversial'.
128 Defaults to 'new'.
129 sort_order (Boolean): True for ascending
130 limit (Integer): How many posts to return. Applies only when sorting posts
131 by 'new'. Defaults to 20.
132
133 Returns:
134 [Link]: DataFrame containing multiple reddit posts
135 belonging to a subreddit feed.
136 """
137

138 # initialize dataframe and parameters for pulling data in loop


139 data = [Link]()
140 requests_string =
,→ "[Link]
,→ = subreddit, sort_by = sort_by)
141

142 if(limit<=100):
143 params = {'limit': limit}
144 res = [Link](requests_string,
145 headers = self.__headers,
146 params = params)
147 # get dataframe from response

Page 62
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

148 data = self.__df_from_response(res)


149 print('Fetching all {} results.'.format([Link][0]))
150 return data
151 else:
152 if(limit % 100 ):
153 params = {'limit': limit % 100 }
154 else:
155 params = {'limit': 100 }
156
157 res = [Link](requests_string,
158 headers = self.__headers,
159 params = params)
160 # get last id for the query params
161 params['after'] = [Link]()['data']['after'] or
,→ [Link][len(data)-1]['kind'] + '_' +
,→ [Link][len(new_df)-1]['id']
162 data = self.__df_from_response(res)
163
164 print('Fetched {} results out of {}'.format([Link][0], limit))
165
166 # loop through n times for the remaining
167 for i in range((limit - params['limit']) // 100):
168 # make request
169 params['limit'] = 100
170
171 # params['count'] = [Link][0]
172 res = [Link](requests_string,
173 headers = self.__headers,
174 params = params)
175 # get last id for the query params
176 params['after'] = [Link]()['data']['after'] or
,→ [Link][len(data)-1]['kind'] + '_' +
,→ [Link][len(new_df)-1]['id']
177

178 # get dataframe from response


179 new_df = self.__df_from_response(res)
180 print(params['after'])
181 if(params['after'] == None):
182 print([Link][len(data)-1]['kind'] + '_' +
,→ [Link][len(new_df)-1]['id'])
183 # append new_df to data
184 data = [Link](new_df, ignore_index=True)
185 print('Fetched {} results out of {}'.format([Link][0],
,→ limit))
186 print('\r \r', end='', flush=True)
187

188 return data


189
190 def PullSubredditFeedRaw(self, subreddit, sort_by = 'new', limit = 20,
,→ after = None):
191 requests_string =
,→ "[Link]
,→ = subreddit, sort_by = sort_by)

Page 63
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

192 params = {'limit': limit, 'after': after}


193 res = [Link](requests_string,
194 headers = self.__headers,
195 params = params)
196 return [Link]()['data']
197
198 def FetchPostCommentsSimple(self, subreddit, post_id, limit = 20):
199 requests_string =
,→ "[Link]
,→ = subreddit, post_id = post_id)
200 params = {'limit': limit}
201 res = [Link](requests_string,
202 headers = self.__headers,
203 params = params)
204 comments_data = [data['data'] for data in
,→ [Link]()[1]['data']['children']]
205 comments_bodies = []
206 for comment_data in comments_data:
207 try:
208 comments_bodies.append(comment_data['body'])
209 except:
210 pass
211 return comments_bodies

4.2 Radial Plot Function


1 def plot_post_radar(first_post_df, second_post_df, first_label, second_label):
2 df = [Link]([first_post_df[['upvote_ratio', 'ups', 'num_comments',
,→ 'num_crossposts']], second_post_df[['upvote_ratio', 'ups',
,→ 'num_comments', 'num_crossposts']]], ignore_index=True)
3 df[['ups', 'num_comments', 'num_crossposts']] =
,→ [Link](df[['ups', 'num_comments', 'num_crossposts']])
4
5 # Set the chart style
6 [Link](style="whitegrid")
7

8 # Create a radar chart with multiple individuals


9 categories = list([Link])
10 N = len(categories)
11 angles = [n / float(N) * 2 * 3.141 for n in range(N)]
12 angles += angles[:1]
13 fig = [Link]()
14 ax = fig.add_subplot(111, polar=True)
15 ax.set_theta_offset(angles[0])
16 ax.set_theta_direction(-1)
17 [Link](angles[:-1], categories, size=20)
18 ax.set_rlabel_position(-45)
19 [Link]([0.2, 0.4, 0.6, 0.8, 1], [0.2, 0.4, 0.6, 0.8, 1], color="grey",
,→ size=15)
20 [Link](0, 1)
21
22 # Add the first individual's data to the chart

Page 64
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

23 values = [Link][0].[Link]().tolist()
24 values += values[:1]
25 [Link](angles, values, linewidth=1, linestyle='solid', label=first_label)
26 [Link](angles, values, 'b', alpha=0.1)
27

28 # Add the second individual's data to the chart


29 values = [Link][1].[Link]().tolist()
30 values += values[:1]
31 [Link](angles, values, linewidth=1, linestyle='solid', label=second_label)
32 [Link](angles, values, 'r', alpha=0.1)
33

34 # Adjust the padding between the labels and the plot


35 ax.set_xticklabels(categories, fontsize=14, color='black', ha='center',
,→ va='center', rotation=45, bbox=dict(pad=1.15, edgecolor='none',
,→ facecolor='none'))
36
37 # Add a legend and title to the chart
38 [Link](loc='upper right', bbox_to_anchor=(0.1, 0.1))
39 [Link]('Radar Chart of Reddit Posts', size=14)
40
41 # Show the chart
42 [Link]()

4.3 Facebook Social Network Graph Analysis Reports


4.3.1 Degree Report
Results:
Average Degree: 21.846

Page 65
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 56: Degree distributions.

4.3.2 Weighted Degree Report


Results:
Average Weighted Degree: 21.846

Figure 57: Weighted degree distributions.

Page 66
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

4.3.3 Graph Distance Report


Parameters:
Network Interpretation: directed

Results:
Diameter: 17
Radius: 0
Average Path length: 4.33774423847196

Figure 58: Graph distances distributions.

4.3.4 Modularity Report


Parameters:
Randomize: On
Use edge weights: On
Resolution: 1.0

Results:
Modularity: 0.835
Modularity with resolution: 0.835
Number of Communities: 16

Page 67
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

Figure 59: Communities size distribution by modularity class.

4.3.5 Statistical Inference Report


Results:
Description Length: 285789.193
Number of Communities: 146

Figure 60: Communities size distribution by statistical inference class.

4.4 Text Pre-Processing Utility Class


1 from [Link] import English
2 import nltk

Page 68
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

3 from [Link] import stopwords


4 from [Link] import WordNetLemmatizer
5 from [Link] import SnowballStemmer
6 from string import punctuation
7 from [Link] import wordnet as wn
8 from gensim import corpora
9 import gensim
10 import spacy
11 from wordcloud import WordCloud
12 import [Link] as plt
13
14 class TextPreprocess:
15
16 def __init__(self) -> None:
17
18 # [Link]('en_core_web_trf')
19 [Link] = English()
20 self.stop_words_en = [Link]
21 [Link] = [Link]('en_core_web_trf')
22
23
24 # Writing a function to apply different preprocessing to the text data.
25 def process_corpus(self, corpus, word_reduce_strategy=None,
,→ keep_stop_words=False, keep_punctuation=False, join_tokens=False,
,→ min_token_length=3, extra_stop_words=[]):
26 """
27 Tokenize a string and prepare it for text processing by removing stop words,
28 stemming the words, lemmatizing the words, and converting them to lowercase.
29

30 :param corpus: The raw text to be processed


31 :type corpus: string
32 :param word_reduce_strategy: Specify if the output tokens should
33 be lemmatized or stemmed
34 :type word_reduce_strategy: 'lemma' | 'stem'
35 :param keep_stop_words: Specify if any stop words should be kept
36 in the processed text; Default: False
37 :type keep_stop_words: bool
38 :param keep_punctuation: Specify if any punctuation should be kept
39 in the processed text; Default: False
40 :type keep_punctuation: bool
41 :param join_tokens: Specify if the resulting tokens should be joined
42 into one string or returned as a list of tokens; Default: False
43 :type join_tokens: bool
44 :param min_token_length: The minimum length of a string being returned
45 in the tokens list
46 :type join_tokens: int
47 :param extra_stop_words: Extra stop words to be omitted
48 :type extra_stop_words: str[]
49 """
50 # Tokenize the string into words
51 # words = [Link](corpus)
52 words = [Link]([Link]('’',"'")) # Unicode character
,→ intercepted

Page 69
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

53 in Reddit data
54 tokens = [word for word in words if not word.orth_.isspace()]
55
56 # Removing punctuation
57 if(not keep_punctuation):
58 tokens = [token for token in tokens if not token.is_punct]
59
60 match word_reduce_strategy:
61 case 'lemma':
62 # Lemmatize the words
63 tokens = [token.lemma_ for token in tokens]
64 tokens = [[Link]() for token in tokens]
65 case 'stem':
66 # Stem the words
67 stemmer = SnowballStemmer('english')
68 tokens = [token.lower_ for token in tokens]
69 tokens = [[Link](token) for token in tokens]
70 case other:
71 tokens = [token.lower_ for token in tokens]
72
73 # # Removing stop words
74 if(not keep_stop_words):
75 tokens = [token for token in tokens if token not in
,→ self.stop_words_en and token not in extra_stop_words and
,→ len(token) >= min_token_length]
76
77 if(join_tokens):
78 return ' '.join(tokens)
79 else:
80 return tokens
81
82
83 # Optimized for single use
84 @staticmethod
85 def clean_corpus(corpus, min_word_length=2, extra_stop_words=[]):
86 wn = [Link]()
87 stopwords = [Link]('english')
88 tokens = nltk.word_tokenize(corpus)
89 lower = [[Link]() for word in tokens]
90 no_stopwords = [word for word in lower if word not in stopwords and
,→ word not in extra_stop_words and len(word)>min_word_length]
91 no_alpha = [word for word in no_stopwords if [Link]()]
92 lemm_text = [[Link](word) for word in no_alpha]
93 return lemm_text
94
95

96 # More rudimentary functions


97 def tokenize(self, text):
98 """
99 Tokenizing texts
100 """
101 lda_tokens = []

Page 70
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

102 tokens = [Link](text)


103 for token in tokens:
104 if token.orth_.isspace():
105 continue
106 else:
107 lda_tokens.append(token.lower_)
108 return lda_tokens
109
110 def get_lemma(self, word):
111 """
112 Lemmatization
113 """
114 lemma = [Link](word)
115 if lemma is None:
116 return word
117 else:
118 return lemma
119
120 @staticmethod
121 def generate_wordcloud(words, label, background_color="black",
,→ max_words=100, max_font_size=50):
122 [Link](figsize=(12,8))
123 wc = WordCloud(background_color=background_color, max_words=max_words,
,→ max_font_size=max_font_size)
124 [Link](words)
125 [Link](label, fontsize=20)
126 [Link]([Link](colormap='Pastel2', random_state=17), alpha=0.98)
127 [Link]('off')
128

129 @staticmethod
130 def generate_wordcloud_from_frequencies(frequency_distribution, label,
,→ background_color="black", max_words=100, max_font_size=50):
131 [Link](figsize=(12,8))
132 wc = WordCloud(background_color=background_color, max_words=max_words,
,→ max_font_size=max_font_size)
133 wc.generate_from_frequencies(frequency_distribution)
134 [Link](label, fontsize=20)
135 [Link]([Link](colormap='Pastel2', random_state=17), alpha=0.98)
136 [Link]('off')

4.5 Extractive Summarization Utility Functions


1 def GetText(url):
2 site = [Link](url).text #Request html object from url
3 soup = BeautifulSoup(site, "[Link]") #Create BeautifulSoup object
4 text =""
5 for j in soup.find_all("p"): #Find all paragraph tags within the html page
6 text += j.get_text() #Get text from each paragraph tag in document and
,→ append to 'text' variable
7 return text
8
9

Page 71
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

10 def GenerateWordFrequencyDistribution(text):
11 stops = set([Link]("english"))
12 words = word_tokenize(text) #tokenize text by words
13 freqTable = dict()
14

15 for word in words: #Itterate through all words in text


16 word = [Link]() #Convert word to lowercase
17 if word not in stops: #Ignore stopwords
18 if word in freqTable:
19 freqTable[word] += 1 #Add one to the word frequency if it has
,→ already been added to before
20 else:
21 freqTable[word] = 1 #Set the word frequency to one if it has
,→ not already been added to before
22 return freqTable
23

24
25 def CalculateSentenceImportance (text, freqTable):
26 sents = sent_tokenize(text)
27 sentScores = dict()
28
29 for sent in sents:
30 for word, freq in [Link]():
31 if word in [Link]():
32 if sent in sentScores:
33 sentScores[sent] += freq #Add the frequency of a word
,→ occuring to the sentence score if it has been added to
,→ before
34 else:
35 sentScores[sent] = freq #Set score of a sentence to the
,→ score of the first word occuring in the sentence
36 return sentScores, sents
37

38
39 def CalculateAverageSentenceImportance (sentScores):
40 sumValues = 0
41 for sentence in sentScores:
42 sumValues += sentScores[sentence] #Calculate total sentence values
43

44 return (sumValues / len(sentScores)) #Return average sentence value


45
46
47 def PerformExtractiveSummarisation(sentences, sentScores, average):
48 summary = ""
49 for sent in sentences:
50 if (sentScores[sent] > (1.5 * average)): #Check if the sentence has a
,→ score greater than 1.5 times the average.
51 summary += sent + " "
52
53 return summary

Page 72
BIRMINGHAM CITY UNIVERSITY
SCHOOL OF COMPUTING AND DIGITAL TECHNOLOGY

References
Sponder, M. and G. Khan (2017). Digital analytics for marketing. Routledge.
Ganis, M. and A. Kohirkar (2015). Social media analytics: Techniques and insights for
extracting business value out of social media. IBM Press.
Bastian, M., S. Heymann, and M. Jacomy (2009). “Gephi: an open source software for
exploring and manipulating networks”. In: Proceedings of the international AAAI
conference on web and social media. Vol. 3. 1, pp. 361–362.
Leskovec, J. and J. Mcauley (2012). “Learning to discover social circles in ego networks”.
In: Advances in neural information processing systems 25.
Blondel, V. D., J.-L. Guillaume, R. Lambiotte, and E. Lefebvre (2008). “Fast unfolding
of communities in large networks”. In: Journal of statistical mechanics: theory and
experiment 2008.10, P10008.
Blei, D. M., A. Y. Ng, and M. I. Jordan (2003). “Latent dirichlet allocation”. In: Journal
of machine Learning research [Link], pp. 993–1022.
Hofmann, T. (1999). “Probabilistic latent semantic indexing”. In: Proceedings of the
22nd annual international ACM SIGIR conference on Research and development in
information retrieval, pp. 50–57.
Xiao, J. and Z. Zhou (2020). “Research progress of RNN language model”. In: 2020
IEEE International Conference on Artificial Intelligence and Computer Applications
(ICAICA). IEEE, pp. 1285–1288.
Tarwani, K. M. and S. Edem (2017). “Survey on recurrent neural network in natural
language processing”. In: Int. J. Eng. Trends Technol 48.6, pp. 301–304.
Jelodar, H., Y. Wang, R. Orji, and S. Huang (2020). “Deep sentiment classification and
topic discovery on novel coronavirus or COVID-19 online discussions: NLP using
LSTM recurrent neural network approach”. In: IEEE Journal of Biomedical and
Health Informatics 24.10, pp. 2733–2742.

Page 73

You might also like