Skip to content

Commit

Permalink
Improve data rendering (#97)
Browse files Browse the repository at this point in the history
* Improve CSV class

* Improve data rendering
  • Loading branch information
ishii-norimi committed Mar 13, 2022
1 parent b44c65b commit 02d15e0
Show file tree
Hide file tree
Showing 13 changed files with 551 additions and 484 deletions.
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ <h1><a href="/">AI on Browser</a></h1>
<model-selector />
</div>
<hr />
<div id="render_menu"></div>
<div id="plot-area">
<svg><g class="flip" style="transform: scale(1, -1) translate(0, -100%)"></g></svg>
</div>
Expand Down
99 changes: 8 additions & 91 deletions js/data/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ export class BaseData {
return this.x.length
}

get columnNames() {
const names = []
for (let i = 0; i < this.dimension; i++) {
names[i] = `${i}`
}
return names
}

get x() {
return this._x
}
Expand Down Expand Up @@ -103,10 +111,6 @@ export class BaseData {
return ['TP', 'SM', 'CP'].indexOf(task) >= 0
}

get selectedColumnIndex() {
return this.isSeries ? [Math.min(1, this.dimension - 1)] : this.dimension === 1 ? [0] : [0, 1]
}

get params() {
return {}
}
Expand Down Expand Up @@ -202,93 +206,6 @@ export class MultiDimensionalData extends BaseData {

this._categorical_output = false
this._output_category_names = null

this._select = null
}

get selectedColumnIndex() {
return this._select?.() ?? super.selectedColumnIndex
}

_make_selector(names) {
let e = this.setting.data.configElement.select('div.column-selector')
if (e.size() === 0) {
e = this.setting.data.configElement.append('div').classed('column-selector', true)
} else {
e.selectAll('*').remove()
}
if (this.dimension <= 2) {
this._select = null
} else if (this.dimension <= 4) {
const elm = e.append('table').style('border-collapse', 'collapse')
let row = elm.append('tr').style('text-align', 'center')
row.append('td')
row.append('td').text('>')
row.append('td').text('V').style('transform', 'rotate(180deg')
const ck1 = []
const ck2 = []
for (let i = 0; i < this.dimension; i++) {
row = elm.append('tr')
elm.append('td').text(names[i]).style('text-align', 'right')
const d1 = elm
.append('td')
.append('input')
.attr('type', 'radio')
.attr('name', 'data-d1')
.on('change', () => this._manager.platform.render())
ck1.push(d1)
const d2 = elm
.append('td')
.append('input')
.attr('type', 'radio')
.attr('name', 'data-d2')
.on('change', () => this._manager.platform.render())
ck2.push(d2)
}
ck1[0].property('checked', true)
ck2[1].property('checked', true)
this._select = () => {
const k = []
for (let i = 0; i < this.dimension; i++) {
if (ck1[i].property('checked')) {
k[0] = i
}
if (ck2[i].property('checked')) {
k[1] = i
}
}
return k
}
} else {
names = names.map(v => '' + v)
e.append('span').text('>')
const slct1 = e.append('select').on('change', () => this._manager.platform.render())
slct1
.selectAll('option')
.data(names)
.enter()
.append('option')
.attr('value', d => d)
.text(d => d)
slct1.property('value', names[0])
e.append('span').text('V').style('transform', 'rotate(180deg').style('display', 'inline-block')
const slct2 = e.append('select').on('change', () => this._manager.platform.render())
slct2
.selectAll('option')
.data(names)
.enter()
.append('option')
.attr('value', d => d)
.text(d => d)
slct2.property('value', names[1])
this._select = () => [names.indexOf(slct1.property('value')), names.indexOf(slct2.property('value'))]
}
this._manager.platform.render()
}

terminate() {
this.setting.data.configElement.select('div.column-selector').remove()
super.terminate()
}
}

Expand Down
61 changes: 37 additions & 24 deletions js/data/csv.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
import { FixData } from './base.js'

class CSV {
constructor(data, config) {
constructor(data) {
this._data = data
this._decoder = new TextDecoder('utf-8')
this._config = config

this._delimiter = this._config?.delimiter || ','
}

/**
* @type {Array<Array<string>>}
*/
get data() {
return this._data
}

async load(urlOrFile) {
/**
*
* @param {string | File} urlOrFile
* @param {*} config
* @returns {CSV}
*/
static async load(urlOrFile, config) {
let data
if (urlOrFile instanceof File) {
await new Promise(resolve => {
data = await new Promise(resolve => {
const fr = new FileReader()
fr.onload = () => {
this.loadFromString(fr.result)
resolve()
resolve(fr.result)
}
fr.readAsText(urlOrFile)
})
return
} else {
data = await CSV._fetch(urlOrFile, config)
}
this.loadFromString(await this._fetch(urlOrFile))
const arr = await this.loadFromString(data, config)
return new CSV(arr)
}

loadFromString(str) {
static async loadFromString(str, config) {
const delimiter = config?.delimiter || ','
const data = []
let record = []
let inStr = false
Expand All @@ -45,7 +53,7 @@ class CSV {
}
} else if (str[p] === '"') {
inStr = true
} else if (str.startsWith(this._delimiter, p)) {
} else if (str.startsWith(delimiter, p)) {
record.push(curValue)
curValue = ''
} else if (str[p] === '\n' || str[p] === '\r') {
Expand All @@ -66,10 +74,10 @@ class CSV {
record.push(curValue)
data.push(record)
}
this._data = data
return data
}

async _fetch(url) {
static async _fetch(url, config) {
const response = await fetch(url)
const reader = response.body.getReader()
let { value: chunk, done: readerDone } = await reader.read()
Expand All @@ -86,15 +94,16 @@ class CSV {
}
chunk = pako.ungzip(buf)
}
chunk = chunk ? this._decoder.decode(chunk) : ''
const decoder = new TextDecoder(config?.encoding || 'utf-8')
chunk = chunk ? decoder.decode(chunk) : ''

for (;;) {
if (readerDone) {
break
}
const remainder = chunk
;({ value: chunk, done: readerDone } = await reader.read())
chunk = remainder + (chunk ? this._decoder.decode(chunk) : '')
chunk = remainder + (chunk ? decoder.decode(chunk) : '')
}
return chunk
}
Expand All @@ -112,16 +121,18 @@ export default class CSVData extends FixData {
}
}

readCSV(data, configOrCb, cb) {
const config = typeof configOrCb === 'function' ? null : configOrCb
cb = typeof configOrCb === 'function' ? configOrCb : cb
const csv = new CSV(null, config)
csv.load(data).then(() => cb(csv.data))
get columnNames() {
return this._feature_names || []
}

async readCSV(data, config) {
const csv = await CSV.load(data, config)
return csv.data
}

setCSV(data, infos, header = false) {
if (!Array.isArray(data)) {
this.readCSV(data, d => {
this.readCSV(data).then(d => {
this.setCSV(d, infos, header)
})
return
Expand Down Expand Up @@ -184,6 +195,8 @@ export default class CSVData extends FixData {

this._feature_names = infos.filter(v => !v.out && !v.ignore).map(v => v.name)
this._domain = null
this._make_selector(this._feature_names)
this._manager.onReady(() => {
this._manager.platform.init()
})
}
}
22 changes: 9 additions & 13 deletions js/data/esl.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,19 +203,15 @@ export default class MarketingData extends CSVData {
_readyData() {
const name = this._name
const info = datasetInfos[name]
this.readCSV(
info.file,
{
delimiter: ' ',
},
data => {
if (name === this._name) {
this.setCSV(data, info.info)
this._manager.onReady(() => {
this._manager.platform.render()
})
}
this.readCSV(info.file, {
delimiter: ' ',
}).then(data => {
if (name === this._name) {
this.setCSV(data, info.info)
this._manager.onReady(() => {
this._manager.platform.render()
})
}
)
})
}
}
14 changes: 9 additions & 5 deletions js/data/functional.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,14 @@ export default class FunctionalData extends MultiDimensionalData {
this._createData()
}

get columnNames() {
const axises = []
for (let i = 0; i < this._d; i++) {
axises.push(`x[${i}]`)
}
return axises
}

get series() {
const s = super.series
s.values = this._y.map(v => [v])
Expand Down Expand Up @@ -452,11 +460,7 @@ export default class FunctionalData extends MultiDimensionalData {
}

this._manager.onReady(() => {
const axises = []
for (let i = 0; i < this._d; i++) {
axises.push(`x[${i}]`)
}
this._make_selector(axises)
this._manager.platform.init()
if (this._d === 1) {
const line = d3
.line()
Expand Down
2 changes: 1 addition & 1 deletion js/data/titanic.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default class TitanicData extends CSVData {
}

_readyData() {
this.readCSV('/js/data/csv/titanic.csv.gz', data => {
this.readCSV('/js/data/csv/titanic.csv.gz').then(data => {
this.setCSV(
data,
[
Expand Down
2 changes: 1 addition & 1 deletion js/data/uci.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export default class UCIData extends CSVData {
const info = datasetInfos[this._name]

const name = this._name
this.readCSV(info.file, data => {
this.readCSV(info.file).then(data => {
if (name === this._name) {
this.setCSV(data, info.info)
}
Expand Down
5 changes: 5 additions & 0 deletions js/model_selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,11 @@ Vue.component('model-selector', {
return d3.select('#task_menu')
},
},
render: {
get configElement() {
return d3.select('#render_menu')
},
},
get footer() {
return d3.select('#method_footer')
},
Expand Down
Loading

0 comments on commit 02d15e0

Please sign in to comment.