Danialebrat's picture
Deploying sentiment analysis project
9858829
"""
Sentiment visualization components using Plotly
Creates interactive charts for sentiment analysis
"""
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import pandas as pd
import json
from pathlib import Path
class SentimentCharts:
"""
Creates sentiment-related visualizations
"""
def __init__(self, config_path=None):
"""
Initialize with configuration
Args:
config_path: Path to configuration file
"""
if config_path is None:
config_path = Path(__file__).parent.parent / "config" / "viz_config.json"
with open(config_path, 'r') as f:
self.config = json.load(f)
self.sentiment_colors = self.config['color_schemes']['sentiment_polarity']
self.sentiment_order = self.config['sentiment_order']
self.chart_height = self.config['dashboard']['chart_height']
def create_sentiment_pie_chart(self, df, title="Sentiment Distribution"):
"""
Create pie chart for sentiment distribution
Args:
df: Sentiment dataframe
title: Chart title
Returns:
plotly.graph_objects.Figure
"""
sentiment_counts = df['sentiment_polarity'].value_counts()
# Order by sentiment_order
ordered_sentiments = [s for s in self.sentiment_order if s in sentiment_counts.index]
sentiment_counts = sentiment_counts[ordered_sentiments]
colors = [self.sentiment_colors.get(s, '#CCCCCC') for s in sentiment_counts.index]
fig = go.Figure(data=[go.Pie(
labels=sentiment_counts.index,
values=sentiment_counts.values,
marker=dict(colors=colors),
textinfo='label+percent',
textposition='auto',
hovertemplate='<b>%{label}</b><br>Count: %{value}<br>Percentage: %{percent}<extra></extra>'
)])
fig.update_layout(
title=title,
height=self.chart_height,
showlegend=True,
legend=dict(orientation="v", yanchor="middle", y=0.5, xanchor="left", x=1.05)
)
return fig
def create_sentiment_bar_chart(self, df, group_by, title="Sentiment Distribution"):
"""
Create stacked bar chart for sentiment distribution by group
Args:
df: Sentiment dataframe
group_by: Column to group by
title: Chart title
Returns:
plotly.graph_objects.Figure
"""
# Create pivot table
sentiment_pivot = pd.crosstab(df[group_by], df['sentiment_polarity'])
# Reorder columns by sentiment_order
ordered_columns = [s for s in self.sentiment_order if s in sentiment_pivot.columns]
sentiment_pivot = sentiment_pivot[ordered_columns]
fig = go.Figure()
for sentiment in sentiment_pivot.columns:
fig.add_trace(go.Bar(
name=sentiment,
x=sentiment_pivot.index,
y=sentiment_pivot[sentiment],
marker_color=self.sentiment_colors.get(sentiment, '#CCCCCC'),
hovertemplate='<b>%{x}</b><br>%{y} comments<extra></extra>'
))
fig.update_layout(
title=title,
xaxis_title=group_by.capitalize(),
yaxis_title="Number of Comments",
barmode='stack',
height=self.chart_height,
legend=dict(title="Sentiment", orientation="v", yanchor="top", y=1, xanchor="left", x=1.02)
)
return fig
def create_sentiment_percentage_bar_chart(self, df, group_by, title="Sentiment Distribution (%)"):
"""
Create 100% stacked bar chart for sentiment distribution
Args:
df: Sentiment dataframe
group_by: Column to group by
title: Chart title
Returns:
plotly.graph_objects.Figure
"""
# Create pivot table with percentages
sentiment_pivot = pd.crosstab(df[group_by], df['sentiment_polarity'], normalize='index') * 100
# Reorder columns by sentiment_order
ordered_columns = [s for s in self.sentiment_order if s in sentiment_pivot.columns]
sentiment_pivot = sentiment_pivot[ordered_columns]
fig = go.Figure()
for sentiment in sentiment_pivot.columns:
fig.add_trace(go.Bar(
name=sentiment,
x=sentiment_pivot.index,
y=sentiment_pivot[sentiment],
marker_color=self.sentiment_colors.get(sentiment, '#CCCCCC'),
hovertemplate='<b>%{x}</b><br>%{y:.1f}%<extra></extra>'
))
fig.update_layout(
title=title,
xaxis_title=group_by.capitalize(),
yaxis_title="Percentage (%)",
barmode='stack',
height=self.chart_height,
yaxis=dict(range=[0, 100]),
legend=dict(title="Sentiment", orientation="v", yanchor="top", y=1, xanchor="left", x=1.02)
)
return fig
def create_sentiment_heatmap(self, df, row_dimension, col_dimension, title="Sentiment Heatmap"):
"""
Create heatmap showing sentiment distribution across two dimensions
Args:
df: Sentiment dataframe
row_dimension: Row dimension
col_dimension: Column dimension
title: Chart title
Returns:
plotly.graph_objects.Figure
"""
# Create pivot table for negative sentiment percentage
negative_sentiments = self.config['negative_sentiments']
df_negative = df[df['sentiment_polarity'].isin(negative_sentiments)]
heatmap_data = pd.crosstab(
df[row_dimension],
df[col_dimension],
values=(df['sentiment_polarity'].isin(negative_sentiments)).astype(int),
aggfunc='mean'
) * 100
fig = go.Figure(data=go.Heatmap(
z=heatmap_data.values,
x=heatmap_data.columns,
y=heatmap_data.index,
colorscale='RdYlGn_r',
text=heatmap_data.values.round(1),
texttemplate='%{text}%',
textfont={"size": 12},
hovertemplate='<b>%{y} - %{x}</b><br>Negative: %{z:.1f}%<extra></extra>',
colorbar=dict(title="Negative %")
))
fig.update_layout(
title=title,
xaxis_title=col_dimension.capitalize(),
yaxis_title=row_dimension.capitalize(),
height=self.chart_height
)
return fig
def create_sentiment_timeline(self, df, freq='D', title="Sentiment Over Time"):
"""
Create line chart showing sentiment trends over time
Args:
df: Sentiment dataframe with comment_timestamp
freq: Frequency for aggregation ('D', 'W', 'M')
title: Chart title
Returns:
plotly.graph_objects.Figure
"""
if 'comment_timestamp' not in df.columns:
return go.Figure().add_annotation(
text="No timestamp data available",
xref="paper", yref="paper",
x=0.5, y=0.5, showarrow=False
)
df_temp = df.copy()
df_temp['date'] = pd.to_datetime(df_temp['comment_timestamp']).dt.to_period(freq).dt.to_timestamp()
# Aggregate by date and sentiment
timeline_data = df_temp.groupby(['date', 'sentiment_polarity']).size().reset_index(name='count')
fig = go.Figure()
for sentiment in self.sentiment_order:
sentiment_data = timeline_data[timeline_data['sentiment_polarity'] == sentiment]
if not sentiment_data.empty:
fig.add_trace(go.Scatter(
x=sentiment_data['date'],
y=sentiment_data['count'],
name=sentiment,
mode='lines+markers',
line=dict(color=self.sentiment_colors.get(sentiment, '#CCCCCC'), width=2),
marker=dict(size=6),
hovertemplate='<b>%{x}</b><br>Count: %{y}<extra></extra>'
))
fig.update_layout(
title=title,
xaxis_title="Date",
yaxis_title="Number of Comments",
height=self.chart_height,
legend=dict(title="Sentiment", orientation="v", yanchor="top", y=1, xanchor="left", x=1.02),
hovermode='x unified'
)
return fig
def create_sentiment_score_gauge(self, avg_score, title="Overall Sentiment Score"):
"""
Create gauge chart for average sentiment score
Args:
avg_score: Average sentiment score (-2 to +2)
title: Chart title
Returns:
plotly.graph_objects.Figure
"""
# Normalize score to 0-100 scale
normalized_score = ((avg_score + 2) / 4) * 100
fig = go.Figure(go.Indicator(
mode="gauge+number+delta",
value=normalized_score,
domain={'x': [0, 1], 'y': [0, 1]},
title={'text': title, 'font': {'size': 20}},
number={'suffix': '', 'font': {'size': 40}},
gauge={
'axis': {'range': [0, 100], 'tickwidth': 1, 'tickcolor': "darkblue"},
'bar': {'color': "darkblue"},
'bgcolor': "white",
'borderwidth': 2,
'bordercolor': "gray",
'steps': [
{'range': [0, 20], 'color': '#D32F2F'},
{'range': [20, 40], 'color': '#FF6F00'},
{'range': [40, 60], 'color': '#FFB300'},
{'range': [60, 80], 'color': '#7CB342'},
{'range': [80, 100], 'color': '#00C851'}
],
'threshold': {
'line': {'color': "black", 'width': 4},
'thickness': 0.75,
'value': normalized_score
}
}
))
fig.update_layout(
height=300,
margin=dict(l=20, r=20, t=60, b=20)
)
return fig