AIMirror-Backend / visualizations.py
Zayeemk's picture
Upload 21 files
69aa668 verified
import plotly.graph_objects as go
import plotly.express as px
from wordcloud import WordCloud
import matplotlib.pyplot as plt
import io
import base64
from typing import List, Dict
import numpy as np
import logging
logger = logging.getLogger(__name__)
class VisualizationGenerator:
"""
Generate various visualizations for emotion analysis
"""
def __init__(self):
"""Initialize visualization generator"""
self.emotion_colors = {
'joy': '#FFD700',
'sadness': '#4682B4',
'anger': '#DC143C',
'fear': '#483D8B',
'surprise': '#00CED1',
'neutral': '#A9A9A9',
'disgust': '#556B2F'
}
def create_visualizations(self, results: List[Dict]) -> Dict:
"""
Create all visualizations from emotion analysis results
Args:
results: List of emotion detection results
Returns:
Dictionary containing all visualization data
"""
try:
# Aggregate emotions
aggregated = self._aggregate_results(results)
# Create various charts
emotion_pie = self._create_emotion_pie(aggregated['emotions'])
emotion_bar = self._create_emotion_bar(aggregated['emotions'])
sentiment_gauge = self._create_sentiment_gauge(aggregated['sentiment_score'])
timeline = self._create_emotion_timeline(results)
radar_chart = self._create_emotion_radar(aggregated['emotions'])
return {
"emotion_pie": emotion_pie,
"emotion_bar": emotion_bar,
"sentiment_gauge": sentiment_gauge,
"emotion_timeline": timeline,
"emotion_radar": radar_chart,
"statistics": {
"total_texts": len(results),
"dominant_emotion": aggregated['dominant_emotion'],
"avg_sentiment": aggregated['sentiment_score'],
"sentiment_label": aggregated['sentiment_label']
}
}
except Exception as e:
logger.error(f"Error creating visualizations: {str(e)}")
return {}
def _aggregate_results(self, results: List[Dict]) -> Dict:
"""Aggregate multiple emotion results"""
if not results:
return {}
# Initialize
emotion_sums = {}
sentiment_sum = 0.0
# Aggregate
for result in results:
for emotion, score in result['emotions'].items():
emotion_sums[emotion] = emotion_sums.get(emotion, 0) + score
sentiment_sum += result.get('sentiment_score', 0)
# Calculate averages
n = len(results)
emotions_avg = {emotion: score / n for emotion, score in emotion_sums.items()}
# Get dominant
dominant = max(emotions_avg.items(), key=lambda x: x[1])
# Sentiment
avg_sentiment = sentiment_sum / n
sentiment_label = "positive" if avg_sentiment > 0.1 else "negative" if avg_sentiment < -0.1 else "neutral"
return {
"emotions": emotions_avg,
"dominant_emotion": dominant[0],
"sentiment_score": avg_sentiment,
"sentiment_label": sentiment_label
}
def _create_emotion_pie(self, emotions: Dict[str, float]) -> str:
"""Create a pie chart of emotion distribution"""
try:
# Filter significant emotions
significant = {k: v for k, v in emotions.items() if v > 0.05}
if not significant:
significant = emotions
# Create pie chart
fig = go.Figure(data=[go.Pie(
labels=[e.capitalize() for e in significant.keys()],
values=list(significant.values()),
marker=dict(colors=[self.emotion_colors.get(e, '#808080') for e in significant.keys()]),
hole=0.3,
textinfo='label+percent',
textfont=dict(size=14),
hovertemplate='<b>%{label}</b><br>%{percent}<extra></extra>'
)])
fig.update_layout(
title={
'text': 'Emotion Distribution',
'x': 0.5,
'xanchor': 'center',
'font': {'size': 20, 'color': '#333'}
},
showlegend=True,
height=400,
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)',
font=dict(family="Arial, sans-serif")
)
return fig.to_json()
except Exception as e:
logger.error(f"Error creating pie chart: {str(e)}")
return "{}"
def _create_emotion_bar(self, emotions: Dict[str, float]) -> str:
"""Create a bar chart of emotions"""
try:
# Sort emotions
sorted_emotions = sorted(emotions.items(), key=lambda x: x[1], reverse=True)
labels = [e[0].capitalize() for e in sorted_emotions]
values = [e[1] * 100 for e in sorted_emotions]
colors = [self.emotion_colors.get(e[0], '#808080') for e in sorted_emotions]
fig = go.Figure(data=[go.Bar(
x=labels,
y=values,
marker=dict(color=colors),
text=[f'{v:.1f}%' for v in values],
textposition='auto',
hovertemplate='<b>%{x}</b><br>%{y:.1f}%<extra></extra>'
)])
fig.update_layout(
title={
'text': 'Emotion Intensity',
'x': 0.5,
'xanchor': 'center',
'font': {'size': 20, 'color': '#333'}
},
xaxis_title='Emotion',
yaxis_title='Intensity (%)',
height=400,
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)',
font=dict(family="Arial, sans-serif"),
yaxis=dict(gridcolor='#E5E5E5')
)
return fig.to_json()
except Exception as e:
logger.error(f"Error creating bar chart: {str(e)}")
return "{}"
def _create_sentiment_gauge(self, sentiment_score: float) -> str:
"""Create a gauge chart for sentiment"""
try:
# Normalize to 0-100 scale
gauge_value = (sentiment_score + 1) * 50
# Determine color
if sentiment_score > 0.1:
color = '#28a745'
elif sentiment_score < -0.1:
color = '#dc3545'
else:
color = '#ffc107'
fig = go.Figure(go.Indicator(
mode="gauge+number+delta",
value=gauge_value,
domain={'x': [0, 1], 'y': [0, 1]},
title={'text': "Sentiment Score", 'font': {'size': 20}},
gauge={
'axis': {'range': [0, 100], 'tickwidth': 1},
'bar': {'color': color},
'steps': [
{'range': [0, 33], 'color': '#ffebee'},
{'range': [33, 66], 'color': '#fff9e6'},
{'range': [66, 100], 'color': '#e8f5e9'}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 50
}
}
))
fig.update_layout(
height=300,
paper_bgcolor='rgba(0,0,0,0)',
font=dict(family="Arial, sans-serif")
)
return fig.to_json()
except Exception as e:
logger.error(f"Error creating gauge: {str(e)}")
return "{}"
def _create_emotion_timeline(self, results: List[Dict]) -> str:
"""Create a timeline of emotion changes"""
try:
if len(results) < 2:
return "{}"
# Extract emotion data over time
emotions_over_time = {}
for emotion in results[0]['emotions'].keys():
emotions_over_time[emotion] = [r['emotions'][emotion] * 100 for r in results]
# Create line chart
fig = go.Figure()
for emotion, values in emotions_over_time.items():
if max(values) > 5: # Only show significant emotions
fig.add_trace(go.Scatter(
x=list(range(1, len(values) + 1)),
y=values,
mode='lines+markers',
name=emotion.capitalize(),
line=dict(color=self.emotion_colors.get(emotion, '#808080'), width=2),
marker=dict(size=6)
))
fig.update_layout(
title={
'text': 'Emotion Timeline',
'x': 0.5,
'xanchor': 'center',
'font': {'size': 20, 'color': '#333'}
},
xaxis_title='Text Entry',
yaxis_title='Emotion Intensity (%)',
height=400,
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)',
font=dict(family="Arial, sans-serif"),
hovermode='x unified',
yaxis=dict(gridcolor='#E5E5E5'),
xaxis=dict(gridcolor='#E5E5E5')
)
return fig.to_json()
except Exception as e:
logger.error(f"Error creating timeline: {str(e)}")
return "{}"
def _create_emotion_radar(self, emotions: Dict[str, float]) -> str:
"""Create a radar chart of emotions"""
try:
categories = [e.capitalize() for e in emotions.keys()]
values = [v * 100 for v in emotions.values()]
fig = go.Figure()
fig.add_trace(go.Scatterpolar(
r=values,
theta=categories,
fill='toself',
fillcolor='rgba(75, 192, 192, 0.2)',
line=dict(color='rgb(75, 192, 192)', width=2),
marker=dict(size=8)
))
fig.update_layout(
polar=dict(
radialaxis=dict(
visible=True,
range=[0, max(values) * 1.2] if max(values) > 0 else [0, 100],
gridcolor='#E5E5E5'
),
angularaxis=dict(gridcolor='#E5E5E5')
),
title={
'text': 'Emotion Profile',
'x': 0.5,
'xanchor': 'center',
'font': {'size': 20, 'color': '#333'}
},
height=400,
paper_bgcolor='rgba(0,0,0,0)',
font=dict(family="Arial, sans-serif"),
showlegend=False
)
return fig.to_json()
except Exception as e:
logger.error(f"Error creating radar chart: {str(e)}")
return "{}"