diff --git a/js/data/camera.js b/js/data/camera.js index f1423e58c..4c64ec2d0 100644 --- a/js/data/camera.js +++ b/js/data/camera.js @@ -1,6 +1,8 @@ -import ImageData from './image.js' +import { BaseData } from './base.js' +import ImageLoader from './loader/image.js' +import ImageRenderer from '../renderer/image.js' -export default class CameraData extends ImageData { +export default class CameraData extends BaseData { constructor(manager) { super(manager) @@ -19,7 +21,7 @@ export default class CameraData extends ImageData { this._slctImg.onchange = () => { this._manager.platform.render() this._thumbnail.replaceChildren() - this._thumbnail.appendChild(this._createCanvas(this.x[0])) + this._thumbnail.appendChild(ImageLoader.createCanvas(this.x[0])) } this._mngelm.appendChild(this._slctImg) @@ -31,6 +33,9 @@ export default class CameraData extends ImageData { this._x = [] this._y = [] + + this._manager.platform._renderer.push(new ImageRenderer(manager)) + this._manager.setting.render.selectItem('image') } get availTask() { @@ -63,7 +68,7 @@ export default class CameraData extends ImageData { this._video.height = this._size[0] this._video.autoplay = true this._video.onclick = () => { - this.readImage(this._video).then(image => { + ImageLoader.load(this._video).then(image => { this._x.push(image) this._y.push(0) const opt = document.createElement('option') @@ -71,7 +76,7 @@ export default class CameraData extends ImageData { this._slctImg.appendChild(opt) this._slctImg.value = this._x.length this._thumbnail.replaceChildren() - this._thumbnail.appendChild(this._createCanvas(image)) + this._thumbnail.appendChild(ImageLoader.createCanvas(image)) this.stopVideo() this._mngelm.style.display = null diff --git a/js/data/capture.js b/js/data/capture.js index ab5558bc9..574feb29e 100644 --- a/js/data/capture.js +++ b/js/data/capture.js @@ -1,6 +1,8 @@ -import ImageData from './image.js' +import { BaseData } from './base.js' +import ImageLoader from './loader/image.js' +import ImageRenderer from '../renderer/image.js' -export default class CaptureData extends ImageData { +export default class CaptureData extends BaseData { constructor(manager) { super(manager) @@ -19,7 +21,7 @@ export default class CaptureData extends ImageData { this._slctImg.onchange = () => { this._manager.platform.render() this._thumbnail.replaceChildren() - this._thumbnail.appendChild(this._createCanvas(this.x[0])) + this._thumbnail.appendChild(ImageLoader.createCanvas(this.x[0])) } this._mngelm.appendChild(this._slctImg) @@ -31,6 +33,9 @@ export default class CaptureData extends ImageData { this._x = [] this._y = [] + + this._manager.platform._renderer.push(new ImageRenderer(manager)) + this._manager.setting.render.selectItem('image') } get availTask() { @@ -57,7 +62,7 @@ export default class CaptureData extends ImageData { this._video.height = this._size[0] this._video.autoplay = true this._video.onclick = () => { - this.readImage(this._video).then(image => { + ImageLoader.load(this._video).then(image => { this._x.push(image) this._y.push(0) const opt = document.createElement('option') @@ -65,7 +70,7 @@ export default class CaptureData extends ImageData { this._slctImg.appendChild(opt) this._slctImg.value = this._x.length this._thumbnail.replaceChildren() - this._thumbnail.appendChild(this._createCanvas(image)) + this._thumbnail.appendChild(ImageLoader.createCanvas(image)) this.stopVideo() this._mngelm.style.display = null diff --git a/js/data/dashboard_estat.js b/js/data/dashboard_estat.js index fff934de0..668ef449e 100644 --- a/js/data/dashboard_estat.js +++ b/js/data/dashboard_estat.js @@ -1,5 +1,6 @@ import BaseDB from './db/base.js' -import JSONData from './json.js' +import { FixData } from './base.js' +import JSONLoader from './loader/json.js' const BASE_URL = 'https://dashboard.e-stat.go.jp/api/1.0' const ExpiredTime = 1000 * 60 * 60 * 24 * 30 @@ -56,7 +57,7 @@ const presetInfos = { const lockKeys = {} -export default class EStatData extends JSONData { +export default class EStatData extends FixData { constructor(manager) { super(manager) this._name = 'Nikkei Indexes' @@ -614,10 +615,12 @@ export default class EStatData extends JSONData { const minyear = date.reduce((y, d) => Math.min(y, d.year), Infinity) this._datetime = date.map(d => (d.year - minyear) * 12 + d.month - 1) - this.setJSON( + const info = columns.map(c => ({ name: c, nan: 0 })) + const json = new JSONLoader( keys.map(k => seldata[k]), - columns.map(c => ({ name: c, nan: 0 })) + { columnInfos: info } ) + this.setArray(json.data, info) this._readySelector() this.setting.ml.refresh() this.setting.$forceUpdate() diff --git a/js/data/esl.js b/js/data/esl.js index aa94ea0e9..e800a76ea 100644 --- a/js/data/esl.js +++ b/js/data/esl.js @@ -1,4 +1,5 @@ -import CSVData from './csv.js' +import { FixData } from './base.js' +import CSV from './loader/csv.js' // https://web.stanford.edu/~hastie/ElemStatLearn/ const datasetInfos = { @@ -150,7 +151,7 @@ const datasetInfos = { }, } -export default class MarketingData extends CSVData { +export default class MarketingData extends FixData { constructor(manager) { super(manager) this._name = 'marketing' @@ -207,11 +208,9 @@ export default class MarketingData extends CSVData { _readyData() { const name = this._name const info = datasetInfos[name] - this.readCSV(info.file, { - delimiter: ' ', - }).then(data => { + CSV.load(info.file, { delimiter: ' ' }).then(csv => { if (name === this._name) { - this.setCSV(data, info.info) + this.setArray(csv.data, info.info) this._manager.onReady(() => { this._manager.platform.render() }) diff --git a/js/data/json.js b/js/data/json.js deleted file mode 100644 index a968cd0f3..000000000 --- a/js/data/json.js +++ /dev/null @@ -1,94 +0,0 @@ -import { FixData } from './base.js' - -class JSONLoader { - constructor(data) { - this._data = data - } - - get data() { - return this._data - } - - async load(urlOrFile) { - if (urlOrFile instanceof File) { - await new Promise(resolve => { - const fr = new FileReader() - fr.onload = () => { - this._data = JSON.parse(fr.result) - resolve() - } - fr.readAsText(urlOrFile) - }) - return - } - const response = await fetch(urlOrFile) - this._data = await response.json() - } -} - -export default class JSONData extends FixData { - constructor(manager, data, columnInfos) { - super(manager) - - if (data && columnInfos) { - this.setJSON(data, columnInfos) - } - } - - async readJSON(data) { - return new Promise(resolve => { - const js = new JSONLoader(null) - js.load(data).then(() => resolve(js.data)) - }) - } - - setJSON(data, infos) { - if (!Array.isArray(data)) { - this.readJSON(data).then(d => { - this.setJSON(d, infos) - }) - return - } - - const columns = [] - if (infos) { - for (let i = 0; i < infos.length; i++) { - columns[i] = infos[i].name - } - } - const x = [] - const iscat = [] - for (let i = 0; i < data.length; i++) { - const xi = [] - for (const key of Object.keys(data[i])) { - let idx = columns.indexOf(key) - if (idx < 0) { - columns.push(key) - idx = columns.length - 1 - iscat[idx] = false - } - xi[idx] = data[i][key] - iscat[idx] ||= isNaN(xi[idx]) - } - if (infos) { - for (let i = 0; i < infos.length; i++) { - if (xi[i] == null) { - if (typeof infos[i].nan === 'number') { - xi[i] = infos[i].nan - } - } - } - } - x[i] = xi - } - - if (!infos) { - infos = columns.map((c, i) => { - return { name: c, type: iscat[i] ? 'category' : 'numeric' } - }) - infos[infos.length - 1].out = true - } - - this.setArray(x, infos) - } -} diff --git a/js/data/audio.js b/js/data/loader/audio.js similarity index 50% rename from js/data/audio.js rename to js/data/loader/audio.js index d78fcb1e9..41100c57e 100644 --- a/js/data/audio.js +++ b/js/data/loader/audio.js @@ -1,19 +1,5 @@ -import { BaseData } from './base.js' - -export default class AudioData extends BaseData { - constructor(manager) { - super(manager) - } - - get availTask() { - return ['SM'] - } - - get domain() { - return [[-1, 1]] - } - - async readAudio(data) { +export default class AudioLoader { + static load(data) { return new Promise(resolve => { const reader = new FileReader() reader.readAsArrayBuffer(data) diff --git a/js/data/csv.js b/js/data/loader/csv.js similarity index 68% rename from js/data/csv.js rename to js/data/loader/csv.js index 3e44f8590..94d3842fb 100644 --- a/js/data/csv.js +++ b/js/data/loader/csv.js @@ -1,19 +1,53 @@ import 'https://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako.min.js' -import { FixData } from './base.js' - -class CSV { - constructor(data) { +export default class CSV { + constructor(data, { header = 0 } = {}) { this._data = data + this._header = header } /** - * @type {Array>} + * @type {Array>} */ get data() { + if (this._header > 0) { + return this._data.slice(this._header) + } return this._data } + /** + * @type {string[]} + */ + get columns() { + if (this._header > 0) { + return this._data[0] + } + return Array.from(this._data[0], (_, i) => i) + } + + /** + * @type {string[]} + */ + get type() { + const data = this.data + return data[0].map((_, i) => { + const cat = data.some(d => isNaN(d[i])) + return cat ? 'category' : 'numeric' + }) + } + + get info() { + const names = this.columns + const types = this.type + const info = [] + for (let i = 0; i < names.length; i++) { + info[i] = { name: names[i], type: types[i] } + } + info[info.length - 1].out = true + return info + } + /** * * @param {string} str @@ -59,14 +93,14 @@ class CSV { record.push(curValue) data.push(record) } - return new CSV(data) + return new CSV(data, config) } /** * * @param {string | File} value * @param {*} [config] - * @returns {CSV} + * @returns {Promise} */ static async load(value, config = {}) { if (typeof value === 'string') { @@ -89,43 +123,3 @@ class CSV { } } } - -export default class CSVData extends FixData { - constructor(manager, data, columnInfos) { - super(manager) - - if (data && columnInfos) { - this.setCSV(data, columnInfos) - } - } - - 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).then(d => { - this.setCSV(d, infos, header) - }) - return - } - if (header) { - const cols = data[0] - data = data.slice(1) - if (!infos) { - infos = cols.map((c, i) => { - const cat = data.some(d => isNaN(d[i])) - return { - name: c, - type: cat ? 'category' : 'numeric', - } - }) - infos[infos.length - 1].out = true - } - } - - this.setArray(data, infos) - } -} diff --git a/js/data/document.js b/js/data/loader/document.js similarity index 87% rename from js/data/document.js rename to js/data/loader/document.js index 09b250f18..d4d4527a6 100644 --- a/js/data/document.js +++ b/js/data/loader/document.js @@ -1,16 +1,10 @@ import 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.min.js' import 'https://cdnjs.cloudflare.com/ajax/libs/encoding-japanese/2.0.0/encoding.min.js' -import { BaseData } from './base.js' - pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.worker.min.js' -export default class DocumentData extends BaseData { - constructor(manager) { - super(manager) - } - - async readDocument(data) { +export default class DocumentLoader { + static load(data) { return new Promise(resolve => { const reader = new FileReader() reader.readAsArrayBuffer(data) @@ -46,11 +40,11 @@ export default class DocumentData extends BaseData { }) } - segment(text) { + static segment(text) { return text.split(/[ -@\[-`{-~\s]+/) } - ordinal(texts, { ignoreCase = true } = {}) { + static ordinal(texts, { ignoreCase = true } = {}) { const words = [] const ord = [] for (const text of texts) { diff --git a/js/data/image.js b/js/data/loader/image.js similarity index 72% rename from js/data/image.js rename to js/data/loader/image.js index d093cc2a2..4d2bcbf4f 100644 --- a/js/data/image.js +++ b/js/data/loader/image.js @@ -1,14 +1,55 @@ -import { BaseData } from './base.js' -import ImageRenderer from '../renderer/image.js' - -export default class ImageData extends BaseData { - constructor(manager) { - super(manager) - - this._manager.platform._renderer.push(new ImageRenderer(manager)) - this._manager.setting.render.selectItem('image') - } +const grayScales = { + AVERAGE: { + name: 'average', + calc: (r, g, b) => (r + g + b) / 3, + }, + AMMA_AVERAGE: { + name: 'Gamma correction average', + calc: (r, g, b) => (r + g + b) / 3, + }, + CIEXYZ: { + name: 'CIE XYZ', + calc: (r, g, b) => 0.2126 * r + 0.7152 * g + 0.0722 * b, + }, + BT601: { + name: 'ITU-R Rec BT.601', + calc: (r, g, b) => 0.299 * r + 0.587 * g + 0.114 * b, + }, + BT601_ROUGH1: { + name: 'Second decimal place of BT.601', + calc: (r, g, b) => 0.3 * r + 0.59 * g + 0.11 * b, + }, + BT601_ROUGH2: { + name: 'First decimal place of BT.601', + calc: (r, g, b) => 0.3 * r + 0.6 * g + 0.1 * b, + }, + BT709: { + name: 'ITU-R Rec BT.709', + calc: (r, g, b) => 0.2126 * r + 0.7152 * g + 0.0722 * b, + }, + YCgCo: { + name: 'Y of YCgCo', + calc: (r, g, b) => r / 4 + g / 2 + b / 4, + }, + HSV: { + name: 'V of HSV', + calc: (r, g, b) => Math.max(r, g, b), + }, + HLS: { + name: 'V of HSV', + calc: (r, g, b) => (Math.max(r, g, b) + Math.min(r, g, b)) / 2, + }, + G: { + name: 'Green', + calc: (r, g, b) => g, + }, + B: { + name: 'Blue', + calc: (r, g, b) => b, + }, +} +export default class ImageLoader { static get colorSpaces() { return { RGB: 'rgb', @@ -20,64 +61,7 @@ export default class ImageData extends BaseData { } } - static get grayScales() { - return { - AVERAGE: { - name: 'average', - calc: (r, g, b) => (r + g + b) / 3, - }, - AMMA_AVERAGE: { - name: 'Gamma correction average', - calc: (r, g, b) => (r + g + b) / 3, - }, - CIEXYZ: { - name: 'CIE XYZ', - calc: (r, g, b) => 0.2126 * r + 0.7152 * g + 0.0722 * b, - }, - BT601: { - name: 'ITU-R Rec BT.601', - calc: (r, g, b) => 0.299 * r + 0.587 * g + 0.114 * b, - }, - BT601_ROUGH1: { - name: 'Second decimal place of BT.601', - calc: (r, g, b) => 0.3 * r + 0.59 * g + 0.11 * b, - }, - BT601_ROUGH2: { - name: 'First decimal place of BT.601', - calc: (r, g, b) => 0.3 * r + 0.6 * g + 0.1 * b, - }, - BT709: { - name: 'ITU-R Rec BT.709', - calc: (r, g, b) => 0.2126 * r + 0.7152 * g + 0.0722 * b, - }, - YCgCo: { - name: 'Y of YCgCo', - calc: (r, g, b) => r / 4 + g / 2 + b / 4, - }, - HSV: { - name: 'V of HSV', - calc: (r, g, b) => Math.max(r, g, b), - }, - HLS: { - name: 'V of HSV', - calc: (r, g, b) => (Math.max(r, g, b) + Math.min(r, g, b)) / 2, - }, - G: { - name: 'Green', - calc: (r, g, b) => g, - }, - B: { - name: 'Blue', - calc: (r, g, b) => b, - }, - } - } - - get availTask() { - return ['SG', 'DN', 'ED'] - } - - async readImage(data) { + static async load(data) { if (data instanceof Blob) { return new Promise(resolve => { const reader = new FileReader() @@ -126,7 +110,7 @@ export default class ImageData extends BaseData { } } - _reduce(im, step, algorithm = 'mean') { + static reduce(im, step, algorithm = 'mean') { const x = [] const d = im[0][0].length let f = null @@ -161,7 +145,7 @@ export default class ImageData extends BaseData { return x } - _convertSpace(data, space, normalize = false, binary_threshold = 180) { + static _convertSpace(data, space, normalize = false, binary_threshold = 180) { const [r, g, b, a] = data if (space === 'rgb') { if (normalize) { @@ -179,14 +163,14 @@ export default class ImageData extends BaseData { return [br, bg, bb] } } else if (space === 'gray') { - const v = ImageData.grayScales.BT709.calc(r, g, b) + const v = grayScales.BT709.calc(r, g, b) if (normalize) { return [v / 255] } else { return [v] } } else if (space === 'binary') { - let v = ImageData.grayScales.BT709.calc(r, g, b) + let v = grayScales.BT709.calc(r, g, b) v = v < binary_threshold ? 0 : 255 if (normalize) { return [v / 255] @@ -236,18 +220,18 @@ export default class ImageData extends BaseData { } } - _applySpace(data, space, normalize = false, binary_threshold = 180) { + static applySpace(data, space, normalize = false, binary_threshold = 180) { const cp = [] for (let i = 0; i < data.length; i++) { cp[i] = [] for (let j = 0; j < data[i].length; j++) { - cp[i][j] = this._convertSpace(data[i][j], space, normalize, binary_threshold) + cp[i][j] = ImageLoader._convertSpace(data[i][j], space, normalize, binary_threshold) } } return cp } - _createCanvas(data, width = 80, height = null) { + static createCanvas(data, width = 80, height = null) { const orgwidth = data[0].length const orgheight = data.length if (!height) { diff --git a/js/data/loader/json.js b/js/data/loader/json.js new file mode 100644 index 000000000..dd8ede1e7 --- /dev/null +++ b/js/data/loader/json.js @@ -0,0 +1,71 @@ +export default class JSONLoader { + constructor(json, { columnInfos } = {}) { + this._json = json + this._columnInfos = columnInfos + + const columns = [] + if (this._columnInfos) { + for (let i = 0; i < this._columnInfos.length; i++) { + columns[i] = this._columnInfos[i].name + } + } + this._data = [] + const iscat = [] + for (let i = 0; i < this._json.length; i++) { + const xi = [] + for (const key of Object.keys(this._json[i])) { + let idx = columns.indexOf(key) + if (idx < 0) { + columns.push(key) + idx = columns.length - 1 + iscat[idx] = false + } + xi[idx] = this._json[i][key] + iscat[idx] ||= isNaN(xi[idx]) + } + if (this._columnInfos) { + for (let i = 0; i < this._columnInfos.length; i++) { + if (xi[i] == null) { + if (typeof this._columnInfos[i].nan === 'number') { + xi[i] = this._columnInfos[i].nan + } + } + } + } + this._data[i] = xi + } + this._info = columns.map((c, i) => ({ name: c, type: iscat[i] ? 'category' : 'numeric' })) + this._info[this._info.length - 1].out = true + } + + get json() { + return this._json + } + + get data() { + return this._data + } + + get info() { + return this._info + } + + /** + * @param {string | File} urlOrFile + * @param {*} [config] + * @returns {Promise} + */ + static async load(urlOrFile, config) { + if (urlOrFile instanceof File) { + return new Promise(resolve => { + const fr = new FileReader() + fr.onload = () => { + resolve(new JSONLoader(JSON.parse(fr.result), config)) + } + fr.readAsText(urlOrFile) + }) + } + const response = await fetch(urlOrFile) + return new JSONLoader(await response.json(), config) + } +} diff --git a/js/data/microphone.js b/js/data/microphone.js index 8e9a8bc9b..2f5822153 100644 --- a/js/data/microphone.js +++ b/js/data/microphone.js @@ -1,6 +1,7 @@ -import AudioData from './audio.js' +import { BaseData } from './base.js' +import AudioLoader from './loader/audio.js' -export default class MicrophoneData extends AudioData { +export default class MicrophoneData extends BaseData { constructor(manager) { super(manager) @@ -138,7 +139,7 @@ export default class MicrophoneData extends AudioData { }) mediaRecorder.addEventListener('stop', () => { const blob = new Blob(chunks) - this.readAudio(blob).then(buf => { + AudioLoader.load(blob).then(buf => { this._x.push(Array.from(buf.getChannelData(0))) this._y.push(0) this._audioDatas.push({ blob, buff: buf }) diff --git a/js/data/poke.js b/js/data/poke.js index ed0a3715d..c05af1151 100644 --- a/js/data/poke.js +++ b/js/data/poke.js @@ -1,5 +1,6 @@ import BaseDB from './db/base.js' -import JSONData from './json.js' +import { FixData } from './base.js' +import JSONLoader from './loader/json.js' const BASE_URL = 'https://pokeapi.co/api/v2' const DB_NAME = 'poke_api' @@ -18,7 +19,7 @@ class PokeDB extends BaseDB { } } -export default class PokeData extends JSONData { +export default class PokeData extends FixData { // https://pokeapi.co/ constructor(manager) { super(manager) @@ -147,7 +148,10 @@ export default class PokeData extends JSONData { this._target = 1 this._names = localData.map(v => v.name) - this.setJSON( + const info = ['height', 'weight', 'hp', 'attack', 'defense', 'special-attack', 'special-defense', 'speed'].map( + c => ({ name: c, nan: 0 }) + ) + const json = new JSONLoader( localData.map(v => { const data = { height: v.height, weight: v.weight } for (const stat of v.stats) { @@ -155,11 +159,9 @@ export default class PokeData extends JSONData { } return data }), - ['height', 'weight', 'hp', 'attack', 'defense', 'special-attack', 'special-defense', 'speed'].map(c => ({ - name: c, - nan: 0, - })) + { columnInfos: info } ) + this.setArray(json.data, info) this._readySelector() } diff --git a/js/data/statlib.js b/js/data/statlib.js index 8c3525d06..b0fc868c0 100644 --- a/js/data/statlib.js +++ b/js/data/statlib.js @@ -1,4 +1,5 @@ -import CSVData from './csv.js' +import { FixData } from './base.js' +import CSV from './loader/csv.js' // http://lib.stat.cmu.edu/datasets/ const datasetInfos = { @@ -39,7 +40,7 @@ const datasetInfos = { }, } -export default class MarketingData extends CSVData { +export default class MarketingData extends FixData { constructor(manager) { super(manager) this._name = 'boston' @@ -101,10 +102,10 @@ export default class MarketingData extends CSVData { _readyData() { const name = this._name const info = datasetInfos[name] - this.readCSV(info.file, { delimiter: ',' }).then(data => { + CSV.load(info.file, { delimiter: ',' }).then(csv => { if (name === this._name) { this._credit.innerText = info.credit - this.setCSV(data, info.info) + this.setArray(csv.data, info.info) this._manager.onReady(() => { this._manager.platform.render() }) diff --git a/js/data/text.js b/js/data/text.js index b5f52ff6a..bbb5cedff 100644 --- a/js/data/text.js +++ b/js/data/text.js @@ -1,7 +1,8 @@ import BaseDB from './db/base.js' -import DocumentData from './document.js' +import { BaseData } from './base.js' +import DocumentLoader from './loader/document.js' -export default class TextData extends DocumentData { +export default class TextData extends BaseData { constructor(manager) { super(manager) const elm = this.setting.data.configElement @@ -32,7 +33,7 @@ export default class TextData extends DocumentData { title.value = 'Artificial intelligence' title.onchange = async () => { textarea.value = await WikipediaPreset.getText(title.value) - this._x = [this.segment(textarea.value)] + this._x = [DocumentLoader.segment(textarea.value)] this.setting.pushHistory() } titleelm.append('Title', title) @@ -43,7 +44,7 @@ export default class TextData extends DocumentData { randomButton.disabled = true title.value = await WikipediaPreset.getRandom() textarea.value = await WikipediaPreset.getText(title.value) - this._x = [this.segment(textarea.value)] + this._x = [DocumentLoader.segment(textarea.value)] randomButton.disabled = false } titleelm.appendChild(randomButton) @@ -61,16 +62,16 @@ export default class TextData extends DocumentData { textarea.classList.add('data-upload') textarea.value = '' textarea.onchange = () => { - this._x = [this.segment(textarea.value)] + this._x = [DocumentLoader.segment(textarea.value)] this._y = [0] } elm.appendChild(textarea) - this._x = [this.segment(textarea.value)] + this._x = [DocumentLoader.segment(textarea.value)] this._y = [0] WikipediaPreset.getText(title.value).then(text => { textarea.value = text - this._x = [this.segment(textarea.value)] + this._x = [DocumentLoader.segment(textarea.value)] }) } diff --git a/js/data/titanic.js b/js/data/titanic.js index 9186c5a47..4de3975bc 100644 --- a/js/data/titanic.js +++ b/js/data/titanic.js @@ -1,6 +1,7 @@ -import CSVData from './csv.js' +import { FixData } from './base.js' +import CSV from './loader/csv.js' -export default class TitanicData extends CSVData { +export default class TitanicData extends FixData { // https://www.openml.org/d/40945 // Simonoff, Jeffrey S (1997): The "unusual episode" and a second statistics course. J Statistics Education, Vol. 5 No. 1. constructor(manager) { @@ -14,27 +15,23 @@ export default class TitanicData extends CSVData { } _readyData() { - this.readCSV('/js/data/csv/titanic.csv.gz').then(data => { - this.setCSV( - data, - [ - { name: 'pclass', type: 'numeric' }, - { name: 'survived', type: 'category' }, - { name: 'name', type: 'category' }, - { name: 'sex', type: 'category' }, - { name: 'age', type: 'numeric' }, - { name: 'sibsp', type: 'numeric' }, - { name: 'parch', type: 'numeric' }, - { name: 'ticket', type: 'category' }, - { name: 'fare', type: 'numeric' }, - { name: 'cabin', type: 'category' }, - { name: 'embarked', type: 'category' }, - { name: 'boat', type: 'category' }, - { name: 'body', type: 'numeric' }, - { name: 'home.dest', type: 'category' }, - ], - true - ) + CSV.load('/js/data/csv/titanic.csv.gz', { header: 1 }).then(csv => { + this.setArray(csv.data, [ + { name: 'pclass', type: 'numeric' }, + { name: 'survived', type: 'category' }, + { name: 'name', type: 'category' }, + { name: 'sex', type: 'category' }, + { name: 'age', type: 'numeric' }, + { name: 'sibsp', type: 'numeric' }, + { name: 'parch', type: 'numeric' }, + { name: 'ticket', type: 'category' }, + { name: 'fare', type: 'numeric' }, + { name: 'cabin', type: 'category' }, + { name: 'embarked', type: 'category' }, + { name: 'boat', type: 'category' }, + { name: 'body', type: 'numeric' }, + { name: 'home.dest', type: 'category' }, + ]) this._manager.onReady(() => { this._manager.platform.render() }) diff --git a/js/data/uci.js b/js/data/uci.js index ad4943d4d..67055559b 100644 --- a/js/data/uci.js +++ b/js/data/uci.js @@ -1,4 +1,5 @@ -import CSVData from './csv.js' +import { FixData } from './base.js' +import CSV from './loader/csv.js' // https://archive.ics.uci.edu/ml/index.php const datasetInfos = { @@ -67,7 +68,7 @@ const datasetInfos = { }, } -export default class UCIData extends CSVData { +export default class UCIData extends FixData { constructor(manager) { super(manager) this._name = 'iris' @@ -85,7 +86,11 @@ export default class UCIData extends CSVData { datanames.onchange = () => { this._name = datanames.value const info = datasetInfos[this._name] - this.setCSV(info.file, info.info) + CSV.load(info.file).then(csv => { + if (name === this._name) { + this.setArray(csv.data, info.info) + } + }) this.setting.pushHistory() } for (const d of Object.keys(datasetInfos)) { @@ -105,9 +110,9 @@ export default class UCIData extends CSVData { const info = datasetInfos[this._name] const name = this._name - this.readCSV(info.file).then(data => { + CSV.load(info.file).then(csv => { if (name === this._name) { - this.setCSV(data, info.info) + this.setArray(csv.data, info.info) } }) } @@ -123,10 +128,14 @@ export default class UCIData extends CSVData { set params(params) { if (params.dataname && Object.keys(datasetInfos).includes(params.dataname)) { const elm = this.setting.data.configElement - this._name = params.dataname + const name = (this._name = params.dataname) elm.querySelector('[name=name]').value = params.dataname const info = datasetInfos[this._name] - this.setCSV(info.file, info.info) + CSV.load(info.file).then(csv => { + if (name === this._name) { + this.setArray(csv.data, info.info) + } + }) } } } diff --git a/js/data/upload.js b/js/data/upload.js index 2ce007779..80bb232f2 100644 --- a/js/data/upload.js +++ b/js/data/upload.js @@ -1,9 +1,9 @@ -import { BaseData } from './base.js' -import CSVData from './csv.js' -import JSONData from './json.js' -import ImageData from './image.js' -import AudioData from './audio.js' -import DocumentData from './document.js' +import { BaseData, FixData } from './base.js' +import AudioLoader from './loader/audio.js' +import DocumentLoader from './loader/document.js' +import CSV from './loader/csv.js' +import ImageLoader from './loader/image.js' +import JSONLoader from './loader/json.js' export default class UploadData extends BaseData { constructor(manager) { @@ -52,7 +52,7 @@ export default class UploadData extends BaseData { } } - loadFile(file) { + async loadFile(file) { this._file = file this._filetype = null if (file.type.startsWith('image/')) { @@ -80,37 +80,33 @@ export default class UploadData extends BaseData { } if (this._filetype === 'image') { - UploadData.prototype.__proto__ = ImageData.prototype - UploadData.__proto__ = ImageData - this.readImage(file).then(data => { - this._x = [data] - this._y = [0] - this._manager.platform.render && this._manager.platform.render() - }) + UploadData.prototype.__proto__ = BaseData.prototype + UploadData.__proto__ = BaseData + const data = await ImageLoader.load(file) + this._x = [data] + this._y = [0] } else if (this._filetype === 'audio' || this._filetype === 'video') { - UploadData.prototype.__proto__ = AudioData.prototype - UploadData.__proto__ = AudioData - this.readAudio(file).then(buf => { - this._x = Array.from(buf.getChannelData(0)).map(v => [v]) - this._y = Array(this._x.length).fill(0) - this._manager.platform.render && this._manager.platform.render() - }) + UploadData.prototype.__proto__ = BaseData.prototype + UploadData.__proto__ = BaseData + const buf = await AudioLoader.load(file) + this._x = Array.from(buf.getChannelData(0)).map(v => [v]) + this._y = Array(this._x.length).fill(0) } else if (this._filetype === 'text') { - UploadData.prototype.__proto__ = DocumentData.prototype - UploadData.__proto__ = DocumentData - this.readDocument(file).then(data => { - this._x = [this.segment(data)] - this._y = [0] - this._manager.platform.render && this._manager.platform.render() - }) + UploadData.prototype.__proto__ = BaseData.prototype + UploadData.__proto__ = BaseData + const data = await DocumentLoader.load(file) + this._x = [DocumentLoader.segment(data)] + this._y = [0] } else if (this._filetype === 'json') { - UploadData.prototype.__proto__ = JSONData.prototype - UploadData.__proto__ = JSONData - this.setJSON(file) + UploadData.prototype.__proto__ = FixData.prototype + UploadData.__proto__ = FixData + const json = await JSONLoader.load(file) + this.setArray(json.data, json.info) } else { - UploadData.prototype.__proto__ = CSVData.prototype - UploadData.__proto__ = CSVData - this.setCSV(file, null, true) + UploadData.prototype.__proto__ = FixData.prototype + UploadData.__proto__ = FixData + const csv = await CSV.load(file, { header: 1 }) + this.setArray(csv.data, csv.info) } this.setting.ml.refresh() this._manager.setTask('') diff --git a/js/platform/image.js b/js/platform/image.js index 8235662ad..a7c505a62 100644 --- a/js/platform/image.js +++ b/js/platform/image.js @@ -1,6 +1,6 @@ import { BasePlatform } from './base.js' -import ImageData from '../data/image.js' import ImageRenderer from '../renderer/image.js' +import ImageLoader from '../data/loader/image.js' export default class ImagePlatform extends BasePlatform { constructor(manager) { @@ -25,7 +25,7 @@ export default class ImagePlatform extends BasePlatform { threshold.style.display = this._color_space === 'binary' ? null : 'none' this.render() } - for (const cs of Object.keys(ImageData.colorSpaces).map(k => ImageData.colorSpaces[k])) { + for (const cs of Object.values(ImageLoader.colorSpaces)) { const opt = document.createElement('option') opt.value = cs opt.innerText = cs @@ -56,8 +56,8 @@ export default class ImagePlatform extends BasePlatform { get trainInput() { const data = this.datas.x[0] - let x = this.datas._applySpace( - this.datas._reduce(data, this._step, this._reduce_algorithm), + let x = ImageLoader.applySpace( + ImageLoader.reduce(data, this._step, this._reduce_algorithm), this._color_space, this._normalize, this._binary_threshold @@ -75,7 +75,7 @@ export default class ImagePlatform extends BasePlatform { testInput(step = 8) { const data = this.datas.x[0] - let x = this.datas._reduce(data, step, this._reduce_algorithm) + let x = ImageLoader.reduce(data, step, this._reduce_algorithm) for (const preprocess of this._manager.preprocesses) { x = preprocess.apply(x, { dofit: false }) } @@ -88,7 +88,7 @@ export default class ImagePlatform extends BasePlatform { } } } - const sx = this.datas._applySpace(x, this._color_space, this._normalize, this._binary_threshold) + const sx = ImageLoader.applySpace(x, this._color_space, this._normalize, this._binary_threshold) this.__pred = sx this.__pred_x = x this.__pred_step = step diff --git a/js/renderer/document.js b/js/renderer/document.js index ae84e17f9..ab205c151 100644 --- a/js/renderer/document.js +++ b/js/renderer/document.js @@ -1,4 +1,5 @@ import BaseRenderer from './base.js' +import DocumentLoader from '../data/loader/document.js' export default class DocumentScatterRenderer extends BaseRenderer { constructor(manager) { @@ -67,7 +68,7 @@ export default class DocumentScatterRenderer extends BaseRenderer { testData() { const x = this.datas.x[0] - const [words, idxs] = this.datas.ordinal(x) + const [words, idxs] = DocumentLoader.ordinal(x) return words } diff --git a/js/renderer/image.js b/js/renderer/image.js index 04dbae03a..a24423071 100644 --- a/js/renderer/image.js +++ b/js/renderer/image.js @@ -1,5 +1,6 @@ import BaseRenderer from './base.js' import { specialCategory, getCategoryColor } from '../utils.js' +import ImageLoader from '../data/loader/image.js' export default class ImageRenderer extends BaseRenderer { constructor(manager) { @@ -67,7 +68,7 @@ export default class ImageRenderer extends BaseRenderer { const data = this.datas.x[0] const x = this._manager.platform._color_space - ? this.datas._applySpace( + ? ImageLoader.applySpace( data, this._manager.platform._color_space, this._manager.platform._normalize,