flaskapp / src /.ipynb_checkpoints /analyze_yelp_data-checkpoint.py
OVH
Added all the files
67b1c6c
import pandas as pd
import numpy as np
from transformers import AutoTokenizer, AutoModel
import torch
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler
from textblob import TextBlob
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import PCA
import warnings
from typing import Dict, List, Tuple
import logging
from collections import Counter
from detoxify import Detoxify
import re
from datetime import datetime
import seaborn as sns
import matplotlib.pyplot as plt
from pathlib import Path
import json
class AdvancedYelpAnalyzer:
def __init__(self, df: pd.DataFrame):
"""Initialize the analyzer with necessary models and configurations"""
self.df = df.copy()
self.bert_tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
self.bert_model = AutoModel.from_pretrained('bert-base-uncased')
self.vader = SentimentIntensityAnalyzer()
self.toxic_model = Detoxify('original')
self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
self.bert_model.to(self.device)
# Configure logging
logging.basicConfig(level=logging.INFO)
self.logger = logging.getLogger(__name__)
def get_bert_embeddings(self, texts: List[str], batch_size: int = 32) -> np.ndarray:
"""Generate BERT embeddings for text"""
embeddings = []
for i in range(0, len(texts), batch_size):
batch_texts = texts[i:i + batch_size]
encoded = self.bert_tokenizer(batch_texts,
padding=True,
truncation=True,
max_length=512,
return_tensors='pt')
with torch.no_grad():
encoded = {k: v.to(self.device) for k, v in encoded.items()}
outputs = self.bert_model(**encoded)
batch_embeddings = outputs.last_hidden_state[:, 0, :].cpu().numpy()
embeddings.append(batch_embeddings)
return np.vstack(embeddings)
def analyze_sentiment(self) -> pd.DataFrame:
"""Perform comprehensive sentiment analysis using multiple tools"""
self.logger.info("Starting sentiment analysis...")
# Calculate BERT embeddings for reviews
self.logger.info("Calculating BERT embeddings...")
review_texts = self.df['review_text'].fillna('').tolist()
bert_embeddings = self.get_bert_embeddings(review_texts)
# Calculate review length using BERT tokenizer
self.logger.info("Calculating tokenized lengths...")
self.df['review_length'] = self.df['review_text'].apply(
lambda x: len(self.bert_tokenizer.encode(str(x)))
)
# Store BERT embeddings mean and std as features
self.df['bert_embedding_mean'] = np.mean(bert_embeddings, axis=1)
self.df['bert_embedding_std'] = np.std(bert_embeddings, axis=1)
# TextBlob sentiment and subjectivity
self.df['textblob_polarity'] = self.df['review_text'].apply(
lambda x: TextBlob(str(x)).sentiment.polarity
)
self.df['textblob_subjectivity'] = self.df['review_text'].apply(
lambda x: TextBlob(str(x)).sentiment.subjectivity
)
# VADER sentiment with custom negative phrase handling
def get_enhanced_vader_scores(text):
# Custom negative phrases
negative_phrases = [
'too long', 'way too long', 'waiting', 'changed our minds',
'too many', 'took forever', 'took too long', 'waste of time',
'not worth', 'disappointing', 'mediocre', 'needs improvement'
]
# Get base VADER scores
base_scores = self.vader.polarity_scores(str(text))
# Check for negative phrases
text_lower = str(text).lower()
neg_count = sum(1 for phrase in negative_phrases if phrase in text_lower)
# Adjust scores if negative phrases are found
if neg_count > 0:
base_scores['neg'] = max(base_scores['neg'], min(0.7, neg_count * 0.2))
base_scores['compound'] *= (1 - (neg_count * 0.15))
# Readjust neutral score
base_scores['neu'] = max(0, 1 - base_scores['neg'] - base_scores['pos'])
return base_scores
# Apply enhanced VADER scoring
vader_scores = self.df['review_text'].apply(get_enhanced_vader_scores)
self.df['vader_compound'] = vader_scores.apply(lambda x: x['compound'])
self.df['vader_negative'] = vader_scores.apply(lambda x: x['neg'])
self.df['vader_positive'] = vader_scores.apply(lambda x: x['pos'])
self.df['vader_neutral'] = vader_scores.apply(lambda x: x['neu'])
# Calculate sentiment extremity
self.df['sentiment_extremity'] = self.df['vader_compound'].abs()
return self.df
def detect_anomalies(self) -> pd.DataFrame:
"""Detect anomalous reviews using Isolation Forest with BERT features"""
self.logger.info("Detecting anomalies...")
# Prepare features for anomaly detection
features = [
'review_stars',
'textblob_polarity',
'vader_compound',
'sentiment_extremity',
'review_length',
'bert_embedding_mean',
'bert_embedding_std'
]
# Ensure all features exist
missing_features = [f for f in features if f not in self.df.columns]
if missing_features:
self.analyze_sentiment()
# Standardize features
scaler = StandardScaler()
X = scaler.fit_transform(self.df[features])
# Apply Isolation Forest
iso_forest = IsolationForest(
contamination=0.1,
random_state=42,
n_jobs=-1
)
# Fit and predict
self.df['is_anomaly'] = iso_forest.fit_predict(X)
self.df['anomaly_score'] = iso_forest.score_samples(X)
return self.df
def detect_ai_generated_text(self) -> pd.DataFrame:
"""Estimate likelihood of AI-generated content"""
self.logger.info("Detecting AI-generated content...")
# Ensure sentiment analysis has been run
if 'textblob_subjectivity' not in self.df.columns:
self.analyze_sentiment()
# Use detoxify model to get toxicity scores
texts = self.df['review_text'].fillna('').tolist()
toxic_scores = self.toxic_model.predict(texts)
# Add scores to DataFrame
toxic_score_types = ['toxicity', 'severe_toxicity', 'obscene', 'identity_attack',
'insult', 'threat', 'sexual_explicit']
for score_type in toxic_score_types:
if score_type in toxic_scores:
self.df[f'toxic_{score_type}'] = toxic_scores[score_type]
# Calculate AI generation likelihood based on various factors
self.df['ai_generated_likelihood'] = (
(self.df['textblob_subjectivity'] < 0.3) & # Low subjectivity
(self.df['sentiment_extremity'] > 0.8) & # Extreme sentiment
(self.df['review_length'] > self.df['review_length'].quantile(0.95)) & # Unusually long
(self.df['bert_embedding_std'] < self.df['bert_embedding_std'].quantile(0.25)) # Unusual language patterns
).astype(int)
# Add additional AI detection features
self.df['ai_detection_score'] = (
(self.df['textblob_subjectivity'] * -1) + # Lower subjectivity increases score
(self.df['sentiment_extremity'] * 0.5) + # Extreme sentiment contributes somewhat
(self.df['bert_embedding_std'] * -0.5) # Lower variation in embeddings increases score
).clip(0, 1) # Normalize between 0 and 1
return self.df
def analyze_business_categories(self) -> Dict:
"""Analyze trends and patterns specific to business categories"""
self.logger.info("Analyzing business categories...")
# Extract categories
categories = self.df['categories'].fillna('').str.split(', ')
all_categories = [cat for cats in categories if isinstance(cats, list) for cat in cats]
category_counts = Counter(all_categories)
# Analyze reviews by category
category_analysis = {}
for category in set(all_categories):
category_reviews = self.df[self.df['categories'].str.contains(category, na=False)]
category_analysis[category] = {
'review_count': len(category_reviews),
'avg_rating': category_reviews['review_stars'].mean() if not category_reviews.empty else None,
'avg_sentiment': category_reviews['vader_compound'].mean() if 'vader_compound' in self.df.columns and not category_reviews.empty else None,
'avg_subjectivity': category_reviews['textblob_subjectivity'].mean() if 'textblob_subjectivity' in self.df.columns and not category_reviews.empty else None
}
return category_analysis
def visualize_results(self, output_dir: str):
"""Create visualizations for analysis results"""
plt.figure(figsize=(15, 10))
# Sentiment Distribution
plt.subplot(2, 2, 1)
sns.histplot(data=self.df, x='vader_compound', bins=50)
plt.title('Sentiment Distribution')
# Review Volume Over Time
plt.subplot(2, 2, 2)
daily_reviews = self.df.groupby('review_date').size()
daily_reviews.plot()
plt.title('Review Volume Over Time')
# Anomaly Score Distribution
plt.subplot(2, 2, 3)
if 'anomaly_score' not in self.df.columns:
self.detect_anomalies()
sns.histplot(data=self.df, x='anomaly_score', bins=50)
plt.title('Anomaly Score Distribution')
# AI Generation Likelihood
plt.subplot(2, 2, 4)
if 'ai_generated_likelihood' not in self.df.columns:
self.detect_ai_generated_text()
sns.histplot(data=self.df, x='ai_generated_likelihood', bins=2)
plt.title('AI Generation Likelihood')
plt.tight_layout()
plt.savefig(f'{output_dir}/analysis_results.png')
plt.close()
def run_full_analysis(self, output_dir: str) -> Tuple[pd.DataFrame, Dict]:
"""Run complete analysis pipeline with detailed outputs"""
self.logger.info("Starting full analysis pipeline...")
# Create output directory if it doesn't exist
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
try:
# Run all analyses
self.analyze_sentiment()
self.detect_anomalies()
self.detect_ai_generated_text()
category_analysis = self.analyze_business_categories()
# Create visualizations
self.visualize_results(str(output_dir))
# Compile results
analysis_results = {
'category_analysis': category_analysis,
'sentiment_summary': {
'avg_sentiment': self.df['vader_compound'].mean(),
'positive_reviews': len(self.df[self.df['vader_compound'] > 0.5]),
'negative_reviews': len(self.df[self.df['vader_compound'] < -0.5]),
'neutral_reviews': len(self.df[abs(self.df['vader_compound']) <= 0.5])
},
'ai_detection_summary': {
'likely_ai_generated': len(self.df[self.df['ai_generated_likelihood'] == 1]),
'avg_ai_score': self.df['ai_detection_score'].mean()
},
'anomaly_summary': {
'anomalous_reviews': len(self.df[self.df['is_anomaly'] == -1]),
'avg_anomaly_score': self.df['anomaly_score'].mean()
}
}
# Save results
self.df.to_csv(output_dir / "analyzed_data.csv", index=False)
with open(output_dir / "analysis_results.json", 'w') as f:
json.dump(analysis_results, f, indent=4)
return self.df, analysis_results
except Exception as e:
self.logger.error(f"Error during analysis: {str(e)}")
raise
# For testing
if __name__ == "__main__":
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
try:
# Read test data
df = pd.read_csv("test_data.csv")
# Initialize analyzer
analyzer = AdvancedYelpAnalyzer(df)
# Run analysis
output_dir = "output"
analyzed_df, results = analyzer.run_full_analysis(output_dir)
logger.info("Analysis completed successfully!")
except Exception as e:
logger.error(f"Error during testing: {str(e)}")
raise