:root {
–color-primary: #2180 8D;
–color-primary-hover: #1D7480;
–color-primary-active: #1A6873;
–color-success: #208D81;
–color-warning: #A84B2F;
–color-error: #C0152F;
–color-bg-1: rgba(59, 130, 246, 0.08);
–color-bg-2: rgba(245, 158, 11, 0.08);
–color-bg-3: rgba(34, 197, 94, 0.08);
–color-bg-4: rgba(239, 68, 68, 0.08);
–color-bg-5: rgba(147, 51, 234, 0.08);
–color-bg-6: rgba(249, 115, 22, 0.08);
–color-bg-7: rgba(236, 72, 153, 0.08);
–color-bg-8: rgba(6, 182, 212, 0.08);
–color-background: #fCfCf9;
–color-surface: #ffFffd;
–color-text: #134252;
–color-text-secondary: #627078;
–color-border: rgba(94, 82, 64, 0.2);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, Roboto, sans-serif;
background-color: var(–color-background);
color: var(–color-text);
line-height: 1.6;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
header {
background: linear-gradient(135deg, #208D81 0%, #1D7480 100%);
color: white;
padding: 30px 20px;
border-radius: 12px;
margin-bottom: 30px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
header h1 {
font-size: 32px;
margin-bottom: 10px;
font-weight: 600;
}
header p {
font-size: 16px;
opacity: 0.9;
}
.kpi-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 30px;
}
.kpi-card {
background: var(–color-surface);
border: 1px solid var(–color-border);
border-radius: 10px;
padding: 20px;
text-align: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.kpi-card:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.kpi-card .label {
font-size: 12px;
color: var(–color-text-secondary);
text-transform: uppercase;
font-weight: 600;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.kpi-card .value {
font-size: 28px;
font-weight: 700;
color: var(–color-primary);
margin-bottom: 4px;
}
.kpi-card .subtext {
font-size: 12px;
color: var(–color-text-secondary);
}
.charts-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.chart-container {
background: var(–color-surface);
border: 1px solid var(–color-border);
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.chart-container h2 {
font-size: 18px;
font-weight: 600;
margin-bottom: 20px;
color: var(–color-text);
border-bottom: 2px solid var(–color-primary);
padding-bottom: 10px;
}
.chart-wrapper {
position: relative;
height: 300px;
margin-bottom: 15px;
}
.wordcloud-container {
background: var(–color-surface);
border: 1px solid var(–color-border);
border-radius: 10px;
padding: 30px 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
grid-column: 1 / -1;
}
.wordcloud-container h2 {
font-size: 18px;
font-weight: 600;
margin-bottom: 20px;
color: var(–color-text);
border-bottom: 2px solid var(–color-primary);
padding-bottom: 10px;
}
.wordcloud {
display: flex;
flex-wrap: wrap;
gap: 15px;
justify-content: center;
align-items: center;
}
.word {
padding: 8px 16px;
background: var(–color-bg-1);
border-radius: 20px;
color: var(–color-text);
font-weight: 500;
border: 1px solid var(–color-border);
transition: all 0.3s ease;
cursor: pointer;
}
.word:hover {
background: var(–color-primary);
color: white;
transform: scale(1.1);
}
.legend {
display: flex;
gap: 20px;
margin-top: 15px;
font-size: 12px;
flex-wrap: wrap;
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
}
.legend-color {
width: 12px;
height: 12px;
border-radius: 3px;
}
.explanation {
background: var(–color-bg-3);
border-left: 4px solid var(–color-success);
border-radius: 6px;
padding: 15px;
margin-bottom: 20px;
font-size: 14px;
color: var(–color-text);
}
.explanation h3 {
font-size: 14px;
font-weight: 600;
margin-bottom: 8px;
color: var(–color-text);
}
.explanation p {
margin: 0;
}
.methodology-section {
background: var(–color-surface);
border: 1px solid var(–color-border);
border-radius: 10px;
padding: 20px;
margin-top: 30px;
}
.methodology-section h2 {
font-size: 18px;
font-weight: 600;
margin-bottom: 15px;
color: var(–color-text);
}
.methodology-section ol {
margin-left: 20px;
color: var(–color-text-secondary);
}
.methodology-section li {
margin-bottom: 10px;
line-height: 1.6;
}
.sentiment-color-positive { color: var(–color-success); font-weight: 600; }
.sentiment-color-negative { color: var(–color-error); font-weight: 600; }
.sentiment-color-neutral { color: var(–color-warning); font-weight: 600; }
@media (max-width: 768px) {
.charts-grid {
grid-template-columns: 1fr;
}
header h1 {
font-size: 24px;
}
.kpi-section {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
}
https://cdn.jsdelivr.net/npm/chart.js
Total Komentar
421
dari 369 pengguna unik
Sentimen Negatif
300
71.3% dari total
Sentimen Netral
84
20.0% dari total
Sentimen Positif
37
8.8% dari total
Sesuai Konteks
418
99.3% relevan
Tidak Sesuai Konteks
3
0.7% tidak relevan
📌 Penjelasan Analisis
Data ini menganalisis sentimen netizen terhadap kebijakan MBG yang tetap disalurkan saat libur sekolah. Mayoritas komentar (71.3%) bersifat negatif, mencerminkan kekhawatiran publik tentang efisiensi anggaran dan ketepatan kebijakan. Hanya 8.8% komentar yang positif, menunjukkan dukungan terbatas terhadap kebijakan ini.
🔀 Irisan Sentiment × Konteks
⏰ Distribusi Waktu Posting Thread
📅 Sentimen Berdasarkan Waktu Posting
💬 Rata-rata Likes Berdasarkan Waktu Posting
☁️ Word Cloud – Kata Kunci Utama
MBG
Libur
Sekolah
Tetap
Anak
Jalan
Bencana
Program
Masih
Makan
Dana
Anggaran
Korupsi
Selama
Aceh
🔍 Metodologi Analisis
- Pengumpulan Data: Ekstraksi 421 komentar dari thread Threads dengan pencarian “mbg libur”. Data mencakup username, waktu, text, dan engagement metrics.
- Preprocessing & Kombinasi Teks: Menggabungkan 4 kolom teks (text1-text4) menjadi satu komentar lengkap untuk analisis holistik. Normalisasi teks dilakukan untuk memastikan konsistensi.
- Sentiment Analysis (Lexicon-Based):
- Positif: Kata-kata seperti “baik”, “bagus”, “setuju”, “bermanfaat”, “berhasil”, “enak”, “bergizi”
- Negatif: Kata-kata seperti “korupsi”, “tidak masuk akal”, “aneh”, “ribet”, “merugikan”, “korupsi”, “serakah”, “tidak transparan”
- Netral: Ketika tidak ada indikator sentimen yang jelas atau seimbang
- Context Analysis: Menentukan apakah komentar relevan dengan topik MBG libur sekolah. Minimal 1 kata kunci dari domain MBG harus ada (mbg, libur, sekolah, gizi, dana, korupsi, bencana, dll).
- Sentiment-Context Matrix: Membuat irisan antara sentiment (Positif/Negatif/Netral) × Context (Sesuai/Tidak Sesuai) untuk analisis mendalam.
- Word Frequency Analysis: Ekstraksi 30 kata paling sering muncul setelah menghilangkan stopwords bahasa Indonesia dan kata-kata UI (More, Like, Reply, Share).
- Visualisasi: Menggunakan Chart.js untuk pie charts, bar charts, dan word cloud untuk presentasi insight yang mudah dipahami.
📊 Insight Utama:
- Publik sangat kritis terhadap kebijakan MBG saat libur (71.3% negatif)
- Isu utama: efisiensi anggaran, potensi korupsi, dan alokasi ke bencana Sumatra
- Hampir semua komentar relevan dengan topik (99.3%), menunjukkan diskusi yang fokus
- Hanya 8.8% dukungan positif, sebagian besar dari pengelola dapur/penerima manfaat langsung
- Pola Waktu: Mayoritas komentar pada 3-5 hari lalu (159 komentar), dengan engagement tertinggi pada 1-3 hari lalu (rata-rata 78.9 likes)
- Tren Sentimen: Sentimen negatif konsisten mendominasi di semua periode waktu (36-57 per periode)
- Engagement Volatility: Engagement tertinggi pada thread yang berusia 1-3 hari, menunjukkan puncak viral pada periode tersebut
// Color palette
const colors = {
negative: ‘#C0152F’,
neutral: ‘#A84B2F’,
positive: ‘#208D81’,
secondary: ‘#1D7480’,
border: ‘rgba(94, 82, 64, 0.2)’
};
// Chart 1: Sentiment Distribution (Doughnut)
const sentimentCtx = document.getElementById(‘sentimentChart’).getContext(‘2d’);
new Chart(sentimentCtx, {
type: ‘doughnut’,
data: {
labels: [‘Negatif’, ‘Netral’, ‘Positif’],
datasets: [{
data: [300, 84, 37],
backgroundColor: [colors.negative, colors.neutral, colors.positive],
borderColor: ‘#fff’,
borderWidth: 2,
spacing: 10
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘bottom’,
labels: {
padding: 20,
font: { size: 12, weight: ‘500’ },
color: ‘#134252’
}
}
}
}
});
// Chart 2: Context Distribution (Doughnut)
const contextCtx = document.getElementById(‘contextChart’).getContext(‘2d’);
new Chart(contextCtx, {
type: ‘doughnut’,
data: {
labels: [‘Sesuai Konteks’, ‘Tidak Sesuai Konteks’],
datasets: [{
data: [418, 3],
backgroundColor: [colors.positive, colors.negative],
borderColor: ‘#fff’,
borderWidth: 2,
spacing: 10
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘bottom’,
labels: {
padding: 20,
font: { size: 12, weight: ‘500’ },
color: ‘#134252’
}
}
}
}
});
// Chart 3: Sentiment-Context Matrix (Stacked Bar)
const sentimentContextCtx = document.getElementById(‘sentimentContextChart’).getContext(‘2d’);
new Chart(sentimentContextCtx, {
type: ‘bar’,
data: {
labels: [‘Sesuai’, ‘Tidak Sesuai’],
datasets: [
{
label: ‘Negatif’,
data: [299, 1],
backgroundColor: colors.negative
},
{
label: ‘Netral’,
data: [82, 2],
backgroundColor: colors.neutral
},
{
label: ‘Positif’,
data: [37, 0],
backgroundColor: colors.positive
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
indexAxis: ‘y’,
scales: {
x: {
stacked: true,
ticks: { color: ‘#627078’, font: { size: 11 } },
grid: { color: ‘rgba(94, 82, 64, 0.1)’ }
},
y: {
stacked: true,
ticks: { color: ‘#627078’, font: { size: 11 } }
}
},
plugins: {
legend: {
position: ‘top’,
labels: {
padding: 15,
font: { size: 12, weight: ‘500’ },
color: ‘#134252’
}
}
}
}
});
// Chart 4: Sentiment Pie
const sentimentPieCtx = document.getElementById(‘sentimentPieChart’).getContext(‘2d’);
new Chart(sentimentPieCtx, {
type: ‘pie’,
data: {
labels: [‘Negatif\n(71.3%)’, ‘Netral\n(20.0%)’, ‘Positif\n(8.8%)’],
datasets: [{
data: [71.3, 20.0, 8.8],
backgroundColor: [colors.negative, colors.neutral, colors.positive],
borderColor: ‘#fff’,
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘bottom’,
labels: {
padding: 20,
font: { size: 12, weight: ‘500’ },
color: ‘#134252’
}
}
}
}
});
// Chart 5: Time Distribution (Bar)
const timeDistributionCtx = document.getElementById(‘timeDistributionChart’).getContext(‘2d’);
new Chart(timeDistributionCtx, {
type: ‘bar’,
data: {
labels: [‘Last Hour’, ‘1-6 Hours’, ‘6-24 Hours’, ‘1-3 Days’, ‘3-5 Days’, ‘5+ Days’],
datasets: [{
label: ‘Jumlah Komentar’,
data: [13, 17, 36, 74, 159, 117],
backgroundColor: colors.positive,
borderColor: colors.secondary,
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
ticks: { color: ‘#627078’, font: { size: 11 } },
grid: { color: ‘rgba(94, 82, 64, 0.1)’ }
},
x: {
ticks: { color: ‘#627078’, font: { size: 10 } },
grid: { display: false }
}
},
plugins: {
legend: {
display: false
}
}
}
});
// Chart 6: Sentiment by Time (Stacked Bar)
const sentimentByTimeCtx = document.getElementById(‘sentimentByTimeChart’).getContext(‘2d’);
new Chart(sentimentByTimeCtx, {
type: ‘bar’,
data: {
labels: [‘Last Hour’, ‘1-6 Hours’, ‘6-24 Hours’, ‘1-3 Days’, ‘3-5 Days’, ‘5+ Days’],
datasets: [
{
label: ‘Negatif’,
data: [8, 2, 10, 20, 57, 36],
backgroundColor: colors.negative
},
{
label: ‘Netral’,
data: [2, 8, 15, 31, 70, 61],
backgroundColor: colors.neutral
},
{
label: ‘Positif’,
data: [3, 7, 11, 23, 32, 20],
backgroundColor: colors.positive
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
stacked: true,
ticks: { color: ‘#627078’, font: { size: 10 } },
grid: { display: false }
},
y: {
stacked: true,
ticks: { color: ‘#627078’, font: { size: 11 } },
grid: { color: ‘rgba(94, 82, 64, 0.1)’ }
}
},
plugins: {
legend: {
position: ‘top’,
labels: {
padding: 15,
font: { size: 12, weight: ‘500’ },
color: ‘#134252’
}
}
}
}
});
// Chart 7: Engagement by Time (Line)
const engagementByTimeCtx = document.getElementById(‘engagementByTimeChart’).getContext(‘2d’);
new Chart(engagementByTimeCtx, {
type: ‘line’,
data: {
labels: [‘Last Hour’, ‘1-6 Hours’, ‘6-24 Hours’, ‘1-3 Days’, ‘3-5 Days’, ‘5+ Days’],
datasets: [{
label: ‘Rata-rata Likes’,
data: [1.3, 11.3, 4.5, 78.9, 21.2, 28.7],
borderColor: colors.positive,
backgroundColor: ‘rgba(32, 141, 129, 0.05)’,
borderWidth: 3,
tension: 0.4,
pointRadius: 5,
pointBackgroundColor: colors.positive,
pointBorderColor: ‘#fff’,
pointBorderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
ticks: { color: ‘#627078’, font: { size: 11 } },
grid: { color: ‘rgba(94, 82, 64, 0.1)’ }
},
x: {
ticks: { color: ‘#627078’, font: { size: 10 } },
grid: { color: ‘rgba(94, 82, 64, 0.1)’ }
}
},
plugins: {
legend: {
display: true,
labels: {
padding: 15,
font: { size: 12, weight: ‘500’ },
color: ‘#134252’
}
}
}
}
});