From ae1eda8c064d037d5812abfaa6ed6b66c33c4246 Mon Sep 17 00:00:00 2001 From: arpitavbhosale Date: Sat, 30 Aug 2025 22:40:01 +0530 Subject: [PATCH] Added WhatsApp Sentiment Analysis --- Whatsapp Chats Sentiment Analysis/README.md | 55 ++++ .../Screenshot 2025-08-24 195928.png | Bin 0 -> 56757 bytes .../Whatsapp Chats Sentiment Analysis.ipynb | 251 ++++++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 Whatsapp Chats Sentiment Analysis/README.md create mode 100644 Whatsapp Chats Sentiment Analysis/Screenshot 2025-08-24 195928.png create mode 100644 Whatsapp Chats Sentiment Analysis/Whatsapp Chats Sentiment Analysis.ipynb diff --git a/Whatsapp Chats Sentiment Analysis/README.md b/Whatsapp Chats Sentiment Analysis/README.md new file mode 100644 index 00000000..fc34ee8c --- /dev/null +++ b/Whatsapp Chats Sentiment Analysis/README.md @@ -0,0 +1,55 @@ +# WhatsApp Chats Sentiment Analysis + +## Overview +This project performs sentiment analysis on WhatsApp chat data using Natural Language Processing (NLP) techniques. The implementation extracts chat messages from a WhatsApp export file, processes the text data, and analyzes sentiment patterns using VADER (Valence Aware Dictionary and sentiment Reasoner). + +## Features +- WhatsApp chat file parsing and message extraction +- Multiline message handling +- Sentiment analysis using NLTK's VADER +- Visualization of sentiment distribution (Positive, Negative, Neutral) + + +## Technologies Used +- Python 3 +- Pandas & NumPy for data manipulation +- NLTK for sentiment analysis +- Matplotlib & Seaborn for visualization +- Emoji library for special character handling +- Regular expressions for text parsing + +## Installation +1. Clone this repository +2. Install required packages: + + +## Usage +1. Export your WhatsApp chat as a .txt file (without media) +2. Place the file in the project directory +3. Update the file path in the notebook: +```python +conversation = r"your_whatsapp_chat.txt" +``` +4. Run the Jupyter notebook cells sequentially + +## Code Structure +1. **Data Extraction**: Parses WhatsApp chat format and extracts messages with metadata +2. **Data Cleaning**: Handles multiline messages and missing values +3. **Sentiment Analysis**: Uses VADER to compute positive, negative, and neutral scores +4. **Visualization**: Generates bar charts showing sentiment distribution + +## Results +The analysis provides: +- Average sentiment scores for the conversation +- Visual representation of sentiment distribution +- Message-level sentiment scoring + +## Note +- The implementation handles WhatsApp's specific date-time format and message structure +- Supports both 12-hour time formats +- Properly processes messages with emojis and special characters +- Maintains message context across line breaks + +## Privacy +This tool processes chat data locally. No data is sent to external servers, ensuring your conversations remain private. + diff --git a/Whatsapp Chats Sentiment Analysis/Screenshot 2025-08-24 195928.png b/Whatsapp Chats Sentiment Analysis/Screenshot 2025-08-24 195928.png new file mode 100644 index 0000000000000000000000000000000000000000..c74faf88fd8b194bd11fa2d3707f7d199e2c75bd GIT binary patch literal 56757 zcmeFac{rBq+cvCP4NKE%l}Lk1lN1exQfO34Nydx~%1nbPV}nMdB#MxE3QML8H>FTX zWM5Lx9Q!Fn|{Bi=X<{G{k}im?R~cQ`)q5FxbN#auk$>P{n+>YINUmNc;8G; zUQP}Uj+wIi_Z;Qmm@Lh~F|m2t6nw>^^XPT_Z^DJ6`*v}}d|1?sKm4J$^UzKXjySjJ zLnkKV&wrlXf9wJW$E;WM?}S{la9s|LnggI+_`}Kd5$P-ZNua zPvexn+BDZmPJjP>z@Jqj?cNyGi}d3Ketv1uNb56QpsmNJicJe_@2>`EWk5k3iHp1 zm>3ls8=HPy5c{L^*5(_yrLm9U3H7VTKD*>_t8?zyr#murS-}-Ue~kZyqm#VIzyB~V zlrd@SFPgljNlzI2wCA9c|JY|7td&{v{8MkKn@?H1t-Mj7PrfO_# zY|TCT*URHa4u>h4CTocK9Kt_y=FF+j|7w|bI=Q;6ygby&>5q`m(03=}-so<+Cf;VX zeTmyA)8vVlF9ik$&Re{AaqU^eDX=8UpM1-t%tg1?s@`42mZo9b580GSRSZJSPn7DTB+7|xN_R!E(qT=EQ z-RUm4xx>DG{n{t#fMxLY^=)ZsIcjBsd{8~^ zO{1?>f>nohVnV{3HcO+<&o5cYj)UTdyhUUU3=Hbiv|eL16BCuE&R$aLCuwWW>M;-% z6T?H;@Ba7Se~Vam9(P?Ret7el_l2)-&3*9j;hLzj$f_!J$)9id*9r@(%E(;L=woyk zG!+KaXMUM`p}+G47Z;a7;gg+f*R501*4DPZpNDUCYr3~@$-Na^^XD6m3=O1qs>sKW7Y#S(Q&lcuboFsC+Kde73o z?iMtb;IElgMzPJE^{=pxXPB*$y?yIe(dU;}MbCZNGjpMs9L~$6lcixRd?jsFwpsot zTxgaaP!cA8Lwfk$$2LpfZB~Z|`#LHvPnv4}_01fUx(r6aPrIs{;I{7Z@9X(`U%us=mrgmh64#6=qUKFUT=}H68*<&mv)*G*=)bx?v+AD{ zS0yAQg8eEK6cvMh{yeX&s+!l(6soE#Z0Ztf^- zG`jj2t+dke@>Q92d{}inm>8?ti~F*(vn4M63_fu1AkJo8vLpuw4-W;+{=WlPWfW1PXRt=8OH=wo)%(*ofh*ctEpLx<#s7(RJ?dG z#j>p`w^{8MiI}R6+b-bcF_6eO>C&E3C5M1YOqmwC2bP0H?w@0 z3xvE5ii(O>rC*G1X>AoX{j{a#eBOGN<7mfdv(Csj9i<~jIxpuPcM47Q^73lU7q2Vb zT(CW8c(6}!)25?%c;$`KqZjSljC4vWGI~X8&c2%IEo$V6$e^sQ9@$rGE3c)cRj)O& zTS-;bsIR?dt&q^j$NH4)lM09fKP=O*Zpu13!eJ$qQHQYA^1dBaR(^2US0bdTscH81 z4+ts87w|Z~<>2VFI*J#xsfhMc2Li#w>%LrXpIilP6E=#;KLo*2XN}W*MH6atsMoG3>DN zsZ&AN3}v;oLh+SPgM(MNF4!#d>c(u<0|#znV_G1n`fj}-gWYud=FKBEHmMse`i^Fs zYYKUJ`}kP={3?q~9El(-I)G|%yCf`mX?MCKffVg z7C+jor=+73+um-vckf;UBoR7;GA=v;H*en5ZdBLO@k&_$ z?5M1)8r%9XN`tsP?G;!)3^t65lB7#bPfuV@wBRRYXInds}Adq~o$KP-$G z+OfDx^Y8B$C@U$22Lx!*lSRoWJ3GU zLqJgkp}N6-z2@^m>o%|AqM`t;G|T4ZF#qxozS}IAWvZ>ItuZBmd!2?{H&a(CE+xrhZJy-eUud;vNsw&xc zx48J|123!Iw7!KUK9b{(hknZU?glrTU8R;@Cx9UD^%g2wO2JF6JSe0P#^c5#Zf2mq~ zN~51`7C~w+%cjC*izUqW11;0xLQoCyyHsK<*2lS8ZdJ+N6W8iGRQ2@2?kwIGnq23g zVU{rvYT5oN0$X80_D$@p(~0V??{Wehe0+SEKl6lJDV1XbJh*>9=*yRrCbj9uIx+@h zW?}ctU41wlpmO8Jjl7$5ZyJmF0B-|H2JPdsSIWJ!_&%NsPBN}rNl7Uf!6`f>WG|uu zUDZH$BeS>KtTj1vI2u_>)TBy;e`veoNcs}{9tD%C1X-L;ApZ)$q{NHeXRsL#PMx)= z@a_H8-z?L(=FSaz^JYKYBsOme;$(ttpXp*LJ2kAM=Ud&QNNQ7N^6D;%Dom(rX)#>9 z-TKHvansV?w(3Nq3L*L@3WTY$X_xEl`?Lrb5$eF* zh+h1u89!4r-vgCB^y7nSAt=+u2WZE-qD+}P`Xr0X3H5&*iFP3)rWbF^QiqMn# zLm_C1+rF7IXI3Q|d5M|TKE*8?6!=L>m^a;}ZHjY*DvbYTa;0%gQ&Zvhe4oVAdGp&U z<8#BeXe%n7(!76p$BX`_b@B+g<*_=k*vfBpH?cEtf{Jx;ipZ?9->~jzy~=!SeT4WjbbxnSRoLyp8rB z1^Sj!fm4f%nws?SV0nXH85tQdUrd_a1QdRL%c5u1ZGe3Wh&F^75aYGV&rPe0J9VSC zqb?R}L#Y6Xf9?A9W&fObC4v>_?=dnqE)|STr_@M)P1)Aoo>5g@&3sf~C$szd3durJ zQz|zoRA@U6TQIsmY^#dbbDOnfdpM$qvWiM1gQ11umoN>=;nL0LQvgD!X2IiSe)los zjUB}6(OLW8Cn<<}Nf)7QxM7qE zN{Wj7_umGi&@!sey2iWlq!U6vJv-z�-IL+cXgFo>dyZd-LW9q9}p&3*Y6m?3rb( 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

E{p-AIFHN4G8P=tJ`+ZlNv}#qh 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%0Af`qtJ? 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 E2P5T$MF0Q* literal 0 HcmV?d00001 diff --git a/Whatsapp Chats Sentiment Analysis/Whatsapp Chats Sentiment Analysis.ipynb b/Whatsapp Chats Sentiment Analysis/Whatsapp Chats Sentiment Analysis.ipynb new file mode 100644 index 00000000..bb8c5086 --- /dev/null +++ b/Whatsapp Chats Sentiment Analysis/Whatsapp Chats Sentiment Analysis.ipynb @@ -0,0 +1,251 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "571ba0f0-de49-4f1d-b0c5-2627d9575bc1", + "metadata": {}, + "source": [ + "**Implementation**" + ] + }, + { + "cell_type": "markdown", + "id": "4a14af81-8abc-41d1-8253-a0d4cebf8025", + "metadata": {}, + "source": [ + "Importing the required libraries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91a3c082-7476-4b12-baf5-6172999ef0af", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install emoji\n", + "!pip install wordcloud\n", + "import re\n", + "import pandas as pd\n", + "import numpy as np\n", + "import emoji\n", + "from collections import Counter\n", + "import matplotlib.pyplot as plt\n", + "from PIL import Image\n", + "from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator\n", + "import nltk\n", + "from nltk.sentiment.vader import SentimentIntensityAnalyzer\n", + "import seaborn as sns\n" + ] + }, + { + "cell_type": "markdown", + "id": "cfd7181c-20b3-4605-89cc-67a05d3e1bd5", + "metadata": {}, + "source": [ + "Define the File Path & Open and Read the File" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3b74922-04f6-4526-a9bf-049d8b5a3845", + "metadata": {}, + "outputs": [], + "source": [ + "conversation = r\"whatspp group chat txt file.txt\"\n", + "\n", + "with open(conversation, \"r\", encoding=\"utf-8\") as file:\n", + " lines = file.readlines()\n", + "\n", + "print(f\"Total lines in chat file: {len(lines)}\")\n", + "print(\"\\nFirst 10 lines from the file:\")\n", + "for i in range(min(10, len(lines))):\n", + " print(lines[i].strip())" + ] + }, + { + "cell_type": "markdown", + "id": "5de5da7c-b79c-40a0-b7db-18f361398e47", + "metadata": {}, + "source": [ + "Identification of whether a line from a WhatsApp chat file starts with a timestamp." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28df0be9-e25c-479a-9785-a60a05411833", + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "\n", + "def date_time(s):\n", + " pattern = r'^(\\d{1,2})/(\\d{1,2})/(\\d{2,4}), (\\d{1,2}):(\\d{2}) ?(AM|PM|am|pm)? -'\n", + " return bool(re.match(pattern, s))\n", + "\n", + "# Test on first 10 lines from the chat file\n", + "for line in lines[:10]:\n", + " print(f\"{line.strip()} → {date_time(line)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9bf78892-9c78-4855-8c7a-7f077109439b", + "metadata": {}, + "outputs": [], + "source": [ + "def getMessage(line):\n", + " if \" - \" not in line:\n", + " return None, None, None, None # Skip invalid lines\n", + "\n", + " splitline = line.split(\" - \", 1)\n", + " datetime_part = splitline[0]\n", + " \n", + " try:\n", + " date, time = datetime_part.split(\", \", 1)\n", + " except ValueError:\n", + " return None, None, None, None # Skip invalid lines\n", + " \n", + " message_part = splitline[1]\n", + " if \": \" in message_part:\n", + " author, message = message_part.split(\": \", 1)\n", + " else:\n", + " author, message = None, message_part # No contact name found\n", + " \n", + " return date, time, author, message" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a12a35bf-1127-4c14-8598-668cc6e462d8", + "metadata": {}, + "outputs": [], + "source": [ + "for line in lines[:10]: \n", + " print(getMessage(line))" + ] + }, + { + "cell_type": "markdown", + "id": "3cd59572-ffe0-46db-98fb-7636f66a7f44", + "metadata": {}, + "source": [ + "Extracts structured message data from a WhatsApp chat file and stores it in a list. It correctly handles multiline messages, ensuring they are grouped with their respective timestamps and authors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42224c23-b168-4c6e-9576-657eb09f2b62", + "metadata": {}, + "outputs": [], + "source": [ + "data = []\n", + "messageBuffer = []\n", + "date, time, author = None, None, None\n", + "\n", + "for line in lines:\n", + " line = line.strip()\n", + " if not line:\n", + " continue # Skip empty lines\n", + "\n", + " if date_time(line): # If it's a new message\n", + " if messageBuffer:\n", + " data.append([date, time, author, ' '.join(messageBuffer)])\n", + " messageBuffer.clear()\n", + " date, time, author, message = getMessage(line)\n", + " messageBuffer.append(message)\n", + " else:\n", + " messageBuffer.append(line) # Append multiline messages\n", + "\n", + "if messageBuffer:\n", + " data.append([date, time, author, ' '.join(messageBuffer)])\n", + "\n", + "print(f\"Total messages extracted: {len(data)}\")\n", + "print(data[:5]) # Show first 5 extracted messages\n" + ] + }, + { + "cell_type": "markdown", + "id": "9d029402-54c2-477f-bda8-976a543413a3", + "metadata": {}, + "source": [ + "Sentiment of WhatsApp chat messages using NLTK's VADER Sentiment Analysis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "684780ea-a33c-4905-b5ca-769fa7bc427b", + "metadata": {}, + "outputs": [], + "source": [ + "#Convert Extracted Data into a Pandas DataFrame\n", + "df = pd.DataFrame(data, columns=[\"Date\", \"Time\", \"Contact\", \"Message\"])\n", + "\n", + "#Ensure Data is Clean\n", + "if df.empty:\n", + " print(\"No messages extracted. Fix chat parsing first.\")\n", + "else:\n", + " df['Date'] = pd.to_datetime(df['Date'])\n", + " df.dropna(inplace=True)\n", + "\n", + "\n", + "# Initialize Sentiment Analyzer\n", + "sentiments = SentimentIntensityAnalyzer()\n", + "\n", + "# Apply Sentiment Analysis\n", + "df[\"Positive\"] = df[\"Message\"].astype(str).apply(lambda x: sentiments.polarity_scores(x)[\"pos\"])\n", + "df[\"Negative\"] = df[\"Message\"].astype(str).apply(lambda x: sentiments.polarity_scores(x)[\"neg\"])\n", + "df[\"Neutral\"] = df[\"Message\"].astype(str).apply(lambda x: sentiments.polarity_scores(x)[\"neu\"])\n", + "\n", + "# Display first 5 messages\n", + "pd.set_option('display.width', 200) # Adjust width for better formatting\n", + "print(df.head(25).to_string(index=False))\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "619059af-5f11-4d21-a122-dc13ef1381bc", + "metadata": {}, + "outputs": [], + "source": [ + "# Sentiment Visualization (Positive, Neutral, Negative Messages)\n", + "plt.figure(figsize=(10, 5))\n", + "sentiment_counts = df[[\"Positive\", \"Negative\", \"Neutral\"]].mean()\n", + "sentiment_counts.plot(kind=\"bar\", color=[\"green\", \"red\", \"blue\"])\n", + "plt.title(\"Sentiment Analysis of Chat Messages\")\n", + "plt.ylabel(\"Average Sentiment Score\")\n", + "plt.xticks(rotation=0)\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:base] *", + "language": "python", + "name": "conda-base-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}