| | """ |
| | Distribution visualization components using Plotly |
| | Creates charts for intent, language, and other distributions |
| | """ |
| | 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 DistributionCharts: |
| | """ |
| | Creates distribution 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.intent_colors = self.config['color_schemes']['intent'] |
| | self.platform_colors = self.config['color_schemes']['platform'] |
| | self.brand_colors = self.config['color_schemes']['brand'] |
| | self.intent_order = self.config['intent_order'] |
| | self.chart_height = self.config['dashboard']['chart_height'] |
| |
|
| | def create_intent_bar_chart(self, df, title="Intent Distribution", orientation='h'): |
| | """ |
| | Create horizontal bar chart for intent distribution (handles multi-label) |
| | |
| | Args: |
| | df: Sentiment dataframe |
| | title: Chart title |
| | orientation: 'h' for horizontal, 'v' for vertical |
| | |
| | Returns: |
| | plotly.graph_objects.Figure |
| | """ |
| | |
| | df_exploded = df.copy() |
| | df_exploded['intent'] = df_exploded['intent'].str.split(',') |
| | df_exploded = df_exploded.explode('intent') |
| | df_exploded['intent'] = df_exploded['intent'].str.strip() |
| |
|
| | |
| | intent_counts = df_exploded['intent'].value_counts() |
| |
|
| | |
| | ordered_intents = [i for i in self.intent_order if i in intent_counts.index] |
| | intent_counts = intent_counts[ordered_intents] |
| |
|
| | colors = [self.intent_colors.get(i, '#CCCCCC') for i in intent_counts.index] |
| |
|
| | if orientation == 'h': |
| | fig = go.Figure(data=[go.Bar( |
| | y=intent_counts.index, |
| | x=intent_counts.values, |
| | orientation='h', |
| | marker=dict(color=colors), |
| | text=intent_counts.values, |
| | textposition='auto', |
| | hovertemplate='<b>%{y}</b><br>Count: %{x}<extra></extra>' |
| | )]) |
| |
|
| | fig.update_layout( |
| | title=title, |
| | xaxis_title="Number of Comments", |
| | yaxis_title="Intent", |
| | height=self.chart_height, |
| | yaxis={'categoryorder': 'total ascending'} |
| | ) |
| | else: |
| | fig = go.Figure(data=[go.Bar( |
| | x=intent_counts.index, |
| | y=intent_counts.values, |
| | marker=dict(color=colors), |
| | text=intent_counts.values, |
| | textposition='auto', |
| | hovertemplate='<b>%{x}</b><br>Count: %{y}<extra></extra>' |
| | )]) |
| |
|
| | fig.update_layout( |
| | title=title, |
| | xaxis_title="Intent", |
| | yaxis_title="Number of Comments", |
| | height=self.chart_height |
| | ) |
| |
|
| | return fig |
| |
|
| | def create_intent_pie_chart(self, df, title="Intent Distribution"): |
| | """ |
| | Create pie chart for intent distribution |
| | |
| | Args: |
| | df: Sentiment dataframe |
| | title: Chart title |
| | |
| | Returns: |
| | plotly.graph_objects.Figure |
| | """ |
| | |
| | df_exploded = df.copy() |
| | df_exploded['intent'] = df_exploded['intent'].str.split(',') |
| | df_exploded = df_exploded.explode('intent') |
| | df_exploded['intent'] = df_exploded['intent'].str.strip() |
| |
|
| | intent_counts = df_exploded['intent'].value_counts() |
| |
|
| | |
| | ordered_intents = [i for i in self.intent_order if i in intent_counts.index] |
| | intent_counts = intent_counts[ordered_intents] |
| |
|
| | colors = [self.intent_colors.get(i, '#CCCCCC') for i in intent_counts.index] |
| |
|
| | fig = go.Figure(data=[go.Pie( |
| | labels=intent_counts.index, |
| | values=intent_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_platform_distribution(self, df, title="Comments by Platform"): |
| | """ |
| | Create bar chart for platform distribution |
| | |
| | Args: |
| | df: Sentiment dataframe |
| | title: Chart title |
| | |
| | Returns: |
| | plotly.graph_objects.Figure |
| | """ |
| | platform_counts = df['platform'].value_counts() |
| |
|
| | colors = [self.platform_colors.get(p, self.platform_colors['default']) for p in platform_counts.index] |
| |
|
| | fig = go.Figure(data=[go.Bar( |
| | x=platform_counts.index, |
| | y=platform_counts.values, |
| | marker=dict(color=colors), |
| | text=platform_counts.values, |
| | textposition='auto', |
| | hovertemplate='<b>%{x}</b><br>Comments: %{y}<extra></extra>' |
| | )]) |
| |
|
| | fig.update_layout( |
| | title=title, |
| | xaxis_title="Platform", |
| | yaxis_title="Number of Comments", |
| | height=self.chart_height |
| | ) |
| |
|
| | return fig |
| |
|
| | def create_brand_distribution(self, df, title="Comments by Brand"): |
| | """ |
| | Create bar chart for brand distribution |
| | |
| | Args: |
| | df: Sentiment dataframe |
| | title: Chart title |
| | |
| | Returns: |
| | plotly.graph_objects.Figure |
| | """ |
| | brand_counts = df['brand'].value_counts() |
| |
|
| | colors = [self.brand_colors.get(b, self.brand_colors['default']) for b in brand_counts.index] |
| |
|
| | fig = go.Figure(data=[go.Bar( |
| | x=brand_counts.index, |
| | y=brand_counts.values, |
| | marker=dict(color=colors), |
| | text=brand_counts.values, |
| | textposition='auto', |
| | hovertemplate='<b>%{x}</b><br>Comments: %{y}<extra></extra>' |
| | )]) |
| |
|
| | fig.update_layout( |
| | title=title, |
| | xaxis_title="Brand", |
| | yaxis_title="Number of Comments", |
| | height=self.chart_height |
| | ) |
| |
|
| | return fig |
| |
|
| | def create_language_distribution(self, df, top_n=10, title="Language Distribution"): |
| | """ |
| | Create bar chart for language distribution |
| | |
| | Args: |
| | df: Sentiment dataframe |
| | top_n: Number of top languages to show |
| | title: Chart title |
| | |
| | Returns: |
| | plotly.graph_objects.Figure |
| | """ |
| | if 'detected_language' not in df.columns: |
| | return go.Figure().add_annotation( |
| | text="No language data available", |
| | xref="paper", yref="paper", |
| | x=0.5, y=0.5, showarrow=False |
| | ) |
| |
|
| | lang_counts = df['detected_language'].value_counts().head(top_n) |
| |
|
| | fig = go.Figure(data=[go.Bar( |
| | x=lang_counts.index, |
| | y=lang_counts.values, |
| | marker=dict(color='#2196F3'), |
| | text=lang_counts.values, |
| | textposition='auto', |
| | hovertemplate='<b>%{x}</b><br>Comments: %{y}<extra></extra>' |
| | )]) |
| |
|
| | fig.update_layout( |
| | title=title, |
| | xaxis_title="Language", |
| | yaxis_title="Number of Comments", |
| | height=self.chart_height |
| | ) |
| |
|
| | return fig |
| |
|
| | def create_combined_distribution_sunburst(self, df, title="Hierarchical Distribution"): |
| | """ |
| | Create sunburst chart showing hierarchical distribution |
| | (Brand > Platform > Sentiment) |
| | |
| | Args: |
| | df: Sentiment dataframe |
| | title: Chart title |
| | |
| | Returns: |
| | plotly.graph_objects.Figure |
| | """ |
| | |
| | sunburst_data = df.groupby(['brand', 'platform', 'sentiment_polarity']).size().reset_index(name='count') |
| |
|
| | fig = px.sunburst( |
| | sunburst_data, |
| | path=['brand', 'platform', 'sentiment_polarity'], |
| | values='count', |
| | title=title, |
| | height=500 |
| | ) |
| |
|
| | fig.update_layout( |
| | margin=dict(t=50, l=0, r=0, b=0) |
| | ) |
| |
|
| | return fig |
| |
|
| | def create_brand_platform_matrix(self, df, title="Brand-Platform Comment Matrix"): |
| | """ |
| | Create heatmap showing comment distribution across brands and platforms |
| | |
| | Args: |
| | df: Sentiment dataframe |
| | title: Chart title |
| | |
| | Returns: |
| | plotly.graph_objects.Figure |
| | """ |
| | |
| | matrix_data = pd.crosstab(df['brand'], df['platform']) |
| |
|
| | fig = go.Figure(data=go.Heatmap( |
| | z=matrix_data.values, |
| | x=matrix_data.columns, |
| | y=matrix_data.index, |
| | colorscale='Blues', |
| | text=matrix_data.values, |
| | texttemplate='%{text}', |
| | textfont={"size": 14}, |
| | hovertemplate='<b>%{y} - %{x}</b><br>Comments: %{z}<extra></extra>', |
| | colorbar=dict(title="Comments") |
| | )) |
| |
|
| | fig.update_layout( |
| | title=title, |
| | xaxis_title="Platform", |
| | yaxis_title="Brand", |
| | height=self.chart_height |
| | ) |
| |
|
| | return fig |
| |
|
| | def create_reply_required_chart(self, df, group_by='brand', title="Comments Requiring Reply"): |
| | """ |
| | Create stacked bar chart showing reply requirements |
| | |
| | Args: |
| | df: Sentiment dataframe |
| | group_by: Column to group by |
| | title: Chart title |
| | |
| | Returns: |
| | plotly.graph_objects.Figure |
| | """ |
| | |
| | reply_data = df.groupby([group_by, 'requires_reply']).size().reset_index(name='count') |
| | reply_pivot = reply_data.pivot(index=group_by, columns='requires_reply', values='count').fillna(0) |
| |
|
| | fig = go.Figure() |
| |
|
| | if False in reply_pivot.columns: |
| | fig.add_trace(go.Bar( |
| | name='No Reply Needed', |
| | x=reply_pivot.index, |
| | y=reply_pivot[False], |
| | marker_color='#81C784', |
| | hovertemplate='<b>%{x}</b><br>No Reply: %{y}<extra></extra>' |
| | )) |
| |
|
| | if True in reply_pivot.columns: |
| | fig.add_trace(go.Bar( |
| | name='Reply Required', |
| | x=reply_pivot.index, |
| | y=reply_pivot[True], |
| | marker_color='#FF7043', |
| | hovertemplate='<b>%{x}</b><br>Reply Required: %{y}<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="Reply Status", orientation="v", yanchor="top", y=1, xanchor="left", x=1.02) |
| | ) |
| |
|
| | return fig |
| |
|
| | def create_engagement_scatter(self, content_summary_df, title="Content Engagement Analysis"): |
| | """ |
| | Create scatter plot showing content engagement |
| | |
| | Args: |
| | content_summary_df: DataFrame with content summary statistics |
| | title: Chart title |
| | |
| | Returns: |
| | plotly.graph_objects.Figure |
| | """ |
| | fig = px.scatter( |
| | content_summary_df, |
| | x='total_comments', |
| | y='negative_percentage', |
| | size='reply_required_count', |
| | color='negative_percentage', |
| | hover_data=['content_description'], |
| | title=title, |
| | labels={ |
| | 'total_comments': 'Total Comments', |
| | 'negative_percentage': 'Negative Sentiment %', |
| | 'reply_required_count': 'Replies Required' |
| | }, |
| | color_continuous_scale='RdYlGn_r', |
| | height=self.chart_height |
| | ) |
| |
|
| | fig.update_layout( |
| | xaxis_title="Total Comments", |
| | yaxis_title="Negative Sentiment %", |
| | coloraxis_colorbar=dict(title="Negative %") |
| | ) |
| |
|
| | return fig |