Sentiment-Analysis/PWU-MSIT-v1
0
1#pip install nltk textblob vaderSentiment transformers2#pip install gradio3#pip install torch transformers scikit-learn joblib nltk4import pandas as pd5import numpy as np6import matplotlib.pyplot as plt7import seaborn as sns8import nltk9from nltk.sentiment import SentimentIntensityAnalyzer10from textblob import TextBlob11from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer as VaderAnalyzer12from transformers import pipeline13nltk.download('vader_lexicon')14import gradio as gr15import pandas as pd16import matplotlib.pyplot as plt17import numpy as np18from nltk.sentiment.vader import SentimentIntensityAnalyzer19from sklearn.linear_model import LogisticRegression20from sklearn.feature_extraction.text import TfidfVectorizer21from transformers import pipeline22import joblib23import nltk24import re25import logging26import seaborn as sns27import tempfile28import os29from datetime import datetime30 31# Configure logging32logging.basicConfig(level=logging.INFO)33nltk.download('vader_lexicon', quiet=True)34 35# Initialize components and results file36RESULTS_FILE = "sentiment_analysis_results.csv"37try:38 if not os.path.exists(RESULTS_FILE):39 pd.DataFrame(columns=['timestamp', 'text', 'method', 'sentiment', 'compound', 'scores']).to_csv(RESULTS_FILE, index=False)40except Exception as e:41 logging.error(f"Could not initialize results file: {str(e)}")42 43# Initialize models44try:45 vader = SentimentIntensityAnalyzer()46 ml_model = joblib.load('logreg_model.joblib')47 tfidf = joblib.load('tfidf_vectorizer.joblib')48except (FileNotFoundError, Exception) as e:49 logging.warning(f"Model loading failed: {str(e)} - Training new models...")50 from sklearn.datasets import fetch_20newsgroups51 from sklearn.pipeline import make_pipeline52 53 newsgroups = fetch_20newsgroups(subset='train',54 categories=['alt.atheism', 'soc.religion.christian'])55 tfidf = TfidfVectorizer(max_features=1000, stop_words='english')56 ml_model = make_pipeline(57 tfidf,58 LogisticRegression(max_iter=1000)59 ).fit(newsgroups.data, newsgroups.target)60 61 joblib.dump(ml_model, 'logreg_model.joblib')62 joblib.dump(tfidf, 'tfidf_vectorizer.joblib')63 64try:65 transformer_model = pipeline(66 "sentiment-analysis",67 model="distilbert-base-uncased-finetuned-sst-2-english"68 )69except Exception as e:70 logging.error(f"Transformer model failed to load: {str(e)}")71 transformer_model = None72 73def clean_text(text):74 return re.sub(r'[^a-zA-Z\s]', '', str(text)).lower().strip()75 76def get_sentiment_emoji(sentiment):77 emoji_map = {78 'positive': '๐',79 'negative': '๐ ',80 'neutral': '๐',81 'error': 'โ'82 }83 return emoji_map.get(sentiment.lower(), 'โ')84 85def save_to_results(timestamp, text, method, result):86 try:87 new_row = {88 'timestamp': timestamp,89 'text': text,90 'method': method,91 'sentiment': f"{get_sentiment_emoji(result['sentiment'])} {result['sentiment'].upper()}",92 'compound': result['compound'],93 'scores': str(result['scores'])94 }95 96 # Read existing data97 try:98 df = pd.read_csv(RESULTS_FILE)99 except:100 df = pd.DataFrame(columns=['timestamp', 'text', 'method', 'sentiment', 'compound', 'scores'])101 102 # Append new result103 df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)104 df.to_csv(RESULTS_FILE, index=False)105 106 except Exception as e:107 logging.error(f"Failed to save results: {str(e)}")108 109def analyze_vader(text):110 try:111 scores = vader.polarity_scores(str(text))112 result = {113 'sentiment': 'positive' if scores['compound'] >= 0.05 else114 'negative' if scores['compound'] <= -0.05 else 'neutral',115 'compound': scores['compound'],116 'scores': scores117 }118 save_to_results(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), text, "VADER", result)119 return result120 except Exception as e:121 logging.error(f"VADER analysis failed: {str(e)}")122 return {'sentiment': 'error', 'compound': 0, 'scores': {}}123 124def analyze_ml(text):125 try:126 cleaned = clean_text(text)127 pred = ml_model.predict([cleaned])[0]128 result = {129 'sentiment': 'positive' if pred == 1 else 'negative',130 'compound': 0.8 if pred == 1 else -0.8,131 'scores': {'pos': pred, 'neg': 1-pred}132 }133 save_to_results(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), text, "Logistic Regression", result)134 return result135 except Exception as e:136 logging.error(f"ML analysis failed: {str(e)}")137 return {'sentiment': 'error', 'compound': 0, 'scores': {}}138 139def analyze_transformer(text):140 try:141 if not transformer_model:142 return {'sentiment': 'model_unavailable', 'compound': 0, 'scores': {}}143 result = transformer_model(str(text)[:512])[0]144 result = {145 'sentiment': result['label'].lower(),146 'compound': result['score'] if result['label'] == 'POSITIVE' else -result['score'],147 'scores': {'confidence': result['score']}148 }149 save_to_results(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), text, "Transformer", result)150 return result151 except Exception as e:152 logging.error(f"Transformer analysis failed: {str(e)}")153 return {'sentiment': 'error', 'compound': 0, 'scores': {}}154 155def create_visualization(df, method):156 fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18, 5))157 158 # Ensure required columns exist159 if 'sentiment' not in df.columns:160 df['sentiment'] = 'neutral'161 if 'compound' not in df.columns:162 df['compound'] = 0.0163 164 # Clean sentiment labels165 df['sentiment_cleaned'] = df['sentiment'].apply(166 lambda x: x.split()[1].lower() if ' ' in str(x) else str(x).lower()167 )168 169 # Replace invalid sentiments with neutral170 valid_sentiments = ['positive', 'negative', 'neutral']171 df['sentiment_cleaned'] = df['sentiment_cleaned'].apply(172 lambda x: x if x in valid_sentiments else 'neutral'173 )174 175 # Define colors176 colors = {'positive': '#4CAF50', 'negative': '#F44336', 'neutral': '#2196F3'}177 178 # Plot 1: Sentiment distribution179 sentiment_counts = df['sentiment_cleaned'].value_counts()180 sentiment_counts.plot(181 kind='bar',182 ax=ax1,183 color=[colors.get(sent, '#999999') for sent in sentiment_counts.index]184 )185 186 # Add emojis to bars187 for i, (label, _) in enumerate(sentiment_counts.items()):188 emoji = get_sentiment_emoji(label)189 ax1.text(i, sentiment_counts.iloc[i] + 0.5, emoji, ha='center', va='bottom', fontsize=14)190 191 ax1.set_title('Sentiment Distribution')192 ax1.set_ylabel('Count')193 194 # Plot 2: Compound score distribution195 df['compound'].plot(kind='hist', bins=20, ax=ax2, color='#9C27B0')196 ax2.set_title('Compound Score Distribution')197 ax2.set_xlabel('Compound Score')198 ax2.set_ylabel('Frequency')199 200 # Plot 3: Dot plot201 sns.stripplot(202 x='sentiment_cleaned',203 y='compound',204 data=df,205 ax=ax3,206 palette=colors,207 jitter=True,208 size=6209 )210 ax3.set_title('Sentiment Scores Distribution')211 ax3.axhline(0.05, color='gray', linestyle='--')212 ax3.axhline(-0.05, color='gray', linestyle='--')213 214 plt.suptitle(f"Sentiment Analysis Results ({method})", y=1.05)215 plt.tight_layout()216 return fig217 218def process_csv(csv_file, method):219 try:220 df = pd.read_csv(csv_file.name)221 222 if 'text' not in df.columns:223 raise gr.Error("CSV must contain a 'text' column")224 225 method_map = {226 "VADER": analyze_vader,227 "Logistic Regression": analyze_ml,228 "Transformer": analyze_transformer229 }230 231 if method not in method_map:232 raise gr.Error("Invalid analysis method selected")233 234 analysis_results = df['text'].apply(method_map[method])235 df = pd.concat([df, pd.json_normalize(analysis_results)], axis=1)236 237 # Ensure compound column exists238 if 'compound' not in df.columns:239 df['compound'] = 0.0240 241 fig = create_visualization(df, method)242 243 # Save results244 output_csv = f"results_{method}.csv"245 df.to_csv(output_csv, index=False)246 247 # Save visualization to temp file248 temp_dir = tempfile.mkdtemp()249 plot_path = os.path.join(temp_dir, "sentiment_analysis.png")250 fig.savefig(plot_path, bbox_inches='tight')251 plt.close(fig)252 253 preview_df = df[['text', 'sentiment', 'compound']].head(50)254 preview_df.columns = ['Text', 'Sentiment', 'Compound Score']255 return preview_df, plot_path, output_csv, plot_path256 257 except Exception as e:258 logging.error(f"CSV processing failed: {str(e)}")259 raise gr.Error(f"Processing error: {str(e)}")260 261def analyze_single_text(text, method):262 if not text.strip():263 raise gr.Error("Please enter some text to analyze")264 265 method_map = {266 "VADER": analyze_vader,267 "Logistic Regression": analyze_ml,268 "Transformer": analyze_transformer269 }270 271 if method not in method_map:272 raise gr.Error("Invalid analysis method selected")273 274 result = method_map[method](text)275 emoji = get_sentiment_emoji(result['sentiment'])276 277 interpretation = ""278 if result['compound'] >= 0.05:279 interpretation = "Positive ๐"280 elif result['compound'] <= -0.05:281 interpretation = "Negative ๐ "282 else:283 interpretation = "Neutral ๐"284 285 detailed_explanation = f"""286 **Analysis Method**: {method}287 **Detected Sentiment**: {emoji} {result['sentiment'].upper()}288 **Compound Score**: {result['compound']:.3f}289 **Interpretation**: {interpretation}290 **Detailed Scores**:291 {str(result['scores'])}292 """293 294 return (295 f"{emoji} {result['sentiment'].upper()}", # For sentiment_output (Label)296 float(result['compound']), # For compound_score (Number)297 result['scores'], # For detailed_scores (JSON)298 detailed_explanation # For explanation (Markdown)299 )300 301def clear_single_analysis():302 return (303 "", # Clear text input304 "VADER", # Reset method to default305 "", # Clear sentiment output306 0.0, # Reset compound score307 {}, # Clear detailed scores308 """**Sentiment Interpretation Guide**:309 - **Positive ๐**: Compound score โฅ 0.05310 - **Neutral ๐**: -0.05 < Compound score < 0.05311 - **Negative ๐ **: Compound score โค -0.05312 Higher absolute values indicate stronger sentiment.313 """, # Reset explanation314 pd.read_csv(RESULTS_FILE).tail(10) if os.path.exists(RESULTS_FILE) else None # Keep history315 )316 317def clear_all():318 return None, None, None, None319 320with gr.Blocks(theme=gr.themes.Soft(primary_hue="emerald")) as demo:321 gr.Markdown("""# ๐ Enhanced Sentiment Analysis Dashboard""")322 323 with gr.Tab("๐ Batch CSV Analysis"):324 with gr.Row():325 with gr.Column(scale=1):326 gr.Markdown("## CSV Settings")327 file_input = gr.File(label="Upload CSV", file_types=['.csv'])328 method_choice = gr.Dropdown(329 label="Analysis Method",330 choices=["VADER", "Logistic Regression", "Transformer"],331 value="VADER"332 )333 with gr.Row():334 analyze_btn = gr.Button("Analyze ๐", variant="primary")335 clear_btn = gr.Button("Clear ๐งน", variant="secondary")336 337 with gr.Column(scale=2):338 gr.Markdown("## Analysis Results")339 with gr.Tab("Data Preview"):340 df_display = gr.DataFrame(341 value=None,342 headers=["Text", "Sentiment", "Compound Score"],343 interactive=False,344 datatype=["str", "str", "number"],345 col_count=(3, "fixed")346 )347 with gr.Tab("Visualization"):348 plot_output = gr.Image(label="Generated Visualization")349 350 with gr.Row():351 download_csv = gr.File(label="Download CSV Results ๐พ")352 download_plot = gr.File(label="Download Visualization ๐ผ๏ธ")353 354 with gr.Tab("๐ค Single Text Analysis"):355 with gr.Row():356 with gr.Column(scale=1):357 gr.Markdown("## Text Input")358 text_input = gr.Textbox(359 label="Enter your text โ๏ธ",360 placeholder="Type or paste text here...",361 lines=5362 )363 method_choice_single = gr.Dropdown(364 label="Analysis Method",365 choices=["VADER", "Logistic Regression", "Transformer"],366 value="VADER"367 )368 with gr.Row():369 analyze_btn_single = gr.Button("Analyze ๐", variant="primary")370 clear_btn_single = gr.Button("Clear ๐งน", variant="secondary")371 372 with gr.Column(scale=2):373 gr.Markdown("## Analysis Results")374 with gr.Tab("Sentiment Scores"):375 sentiment_output = gr.Label(label="Predicted Sentiment")376 compound_score = gr.Number(label="Compound Score", precision=3)377 378 with gr.Accordion("Show Detailed Scores ๐", open=False):379 detailed_scores = gr.JSON(label="Detailed Scores")380 381 with gr.Tab("Explanation ๐"):382 explanation = gr.Markdown("""**Sentiment Interpretation Guide**:383 - **Positive ๐**: Compound score โฅ 0.05384 - **Neutral ๐**: -0.05 < Compound score < 0.05385 - **Negative ๐ **: Compound score โค -0.05386 Higher absolute values indicate stronger sentiment.387 """)388 389 with gr.Accordion("View History โณ", open=False):390 history_df = gr.DataFrame(391 value=pd.read_csv(RESULTS_FILE).tail(10) if os.path.exists(RESULTS_FILE) else None,392 headers=["Timestamp", "Method", "Sentiment", "Score"],393 interactive=False,394 datatype=["str", "str", "str", "number"]395 )396 397 analyze_btn.click(398 fn=process_csv,399 inputs=[file_input, method_choice],400 outputs=[df_display, plot_output, download_csv, download_plot]401 )402 403 analyze_btn_single.click(404 fn=analyze_single_text,405 inputs=[text_input, method_choice_single],406 outputs=[sentiment_output, compound_score, detailed_scores, explanation]407 ).then(408 fn=lambda: pd.read_csv(RESULTS_FILE).tail(10) if os.path.exists(RESULTS_FILE) else None,409 outputs=history_df410 )411 412 clear_btn_single.click(413 fn=clear_single_analysis,414 outputs=[text_input, method_choice_single, sentiment_output, compound_score, detailed_scores, explanation, history_df]415 )416 417 clear_btn.click(418 fn=clear_all,419 outputs=[df_display, plot_output, download_csv, download_plot]420 )421 422if __name__ == "__main__":423 try:424 demo.launch(share=True)425 except Exception as e:426 logging.error(f"Failed to launch interface: {str(e)}")427 print(f"Error: {str(e)}")428 