File size: 8,448 Bytes
ae06fdb
 
 
 
 
 
 
 
 
925e69c
 
 
 
 
 
 
ae06fdb
 
925e69c
ae06fdb
 
 
925e69c
ae06fdb
925e69c
ae06fdb
 
925e69c
 
 
 
 
 
 
 
 
ae06fdb
 
 
925e69c
ae06fdb
 
925e69c
ae06fdb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496f017
 
 
 
 
ae06fdb
 
496f017
 
ae06fdb
496f017
 
223798c
 
ae06fdb
b5127dd
ae06fdb
 
 
0e6d93f
b5127dd
 
 
ae06fdb
 
 
0e6d93f
ae06fdb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223798c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import matplotlib.pyplot as plt
import numpy as np
import io
import base64

# Pie chart for conflict prediction

def create_conflict_pie_chart(result):
    fig, ax = plt.subplots(figsize=(3, 2))
    
    # Handle missing confidence
    if 'confidence' in result and result['confidence'] is not None:
        confidence = result['confidence']
    else:
        confidence = 0.8  # Default confidence when not available
    
    if result['conflict_level'] == 'High Risk':
        colors = ['#ff6b6b', '#4ecdc4']
        sizes = [confidence, 1 - confidence]
        labels = ['High Risk', 'Low Risk']
    else:
        colors = ['#4ecdc4', '#ff6b6b']
        sizes = [confidence, 1 - confidence]
        labels = ['Low Risk', 'High Risk']
    
    wedges, texts, autotexts = ax.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%',
                                      startangle=90, explode=(0.1, 0))
    
    # Update title to handle missing confidence
    if 'confidence' in result and result['confidence'] is not None:
        title = f'Conflict Risk Prediction\nConfidence: {confidence:.1%}'
    else:
        title = f'Conflict Risk Prediction\nConfidence: {confidence:.1%} (estimated)'
    
    ax.set_title(title, fontsize=10, fontweight='bold', pad=10)
    
    for autotext in autotexts:
        autotext.set_color('white')
        autotext.set_fontweight('bold')
    
    ax.legend(wedges, labels, title="Risk Levels", loc="center left", bbox_to_anchor=(1, 0, 0.5, 1))
    plt.tight_layout()
    
    img_buffer = io.BytesIO()
    plt.savefig(img_buffer, format='png', dpi=300, bbox_inches='tight')
    img_buffer.seek(0)
    img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
    plt.close()
    return f"data:image/png;base64,{img_base64}"

# Histogram for addiction score (not currently used, but included for completeness)
def create_addiction_score_chart(result, data=None):
    fig, ax = plt.subplots(figsize=(3, 2))
    if data is not None and 'Addicted_Score' in data.columns:
        scores = data['Addicted_Score'].dropna()
    else:
        np.random.seed(42)
        scores = np.random.normal(5.5, 1.5, 1000)
        scores = np.clip(scores, 1, 10)
    n, bins, patches = ax.hist(scores, bins=20, alpha=0.7, color='#4ecdc4',
                              edgecolor='black', linewidth=0.5)
    predicted_score = result['predicted_score']
    ax.axvline(x=predicted_score, color='#ff6b6b', linewidth=3,
               label=f'Your Prediction: {predicted_score:.2f}')
    if 'confidence' in result:
        confidence = result['confidence']
        ax.axvspan(predicted_score - 0.5, predicted_score + 0.5,
                  alpha=0.3, color='#ff6b6b',
                  label=f'Confidence: {confidence:.2f}')
    ax.set_title('Addiction Score Distribution with Your Prediction',
                fontsize=10, fontweight='bold', pad=10)
    ax.set_xlabel('Addiction Score (1-10)', fontsize=8, fontweight='bold')
    ax.set_ylabel('Frequency', fontsize=8, fontweight='bold')
    ax.axvspan(1, 3, alpha=0.2, color='green', label='Low Addiction (1-3)')
    ax.axvspan(3, 7, alpha=0.2, color='orange', label='Moderate Addiction (3-7)')
    ax.axvspan(7, 10, alpha=0.2, color='red', label='High Addiction (7-10)')
    ax.legend(loc='upper right', fontsize=7)
    ax.grid(True, alpha=0.3)
    ax.set_xlim(0, 10)
    plt.tight_layout()
    img_buffer = io.BytesIO()
    plt.savefig(img_buffer, format='png', dpi=300, bbox_inches='tight')
    img_buffer.seek(0)
    img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
    plt.close()
    return f"data:image/png;base64,{img_base64}"

# Gauge chart for addiction score
def create_addiction_gauge_chart(result):
    fig, ax = plt.subplots(figsize=(3, 2), subplot_kw={'projection': 'polar'})
    predicted_score = result['predicted_score']
    angle = (predicted_score - 1) * 20
    theta = np.linspace(0, np.pi, 100)
    # Reduce black line thickness by 30% (from 3 to 2.1)
    ax.plot(theta, [1]*100, 'k-', linewidth=2.1)
    # Flip colors over y-axis - now high scores are on the left (0) and low scores on the right (pi)
    high_angle = np.linspace(0, 2*20*np.pi/180, 50)
    ax.fill_between(high_angle, 0, 1, alpha=0.3, color='red', label='High (7-10)')
    mod_angle = np.linspace(2*20*np.pi/180, 6*20*np.pi/180, 50)
    ax.fill_between(mod_angle, 0, 1, alpha=0.3, color='orange', label='Moderate (3-7)')
    low_angle = np.linspace(6*20*np.pi/180, np.pi, 50)
    ax.fill_between(low_angle, 0, 1, alpha=0.3, color='green', label='Low (1-3)')
    needle_angle = angle * np.pi / 180
    # Reduce needle line thickness by 30% (from 4 to 2.8)
    ax.plot([needle_angle, needle_angle], [0, 1.2], 'k-', linewidth=2.8, label=f'Your Score: {predicted_score:.1f}')
    # Reduce round dot size by 50% (from 10 to 5)
    ax.plot(needle_angle, 1.2, 'ko', markersize=5, markeredgecolor='white', markeredgewidth=2)
    ax.set_title(f'Addiction Score Gauge\nPredicted: {predicted_score:.1f}/10',
                fontsize=8, fontweight='bold', pad=10)
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_ylim(0, 1.3)
    # Flip the gauge by adjusting text positions and angles
    ax.text(np.pi, 1.4, 'Low\n(1-3)', ha='center', va='center', fontsize=3, fontweight='bold')
    ax.text(np.pi/2, 1.4, 'Moderate\n(3-7)', ha='center', va='center', fontsize=3, fontweight='bold')
    ax.text(0, 1.4, 'High\n(7-10)', ha='center', va='center', fontsize=3, fontweight='bold')
    if 'confidence' in result:
        confidence = result['confidence']
        ax.text(0, -0.3, f'Confidence: {confidence:.2f}', ha='center', va='center',
               fontsize=6, fontweight='bold', bbox=dict(boxstyle="round,pad=0.3", facecolor="lightblue"))
    plt.tight_layout()
    img_buffer = io.BytesIO()
    plt.savefig(img_buffer, format='png', dpi=300, bbox_inches='tight')
    img_buffer.seek(0)
    img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
    plt.close()
    return f"data:image/png;base64,{img_base64}"

# Clustering: Elbow and Sleep vs Age scatter plot
def create_clustering_charts(result, cluster_df=None, user_sleep=None, user_age=None, user_cluster=None, cluster_labels_map=None):
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
    optimal_k = 3
    k_values = range(1, 11)
    inertias = [150, 120, 85, 65, 55, 50, 47, 45, 43, 42]
    ax1.plot(k_values, inertias, 'bo-', linewidth=2, markersize=8)
    ax1.set_xlabel('Number of Clusters (k)', fontweight='bold')
    ax1.set_ylabel('Inertia', fontweight='bold')
    ax1.set_title('Elbow Method: Optimal K Selection', fontsize=12, fontweight='bold')
    ax1.grid(True, alpha=0.3)
    ax1.axvline(x=optimal_k, color='red', linestyle='--', alpha=0.7, label=f'Optimal k = {optimal_k}')
    ax1.legend()
    # Scatter plot: Sleep vs Age colored by cluster (always 3 clusters/colors)
    if cluster_df is not None and cluster_labels_map is not None:
        colors = ['#4ecdc4', '#ffd93d', '#ff6b6b']
        for cluster in range(optimal_k):
            sub = cluster_df[cluster_df['cluster'] == cluster]
            color = colors[cluster % len(colors)]
            label = cluster_labels_map.get(cluster, f'Cluster {cluster}')
            ax2.scatter(sub['Sleep_Hours_Per_Night'], sub['Age'], c=color, alpha=0.7, s=50, label=label)
        # Highlight the user's point
        if user_sleep is not None and user_age is not None and user_cluster is not None:
            color = colors[user_cluster % len(colors)]
            ax2.scatter([user_sleep], [user_age], c='red', marker='*', s=250, edgecolors='black', linewidths=2, label='You')
    ax2.set_xlabel('Sleep Hours per Night', fontweight='bold')
    ax2.set_ylabel('Age', fontweight='bold')
    ax2.set_title(f'Cluster Analysis: Sleep vs Age (k={optimal_k})', fontsize=12, fontweight='bold')
    handles, labels = ax2.get_legend_handles_labels()
    # Remove duplicate 'You' label if present
    seen = set()
    new_handles, new_labels = [], []
    for h, l in zip(handles, labels):
        if l not in seen:
            new_handles.append(h)
            new_labels.append(l)
            seen.add(l)
    ax2.legend(new_handles, new_labels)
    ax2.grid(True, alpha=0.3)
    plt.tight_layout()
    img_buffer = io.BytesIO()
    plt.savefig(img_buffer, format='png', dpi=300, bbox_inches='tight')
    img_buffer.seek(0)
    img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
    plt.close()
    return f"data:image/png;base64,{img_base64}"