Skip to content

AI Integration · Engineering

Sentiment Analysis of Twitter Data Using Naive Bayes Algorithm

Build a sentiment analysis pipeline with text preprocessing, CountVectorizer token counts, and Multinomial Naive Bayes classification on Twitter data.

Anurag Verma

Anurag Verma

6 min read

Sentiment Analysis of Twitter Data Using Naive Bayes Algorithm

Sponsored

Share

Sentiment analysis on Twitter data is a classic bag-of-words classification problem: turn short, noisy tweet text into numeric features, then let a model learn which words push a tweet toward positive or negative sentiment. This walkthrough builds that pipeline end to end with a Multinomial Naive Bayes classifier, from raw tweets through cleaning, vectorizing, training, and a closer look at what the accuracy score is actually telling you.

1. Importing the necessary libraries:

import pandas as pd
import re
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import CountVectorizer

import tensorflow as tf
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB

from tensorflow import keras
import matplotlib.pyplot as plt

The pipeline needs five kinds of tools: pandas to load and hold the tweet data, re and NLTK’s stopword list for text cleaning, scikit-learn’s CountVectorizer to turn cleaned text into a token-count matrix, train_test_split to hold out a fair evaluation set, and MultinomialNB as the classifier itself. TensorFlow and matplotlib are imported for later plotting but aren’t required for the core Naive Bayes pipeline.

2. Reading the dataset:

train = pd.read_csv('/kaggle/input/22fall-micro-course-4-w2v-d2v/train.csv')
test = pd.read_csv('/kaggle/input/22fall-micro-course-4-w2v-d2v/test.csv')

The code reads the train and test datasets using the pandas library and stores them in the variables ‘train’ and ‘test’.

train.head()
idlabeltweet
0198130today’s mood 😍 #fashion #outfitoftheday #…
1156070todays #playlist #spotify
2140690best #essentialoils for #weightloss!! #altwa…
3191180i believe luis worked at @user ~ ~ mlc🌴
4128900use the power of your mind to #heal your body!…

3. Preprocessing the data:

import re
import nltk
nltk.download('stopwords')
from nltk.corpus import stopwords

def clean_tweet(tweet):
    # Remove HTML tags and special characters
    tweet = re.sub(r'<.*?>|&[a-z]+;', '', tweet)

    # Remove URLs and hashtags
    tweet = re.sub(r'https?://\S+|#\S+', '', tweet)

    # Tokenize the text
    tokens = nltk.word_tokenize(tweet)

    # Remove stop words and words that are not in the desired language
    tokens = [token for token in tokens if token.lower() not in stopwords.words('english')]

    # Remove special characters and punctuation
    tokens = [re.sub(r'[^\w\s]', '', token) for token in tokens]

    # Convert words to lowercase
    tokens = [token.lower() for token in tokens]

    tokens = [token for token in tokens if len(token)>1]

    return ' '.join(tokens)

# # Example usage
# tweet = "I had a terrible experience at the restaurant last night. The service was slow and the food was overcooked."
# clean_tweet(tweet)
# Output: ['terrible', 'experience', 'restaurant', 'last', 'night', 'service', 'slow', 'food', 'overcooked']

[nltk_data] Downloading package stopwords to /usr/share/nltk_data… [nltk_data] Package stopwords is already up-to-date!

The ‘clean_tweet’ function is used to preprocess the data. It removes HTML tags, special characters, URLs, hashtags and stop words. The preprocessed tweets are then stored back in the ‘train’ dataset using the ‘apply’ function.

4. Converting the tweets to a matrix of token counts:

The vectorizer object from CountVectorizer is used to convert the list of tweets into a matrix of token counts. A token count is a way of representing the frequency of each word in the tweet. The vectorizer object is fit using the fit_transform method, which performs two operations at once: it fits the vectorizer to the data, and then transforms the data into the token count matrix. The result is stored in a variable called X. The scikit-learn CountVectorizer documentation covers the additional parameters (max_features, ngram_range, min_df) worth tuning once the baseline pipeline works.

# Create a list of the tweets
tweets = train['tweet'].tolist()

# Create a list of the labels
labels = train['label'].tolist()

# Convert the tweets to a matrix of token counts
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(tweets)

5. Split the data into training and testing sets:

To evaluate the accuracy of the model, we need to split the data into training and testing sets. This is done using the train_test_split function from the sklearn.model_selection module. The train_test_split function takes four arguments: the features, the labels, the size of the test set, and the random state (which is used to ensure that the same split is produced every time the code is run). The result of train_test_split is four variables: X_train, X_test, y_train, and y_test. X_train and y_train are the features and labels for the training set, and X_test and y_test are the features and labels for the testing set.

X_train, X_test, y_train, y_test = train_test_split(X, labels, test_size=0.2)

6. Train the model:

A Multinomial Naive Bayes model is created and fit to the training data using the fit method. The model is stored in a variable called model.

model = MultinomialNB()
model.fit(X_train, y_train)

MultinomialNB()

7. Evaluate the model:

The accuracy of the model is evaluated using the score method on the model object and passing in the testing data. The accuracy is stored in a variable called accuracy and printed to the console.

accuracy = model.score(X_test, y_test)
print("Accuracy:", accuracy)

Accuracy: 0.9382874775006428

8. A closer look: what the accuracy score hides

A single accuracy number like 93.8% is not enough to judge a sentiment classifier, especially on Twitter data where one label usually dominates. If 93% of tweets in this dataset carry the majority label, a model that predicts that label every single time would score close to the same number without having learned anything. This is worth checking directly with a confusion matrix and a per-class report rather than trusting the headline score, an approach covered in more depth in our guide to classification and regression evaluation metrics.

from sklearn.metrics import confusion_matrix, classification_report

y_pred = model.predict(X_test)

print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))

The classification report breaks the single accuracy number into precision, recall, and F1 for each class. If recall on the minority class (the tweets you actually care about catching) is low, the model is leaning on the majority class to inflate its score, and no amount of retraining on the same imbalanced split fixes that. Two practical next steps once you see that pattern: pass class_weight-style resampling to balance the training data before fitting, or try alpha smoothing on MultinomialNB(alpha=...) to see how much the decision boundary shifts.

9. Where this fits among classification approaches

Multinomial Naive Bayes is a reasonable first model for text because it is fast, works well with sparse token counts, and needs no tuning to get a usable baseline. It is not the only bag-of-words option worth trying on the same features. Our hands-on KNN classification walkthrough and support vector machine classification guide both work from the same CountVectorizer-style feature matrix and are worth benchmarking against Naive Bayes before picking a final model for a production pipeline. If the goal is to squeeze more accuracy out of this exact pipeline rather than swap algorithms, our roundup of techniques for improving machine learning models covers feature engineering and tuning options that apply directly here.

End!

Frequently asked questions

Why Multinomial Naive Bayes rather than Bernoulli or Gaussian?
Because the features are counts. Multinomial models how often each token appears, which is exactly what CountVectorizer produces. Bernoulli treats each feature as present or absent, which discards frequency and suits short binary-feature text. Gaussian assumes continuous, normally distributed features and does not fit token counts at all. If you switch the vectorizer to TF-IDF, Multinomial still works, since the values stay non-negative.
Is 93.8% accuracy actually good here?
Not on its own, and this is the part worth slowing down on. Tweet sentiment datasets are usually imbalanced, and if roughly 93% of the rows carry one label, a model that always predicts that label scores about the same. Look at the confusion matrix, then precision, recall and F1 on the minority class. If recall on the class you care about is low, the headline accuracy is telling you about the class distribution, not the model.
Does removing stopwords and hashtags always help?
For a bag-of-words model like this, usually yes, because stopwords appear everywhere and carry almost no discriminating signal. Hashtags are a judgement call: they are noisy, but on Twitter they often carry the sentiment outright, so stripping them can throw away your best feature. It is worth running the pipeline both ways and comparing rather than assuming.
What is the vectorizer leak, and how do I avoid it?
Calling fit_transform on the whole dataset before train_test_split means the vocabulary was built from rows that end up in your test set. Your test score then reflects a model that already saw the test vocabulary, which inflates it. The fix is to split first, call fit_transform on the training set only, and transform the test set with the vectorizer already fitted.
Where does this approach stop working?
When meaning depends on word order or context. Bag-of-words cannot tell "not good" from "good not", and it has no representation for sarcasm, negation scope, or the same word meaning different things in different sentences, all of which are common on Twitter. Character n-grams and bigrams push it a bit further. Past that you need a model that reads sequences, which is where transformer-based classifiers earn their extra cost.

Sponsored

Sponsored

Discussion

Join the conversation.

Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.

Sponsored