Quantified Self Data Correlation Finder

Quantified Self Data Correlation Finder

Visual Correlation (Scatter Plot)

Select two variables and click "Run Analysis" to see the plot.

N/A
Correlation Coefficient (r)
N/A
Data Points Analyzed
No analysis run yet.

Log Daily Data

Use the US numeric format (e.g., 7.5 for hours or score).

Current Data Log

No data logged yet.

"; } appState.data.forEach(log => { const item = document.createElement("div"); item.className = "qs-log-item"; item.innerHTML = `
${log.date} Sleep: ${log.sleep}h Mood: ${log.mood}/10 Coffee: ${log.coffee} Workout: ${log.workout}m
`; dataLogList.appendChild(item); }); } function renderDashboardTab() { correlationR.textContent = appState.analysis.r; dataPoints.textContent = appState.analysis.points; interpretationText.textContent = getInterpretation(parseFloat(appState.analysis.r) || 0); if (appState.analysis.r === 'N/A') { chartContainer.style.display = 'none'; chartPlaceholder.style.display = 'block'; return; } chartPlaceholder.style.display = 'none'; chartContainer.style.display = 'block'; // Prepare data for Chart.js const X = appState.analysis.x; const Y = appState.analysis.y; const chartData = appState.data.map(log => ({ x: log[X], y: log[Y], date: log.date })); const chartTitle = `${appState.variables[Y]} vs. ${appState.variables[X]}`; if (correlationChart) { correlationChart.destroy(); } correlationChart = new Chart(chartCanvas, { type: 'scatter', data: { datasets: [{ label: 'Daily Logs', data: chartData, backgroundColor: 'rgba(59, 130, 246, 0.7)', borderColor: 'rgba(59, 130, 246, 1)', pointRadius: 6, pointHoverRadius: 8, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: chartTitle, font: { size: 16, weight: 'bold' } }, legend: { display: false }, tooltip: { callbacks: { label: function(context) { const log = context.raw; return `Date: ${log.date} | X: ${log.x} | Y: ${log.y}`; } } } }, scales: { x: { title: { display: true, text: appState.variables[X] }, }, y: { title: { display: true, text: appState.variables[Y] } } } } }); } // --- Event Handlers --- // Add Log addLogBtn.addEventListener("click", () => { const date = logDate.value; const sleep = parseFloat(logSleep.value); const mood = parseFloat(logMood.value); const coffee = parseFloat(logCoffee.value); const workout = parseFloat(logWorkout.value); if (!date || isNaN(sleep) || isNaN(mood) || isNaN(coffee) || isNaN(workout)) { alert("Please fill out all fields with valid numbers."); return; } const newLog = { id: Date.now(), date: date, sleep: sleep, mood: mood, coffee: coffee, workout: workout }; appState.data.push(newLog); saveState(); renderConfigTab(); // Clear date field logDate.value = ''; }); // Remove Log (Event Delegation) dataLogList.addEventListener("click", (e) => { if (e.target.classList.contains("qs-remove-btn")) { const id = parseInt(e.target.dataset.id); appState.data = appState.data.filter(log => log.id !== id); saveState(); renderConfigTab(); } }); // Run Analysis runAnalysisBtn.addEventListener("click", () => { const X = xVariableSelect.value; const Y = yVariableSelect.value; if (!X || !Y || appState.data.length < 2) { appState.analysis.r = 'N/A'; appState.analysis.points = 0; renderDashboardTab(); return; } const dataX = appState.data.map(log => log[X]); const dataY = appState.data.map(log => log[Y]); const r = calculateCorrelation(dataX, dataY); appState.analysis.x = X; appState.analysis.y = Y; appState.analysis.r = r.toFixed(3); appState.analysis.points = appState.data.length; saveState(); renderDashboardTab(); }); // Update Dashboard (from config tab) updateBtn.addEventListener("click", () => { renderDashboardTab(); showTab(0); }); // PDF Download pdfBtn.addEventListener("click", () => { const { jsPDF } = window.jspdf; const fileName = `Correlation_Report_${appState.analysis.x}_vs_${appState.analysis.y}.pdf`; // Temporarily hide the PDF button for capture pdfBtn.style.display = 'none'; html2canvas(exportArea, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }).then(canvas => { // Restore PDF button pdfBtn.style.display = 'block'; const imgData = canvas.toDataURL('image/png'); const doc = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const pdfWidth = doc.internal.pageSize.getWidth(); const pdfHeight = doc.internal.pageSize.getHeight(); const imgProps = doc.getImageProperties(imgData); const imgWidth = imgProps.width; const imgHeight = imgProps.height; const margin = 40; const usableWidth = pdfWidth - (2 * margin); const ratio = usableWidth / imgWidth; let scaledHeight = imgHeight * ratio; doc.addImage(imgData, 'PNG', margin, margin, usableWidth, scaledHeight); doc.save(fileName); }).catch(err => { pdfBtn.style.display = 'block'; // Restore on error console.error("QS PDF Error:", err); // alert("An error occurred while generating the PDF."); // Per spec }); }); // --- Local Storage --- function saveState() { try { localStorage.setItem("qsAppState", JSON.stringify(appState)); } catch (e) { console.warn("QS: Could not save state."); } } function loadState() { try { const storedState = localStorage.getItem("qsAppState"); if (storedState) appState = JSON.parse(storedState); } catch (e) { console.warn("QS: Could not load state."); } } // --- Initial Load --- loadState(); renderVariableSelectors(); renderConfigTab(); renderDashboardTab(); showTab(0); });
Scroll to Top