Skip to content

Commit 601eb2e

Browse files
committed
add single feature to the web
1 parent c3aaa55 commit 601eb2e

File tree

2 files changed

+251
-35
lines changed

2 files changed

+251
-35
lines changed

src/web/index.html

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
</head>
1616

1717
<body>
18-
<main class="container mx-auto border lg:px-12 p-5 min-h-screen">
18+
<main class="container mx-auto lg:px-12 p-5 min-h-screen">
1919
<h1 class="font-bold text-2xl text-center flex items-baseline justify-center space-x-2 mx-auto my-4">
2020
<svg width="1.75em" height="1.75em" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
2121
<rect width="300" height="300" fill="#F7DF1E"/>
@@ -26,12 +26,13 @@ <h1 class="font-bold text-2xl text-center flex items-baseline justify-center spa
2626
<span>Linear Regression</span>
2727
</h1>
2828

29+
<hr>
2930

30-
<section>
31+
<section id="data-input-section" class="pb-32">
3132
<form id="data-form" class="mt-5">
3233
<div class="flex items-center justify-center">
3334
<label for="input-file"
34-
class="cursor-pointer bg-indigo-100 text-indigo-600 py-2 px-4 rounded-md hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
35+
class="w-full text-center cursor-pointer bg-indigo-100 text-indigo-600 py-2 px-4 rounded-md hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
3536
Upload File
3637
</label>
3738
<input id="input-file" type="file" class="hidden" name="data"/>
@@ -41,44 +42,62 @@ <h1 class="font-bold text-2xl text-center flex items-baseline justify-center spa
4142

4243
<div class="flex justify-center">
4344
<input type="submit" value="Preview data" id="btn-preview-data"
44-
class="w-auto mt-5 bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed">
45+
class="w-full mt-5 bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed" disabled>
4546
</div>
4647
</form>
4748

48-
<div id="preview-data-container" class="hidden mt-12 overflow-x-auto">
49-
50-
</div>
51-
52-
<div class="section-controller flex justify-end mt-5 fixed bottom-14 right-8">
53-
<button class="w-auto bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
54-
Next
55-
</button>
56-
</div>
49+
<div id="preview-data-container" class="hidden mt-12 overflow-x-auto"></div>
5750
</section>
5851

5952

60-
<section id="model-selection" class="hidden">
61-
<div class="flex flex-col items-center justify-center space-y-2">
62-
<label for="fruit-select"
63-
class="text-indigo-600 py-2 px-4">
53+
<section id="model-selection-section" class="hidden">
54+
<form id="model-form" class="space-y-2 mt-5">
55+
<label for="model-selection" class="text-indigo-600 text-sm font-medium">
6456
Select Model
6557
</label>
66-
<select id="fruit-select" name="fruit" class="border border-indigo-300 rounded-md py-2 px-4 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
67-
<option value="single">Single feature</option>
58+
<select id="model-selection" name="model" class="w-full bg-white border border-indigo-300 text-indigo-600 rounded-md py-2 px-4 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 hover:bg-gray-50" required>
59+
<option value="single" selected>Single feature</option>
6860
<option value="multi">Multi feature</option>
6961
</select>
70-
</div>
7162

63+
<div id="feature-checkbox-container" class="hidden flex-col items-start space-y-2 mt-5">
64+
<label class="text-indigo-600 text-sm font-medium">Select Features</label>
65+
<div id="feature-checkbox" class="flex flex-col space-y-2">
66+
</div>
67+
</div>
68+
69+
<div id="feature-selection-container" class="block">
70+
<label for="feature-select" class="text-indigo-600 text-sm font-medium">
71+
Select Feature
72+
</label>
73+
<select id="feature-select" name="feature" class="w-full bg-white border border-indigo-300 text-indigo-600 rounded-md py-2 px-4 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 hover:bg-gray-50"></select>
74+
</div>
75+
76+
77+
<div id="label-selection-container">
78+
<label for="label-select" class="text-indigo-600 text-sm font-medium">
79+
Select Label
80+
</label>
81+
<select id="label-select" name="label" class="w-full bg-white border border-indigo-300 text-indigo-600 rounded-md py-2 px-4 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 hover:bg-gray-50" required></select>
82+
</div>
83+
84+
<input type="submit" id="btn-process" value="Process" class="w-full mt-5 bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
85+
</form>
7286

73-
<div class="section-controller flex justify-between mt-5 fixed bottom-14 right-8">
74-
<button class="w-auto bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
75-
Back
76-
</button>
77-
<button class="w-auto bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
78-
Next
79-
</button>
80-
</div>
87+
<div id="equation-result" class="mt-8"></div>
8188
</section>
89+
90+
91+
92+
93+
<nav class="section-controller fixed bottom-0 left-0 w-full flex justify-between bg-indigo-600 p-4 border-t border-indigo-700 lg:px-24">
94+
<button id="btn-prev" class="w-auto bg-white text-indigo-600 py-2 px-4 rounded-md hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 invisible">
95+
Back
96+
</button>
97+
<button id="btn-next" class="w-auto bg-white text-indigo-600 py-2 px-4 rounded-md hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:bg-gray-200 disabled:text-gray-400 disabled:cursor-not-allowed" disabled>
98+
Next
99+
</button>
100+
</nav>
82101
</main>
83102

84103
<script src="script.js"></script>

src/web/script.js

Lines changed: 204 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,66 @@
11
const dataForm = document.getElementById('data-form')
22
const inputFile = document.getElementById('input-file')
33
const fileName = document.getElementById('file-name')
4+
45
const btnPreviewData = document.getElementById('btn-preview-data')
56
const previewData = document.getElementById('preview-data-container')
67

8+
const btnPrev = document.getElementById('btn-prev')
9+
const btnNext = document.getElementById('btn-next')
10+
11+
const sections = document.querySelectorAll('section')
712

813

14+
const modelForm = document.getElementById('model-form')
15+
const modelSelection = document.getElementById('model-selection')
16+
const featureCheckboxContainer = document.getElementById('feature-checkbox-container')
17+
const featureCheckbox = document.getElementById('feature-checkbox')
18+
const featureSelectionContainer = document.getElementById('feature-selection-container')
19+
const featureSelect = document.getElementById('feature-select')
20+
const labelSelectionContainer = document.getElementById('label-selection-container')
21+
const labelSelect = document.getElementById('label-select')
922

23+
const equationResult = document.getElementById('equation-result')
24+
/**
25+
* Linear equation result based on data processing
26+
*
27+
* @param {number} x - a feature data
28+
* @returns {number} a label/target/`y` value result
29+
*/
30+
let equation = null
31+
32+
// Global data
1033
let currentSection = 1
34+
let fileData = null
35+
36+
37+
38+
btnPrev.addEventListener('click', () => {
39+
sections[currentSection - 1].classList.toggle('hidden')
40+
currentSection--
41+
sections[currentSection - 1].classList.toggle('hidden')
42+
if (currentSection == 1) {
43+
btnPrev.classList.toggle('invisible')
44+
}
45+
})
46+
47+
btnNext.addEventListener('click', () => {
48+
sections[currentSection - 1].classList.toggle('hidden')
49+
currentSection++
50+
sections[currentSection - 1].classList.toggle('hidden')
51+
if (currentSection > 1) {
52+
if (btnPrev.classList.contains('invisible')) {
53+
btnPrev.classList.remove('invisible')
54+
}
55+
}
56+
})
57+
58+
59+
1160

61+
//////////////////////////////////////////////////
62+
//// FIRST SECTION SCRIPT
63+
//////////////////////////////////////////////////
1264

1365
dataForm.addEventListener('submit', function(e) {
1466
e.preventDefault()
@@ -28,38 +80,64 @@ dataForm.addEventListener('submit', function(e) {
2880

2981
reader.onload = (e) => {
3082
const fileContent = e.target.result
31-
const rows = fileContent.split(/[\r\n]+/).map(row => row.split(','))
83+
fileData = fileContent
84+
.split(/[\r\n]+/)
85+
.map(row => row.split(','))
86+
.map((val, index, array) => {
87+
if (index == 0) return
88+
89+
const obj = {}
90+
91+
for (let i = 0; i < array[0].length; i++) {
92+
obj[array[0][i]] = val[i]
93+
}
94+
return obj
95+
})
96+
fileData.shift()
97+
3298

3399
let previewedTable = `
34100
<table class="min-w-full bg-white border border-gray-200 rounded-lg">
35101
<thead>
36102
<tr class="bg-gray-100 border-b border-gray-200">
37103
`
38104

39-
for (const item of rows.shift()) {
40-
previewedTable += `<td class="py-2 px-4 text-left text-sm font-medium text-gray-600">${item}</td>`
105+
for (const key in fileData[0]) {
106+
previewedTable += `<td class="py-2 px-4 text-left text-sm font-medium text-gray-600">${key}</td>`
41107
}
42108

43109
previewedTable += `
44110
</tr>
45111
</thead>
112+
<tbody>
46113
`
47114

48-
for (const row of rows) {
115+
for (const row of fileData) {
49116
previewedTable += `<tr class="border-b hover:bg-gray-50">`
50-
for (const item of row) {
117+
for (const item of Object.values(row)) {
51118
previewedTable += `<td class="py-2 px-4 text-sm text-gray-700">${item}</td>`
52119
}
53120
previewedTable += `</tr>`
54-
55121
}
122+
previewedTable += `</tbody>`
56123

57124
previewData.innerHTML = previewedTable
125+
126+
for (const key in fileData[0]) {
127+
featureSelect.innerHTML += `<option value="${key}">${key}</option>`
128+
labelSelect.innerHTML += `<option value="${key}">${key}</option>`
129+
featureCheckbox.innerHTML += `
130+
<label class="flex items-center text-gray-700">
131+
<input type="checkbox" name="features" class="mr-2 leading-tight">
132+
${key}
133+
</label>`
134+
}
58135
}
59136

60137
reader.readAsText(file)
61138

62139
btnPreviewData.setAttribute('disabled', true)
140+
btnNext.removeAttribute('disabled')
63141
this.reset()
64142
})
65143

@@ -72,4 +150,123 @@ inputFile.addEventListener('change', function() {
72150
}
73151

74152
btnPreviewData.removeAttribute('disabled')
75-
})
153+
})
154+
155+
156+
157+
158+
159+
//////////////////////////////////////////////////
160+
//// SECOND SECTION SCRIPT
161+
//////////////////////////////////////////////////
162+
163+
164+
modelSelection.addEventListener('change', function(e) {
165+
const model = e.target.value
166+
167+
switch (model) {
168+
case 'single': showSingleFeatureForm(); break;
169+
case 'multi': showMultiFeatureForm(); break;
170+
default: alert('Invalid model type!'); break;
171+
}
172+
})
173+
174+
175+
function showSingleFeatureForm() {
176+
if (featureCheckboxContainer.classList.contains('flex')) {
177+
featureCheckboxContainer.classList.remove('flex')
178+
featureCheckboxContainer.classList.add('hidden')
179+
180+
featureCheckbox.querySelectorAll('input[type="checkbox"')
181+
.forEach(checkbox => {
182+
checkbox.disabled = true
183+
})
184+
}
185+
186+
featureSelectionContainer.classList.remove('hidden')
187+
featureSelectionContainer.classList.add('block')
188+
featureSelect.disabled = false
189+
190+
labelSelectionContainer.classList.remove('hidden')
191+
}
192+
193+
194+
function showMultiFeatureForm() {
195+
if (featureSelectionContainer.classList.contains('block')) {
196+
featureSelectionContainer.classList.remove('block')
197+
featureSelectionContainer.classList.add('hidden')
198+
featureSelect.disabled = true
199+
}
200+
201+
featureCheckboxContainer.classList.remove('hidden')
202+
featureCheckboxContainer.classList.add('flex')
203+
featureCheckbox.querySelectorAll('input[type="checkbox"')
204+
.forEach(checkbox => {
205+
checkbox.disabled = false
206+
})
207+
208+
labelSelectionContainer.classList.remove('hidden')
209+
}
210+
211+
212+
modelForm.addEventListener('submit', function(e) {
213+
e.preventDefault()
214+
215+
const formData = new FormData(this)
216+
217+
const model = formData.get('model')
218+
const label = formData.get('label')
219+
const feature = formData.get('features') || formData.get('feature')
220+
221+
switch (model) {
222+
case 'single':
223+
singleFeatureProcess(feature, label)
224+
break
225+
case 'multi': multiFeatureProcess(feature, label); break;
226+
default: alert('Invalid model type!'); break;
227+
}
228+
})
229+
230+
231+
/**
232+
* Process linear regression with single feature
233+
*
234+
* Formula
235+
* `y = β0 ​+ β1​x1`
236+
*
237+
* @param {string} feature
238+
* @param {string} label
239+
*/
240+
function singleFeatureProcess(feature, label) {
241+
const sumX = fileData.reduce((sum, val) => sum + parseFloat(val[feature]), 0)
242+
const sumY = fileData.reduce((sum, val) => sum + parseFloat(val[label]), 0)
243+
const sumXY = fileData.reduce((sum, val) => sum + (parseFloat(val[feature]) * parseFloat(val[label])), 0)
244+
const sumXSquared = fileData.reduce((sum, val) => sum + parseFloat(val[feature]) ** 2, 0)
245+
const sumXTimesSumY = sumX * sumY
246+
247+
const b = (
248+
((fileData.length * sumXY) - sumXTimesSumY) /
249+
((fileData.length * sumXSquared) - (sumX ** 2))
250+
)
251+
252+
const a = (sumY / fileData.length) - (b * (sumX / fileData.length))
253+
254+
equation = (x) => a + (b * x)
255+
256+
equationResult.textContent = `Equation result: y = ${a} + ${b}x`
257+
}
258+
259+
260+
/**
261+
* Process linear regression with multi feature
262+
*
263+
* Formula
264+
* `y = β0 ​+ β1​x1 ​+ β2​x2 ​+ ... + βn​xn​`
265+
*
266+
* @param {string[]} features
267+
* @param {string} label
268+
*/
269+
function multiFeatureProcess(features, label) {
270+
console.log('hey its me multi')
271+
console.log(fileData)
272+
}

0 commit comments

Comments
 (0)