Agent API (window.midas)
A JavaScript API for controlling MIDAS programmatically from AI agents, Playwright, or other external tools.
- Overview
- Usage
- Response Format
- Data Model
- Method Reference: status / project / datasets / enums / tabs / models / reports / layout
- Error Codes
Overview
window.midas provides access to MIDAS features from the browser DevTools console or automation tools like Playwright. Use it to manage datasets, tabs, statistical models, reports, and layout.
Availability
- Project screen: All methods are available
- Launcher screen:
help()and the project open/create methods (project.openFile(),project.openUrl(),project.createFromCsv(),project.createFromCsvUrl(),project.openSample(),project.listSamples(),project.createEmpty()) are available. Other methods return aNO_PROJECTerror
When automating with Playwright, open or create a project with one of these methods first, then use the other API methods.
Quick Start for AI Agents
- Call
status()to check the current project state (datasets, tabs, models) - Call
datasets.list()anddatasets.describe(id)to understand the available data - Read Data Model to understand persistence, dataset types, and graph specification
- Refer to Method Reference for the specific operation you need
Usage
From DevTools Console
Open the browser DevTools and call methods directly in the console.
// Check project status
const result = await window.midas.status();
console.log(result.data);
// { datasets: 3, tabs: 2, models: 1, ... }
From Playwright
const result = await page.evaluate(async () => {
return await window.midas.datasets.list();
});
console.log(result.data);
// [{ id: '...', name: 'Iris', rows: 150, columns: 5, type: 'primary' }, ...]
Viewing Help
Call help() to see the list of available method names. Pass a method name to get the detail of that method: description (purpose and behavior), signature, params (one entry per argument; nested arguments are written with dots, such as options.limit), returns (the structure of the result), errors (failures that return a specific error code, with the condition for each), and example. The returned object also includes a documentation field with the URL of this page.
const help = window.midas.help();
console.log(help.methods); // ["help(methodName?)", "status()", ...]
console.log(help.documentation); // "https://midas-app.org/docs/en/agent-api.md"
const detail = window.midas.help('datasets.query');
console.log(detail.signature);
console.log(detail.params); // [{ name: "sql", description: "..." }, { name: "options.limit", ... }, ...]
console.log(detail.errors); // [{ code: "SQL_PARSE_ERROR", when: "..." }, ...]
Response Format
All methods except help() are async and return a unified APIResult<T> response. help() is synchronous and directly returns the method name index, or the detail of the method you name. A violation of an internal MIDAS invariant (an assertion failure) is a defect in MIDAS rather than an input error, so the method rejects the Promise with the exception instead of returning a failure response.
// Success
{
success: true,
message: "Found 3 datasets",
data: [...]
}
// Failure
{
success: false,
message: "Dataset not found",
error: {
code: "DATASET_NOT_FOUND",
message: "No dataset with ID 'abc'",
suggestion: "Use datasets.list() to see available datasets"
}
}
A warnings field may be included when the operation succeeds but there are points to note. For example, models.run() stores data preparation warnings in the top-level warnings and model execution warnings in data.warnings.
The datasets.* methods that create a dataset report in warnings, per column, how many values became null while the dataset was evaluated. This covers values that failed type conversion, dates outside the representable range (years 1 to 9999), integers outside the 64-bit integer range, and non-finite values and empty strings returned by a computation. Non-finite values include ±Infinity from division by zero and NaN from . MIDAS stores non-finite values and empty strings as missing values rather than keeping them as data. The same text also appears as an on-screen notification.
// Example response with warnings
{
success: true,
message: "Model run completed",
warnings: ["3 rows with missing values were excluded from analysis"],
data: {
runId: '...',
warnings: ["Convergence achieved but Hessian is nearly singular"],
...
}
}
Data Model
Understanding how MIDAS manages data helps you use the API effectively.
Persistence
API operations modify the in-memory project state. Nothing is written to browser storage until you call project.save(). If you reload the page without saving, all changes from that session are lost.
The methods that create a new project are an exception. project.createFromCsv(), project.createFromCsvUrl(), project.openSample(), and project.createEmpty() write the new project to browser storage automatically. The write starts after the project opens, so it may not be finished when the method returns. Call project.save() afterwards when you need to confirm the write before moving on.
// Modify a dataset's schema
await window.midas.datasets.setColumnSchema('ds_001', { ... });
// At this point, the change exists only in memory
await window.midas.project.save();
// Now written to browser storage
Dataset Types
datasets.list() returns two types of datasets.
Primary — Original data imported from CSV or other files. The data itself is stored in the project file.
Derived — Data created by a transformation such as SQL or cross-tabulation. The project file stores the operation definition (e.g., which SQL was executed) rather than the data itself. The data is a cache and is not included in the project file by default; in that case, derived datasets are recomputed by re-running their operation after the project is opened, and those that reference a parent reflect the current parent data. For the setting that saves the computed data in the project file, see Datasets. Change this setting with datasets.setSaveDataWithProject(). Use parentIds to inspect dependency relationships.
const result = await window.midas.datasets.list();
// [
// { id: 'ds_001', name: 'Sales', type: 'primary', ... },
// { id: 'derived_001', name: 'Monthly Total', type: 'derived', parentIds: ['ds_001'], ... }
// ]
MIDAS also creates temporary Ephemeral Datasets for internal rendering, but these are not included in datasets.list(). Passing an ephemeral dataset by its ID to an API that takes a dataset (such as models.run, datasets.describe/fetch, tabs.open/setDataset, and reports.addDataTable/addGraph) returns an INVALID_INPUT error.
Report Element Lifecycle
A report consists of two parts: Markdown text called content, and elements such as graphs and model summaries. Writing a reference like {{graph_builder:element_001}} in the content renders the corresponding element at that position.
reports.addGraph() and reports.addModelSummary() create an element and insert a reference into the content in one step. reports.removeElement() removes both the element and its content reference.
When a model or dataset is deleted, any report elements that depend on it are automatically removed. No manual cleanup is needed.
Graph Specification
Custom Graph configuration has two levels, the graph level and the layer level. In multi-panel mode a panel level sits between them.
Graph level — Settings that apply to the entire graph: data source, coordinate system (coordinates), faceting (facets), axis scales (scales), and the brush selection direction (brush). Specified via tabs.configureGraph() or reports.addGraph().
Panel level — Present only in multi-panel mode (panels). Panels are stacked vertically and share the X axis, and each panel has its own layers (layers), Y axis scale (yScale), relative height (heightRatio), and name (name). In multi-panel mode only the layers of each panel are rendered; the graph-level layers and facets are not used. See panels under tabs.configureGraph() for how to set it up.
Layer level — A drawing unit that combines a geometric element (geom: points, lines, bars, etc.), statistical transformations (stats), and aesthetic mapping (aes: which columns map to the x-axis, color, size, etc.). Multiple layers can be stacked on a single graph. Add layers with tabs.addGraphLayer(). Each layer can configure its own scale for each aesthetic via scales.
The graph-level globalAes serves as the default for all layers, and each layer's aes can override it.
For full configuration details, see Custom Graph and Custom Graph Reference.
Method Reference
status()
Get the current project status.
const result = await window.midas.status();
// result.data:
// {
// datasets: 3,
// derivedDatasets: 1,
// tabs: 2,
// models: 1,
// reports: 1,
// activeDatasetId: 'ds_001',
// activeTabId: 'tab_001'
// }
activeTabId is the tab at the front of the active pane. It is null when that pane holds no tabs.
project
project.save()
Save the project to browser storage. See Privacy and Security for details on storage.
await window.midas.project.save();
Returns a SANDBOX_MODE error in sandbox mode (projects where persistence is disabled, such as demos or trials). Saving writes the row data with DuckDB into the Parquet files of the MDS File Format, so it returns an INTERNAL_ERROR error while DuckDB cannot start.
project.exportMds()
Export the project as an MDS (MIDAS project file format) binary. The exported data is returned as an ArrayBuffer. Exporting requires a signing key. If no key is configured, this returns a NO_SIGNING_KEY error; create a key under Settings > Signing Keys. Exporting with a new signature writes the row data as Parquet files with DuckDB, so it returns an INTERNAL_ERROR error while DuckDB cannot start. Exporting an unedited file as the original does not write Parquet files, so this error does not occur.
const result = await window.midas.project.exportMds();
// result.data: { data: ArrayBuffer, size: 12345, suggestedFilename: 'MyProject.mds' }
project.downloadMds()
Download the project as an MDS file through the browser. Like exportMds(), this returns a NO_SIGNING_KEY error if no signing key is configured, and an INTERNAL_ERROR error if the project needs signing and DuckDB cannot start.
const result = await window.midas.project.downloadMds();
// result.data: { filename: 'MyProject.mds' }
project.openFile(data, options?)
Open a project from MDS binary data (Uint8Array). Also available on the launcher screen.
const buf = await fetch('/project.mds').then(r => r.arrayBuffer());
const result = await window.midas.project.openFile(new Uint8Array(buf));
// result.data: { projectId: 'project-xxx' }
Options:
sandbox(boolean, default:false) — Whentrue, assigns a new ID and skips saving to browser storageonDuplicate('overwrite'|'copy', default:'overwrite') — Behavior when a project with the same ID already exists in browser storage.'overwrite'replaces the existing project;'copy'assigns a new ID only when a duplicate exists
If the current project has unsaved changes, a confirmation dialog is shown. Returns USER_CANCELLED if the user declines. Signature warnings are not shown as a dialog; they are reported via the warnings array in the response.
project.openUrl(url, options?)
Fetch an MDS file from a URL and open it. Always opens in sandbox mode (not saved to browser storage). Also available on the launcher screen.
const result = await window.midas.project.openUrl('https://example.com/project.mds');
// result.data: { projectId: 'project-xxx' }
Options:
signal(AbortSignal) — Used to abort the fetch
If the current project has unsaved changes, a confirmation dialog is shown. Returns USER_CANCELLED if the user declines. Signature warnings are not shown as a dialog; they are reported via the warnings array in the response.
The same URL validation and security restrictions as datasets.importFromURL() apply. Only HTTP/HTTPS protocols are allowed, and access to cloud metadata endpoints is blocked. Blocked URLs return an INVALID_INPUT error. Warnings are included in warnings for URLs not in the trusted URL list; if "Block connections to untrusted domains" is enabled in settings, untrusted URLs result in an error. Files exceeding the size warning threshold (default 10 MB, configurable in Settings) are still opened, but a size warning is included in warnings. See Privacy and Security for details. Returns FETCH_ERROR on network failures, timeouts, and aborting via signal.
project.createFromCsv(data, options?)
Create a new project from CSV/TSV binary data (ArrayBuffer or a TypedArray such as Uint8Array). The data is parsed, column types are detected, and the project is opened. Saving to browser storage happens after the project opens, so the save may still be in progress when this method returns. Also available on the launcher screen.
const csv = new TextEncoder().encode('x,y\n1,2\n3,4');
const result = await window.midas.project.createFromCsv(csv, { name: 'My Data' });
// result.data: { projectId: 'project-xxx', datasetId: 'dataset_...', sourceDatasetId: 'dataset_...' }
The returned datasetId is the ID of the created dataset. It has the same meaning as id in datasets.importFromBuffer(): when a type-converted dataset is created, it points to that dataset. Only when a type-converted dataset is created, sourceDatasetId holds the ID of the original all-string dataset.
Options:
name(string, default:"Untitled") — Dataset name. Also becomes the project namehasHeader(boolean, default:true) — Whether the first row is a headerencoding("utf-8"|"shift_jis"|"euc-jp") — Character encoding. When omitted, encoding is auto-detected from the byte sequencedelimiter(","|"\t"|";"|"|", default:",") — Delimiter. Pass"\t"for TSV data
If the current project has unsaved changes, a confirmation dialog is shown. Returns USER_CANCELLED if the user declines.
project.createFromCsvUrl(url, options?)
Fetch a CSV/TSV file from a URL and create a new project from it. Saving to browser storage happens after the project opens, so the save may still be in progress when this method returns. Also available on the launcher screen.
const result = await window.midas.project.createFromCsvUrl('https://example.com/data.csv');
// result.data: { projectId: 'project-xxx', datasetId: 'dataset_...', sourceDatasetId: 'dataset_...' }
The returned datasetId and sourceDatasetId have the same meaning as in project.createFromCsv().
Options:
name(string) — Dataset name. When omitted, the name is inferred from the URL. Also becomes the project namehasHeader(boolean, default:true) — Whether the first row is a headerencoding("utf-8"|"shift_jis"|"euc-jp") — Character encoding. When omitted, encoding is auto-detected from the Content-Type charset and the byte sequencedelimiter(","|"\t"|";"|"|") — Delimiter. When omitted, it follows the file name extension in the URL: tab for.tsvand.txt, comma otherwisesignal(AbortSignal) — Used to abort the fetch
If the current project has unsaved changes, a confirmation dialog is shown. Returns USER_CANCELLED if the user declines. The same URL validation and security restrictions as datasets.importFromURL() apply. Returns FETCH_ERROR on network failures, timeouts, and aborting via signal.
project.openSample(sampleId)
Create a new project from a built-in sample dataset. The project is named Sample: {name} and opened. Saving to browser storage happens after the project opens, so the save may still be in progress when this method returns. Also available on the launcher screen.
const result = await window.midas.project.openSample('penguins');
// result.data: { projectId: 'project-xxx' }
Unknown sample ids return an INVALID_INPUT error listing the valid ids. If the current project has unsaved changes, a confirmation dialog is shown. Returns USER_CANCELLED if the user declines, and FETCH_ERROR when fetching the sample data fails.
project.listSamples()
List the built-in sample datasets available to project.openSample(). Also available on the launcher screen.
const result = await window.midas.project.listSamples();
// result.data: { samples: [{ id: 'penguins', name: 'Palmer Penguins', rows: 344, columns: 8,
// purpose: 'Classification, visualization', recommended: true }, ...] }
project.createEmpty(name?)
Create a new project with no datasets and open it on the Project Overview tab. This is the same path as New Empty Project on the launcher. Add datasets afterwards with datasets.create(), datasets.generateSynthetic(), datasets.importFromBuffer(), or datasets.importFromURL(). Saving to browser storage happens after the project opens, so the save may still be in progress when this method returns. Also available on the launcher screen.
const result = await window.midas.project.createEmpty('Design Study');
// result.data: { projectId: 'project-xxx' }
The name argument (string, default: "Untitled Project") is the project name. A non-string or empty value returns INVALID_INPUT. If the current project has unsaved changes, a confirmation dialog is shown. Returns USER_CANCELLED if the user declines.
datasets
datasets.list()
List all datasets in the project.
const result = await window.midas.datasets.list();
// result.data: [{ id, name, rows, columns, type, parentIds?, saveDataWithProject? }, ...]
type is either 'primary' (imported data) or 'derived' (created by SQL or other operations). parentIds lists the source dataset IDs a derived dataset depends on; it is empty for a derived dataset created from SQL that references no dataset (such as generate_series). Only derived datasets have saveDataWithProject, which tells whether the setting to save the computed data in the project file is enabled. Change the setting with datasets.setSaveDataWithProject(). Temporary internal datasets (ephemeral) are not included in the list.
datasets.describe(id)
Get detailed information about a dataset. Accepts a dataset ID or name (case-insensitive).
const result = await window.midas.datasets.describe('Iris');
// result.data:
// {
// id: 'ds_001',
// name: 'Iris',
// type: 'primary',
// rowCount: 150,
// columns: [
// { id: 'col_001', name: 'sepal_length', type: 'float64', scale: 'ratio' },
// { id: 'col_002', name: 'species', type: 'enum', scale: 'nominal', enumName: 'species_enum' },
// ...
// ]
// }
Each column entry contains id, name, and type. scale, enumName, and displayFormat are optional. enumName is present when the column type is enum, indicating the associated enum definition name. displayFormat holds the format set with datasets.setColumnDisplayFormat() and is absent for columns with no format. The internal row number column (Row #) added by MIDAS is not included in columns. The number of columns matches that of datasets.list().
For a derived dataset whose operation can be edited, operation holds the definition of the operation the dataset is computed from. The editable operation types are those in the editingDatasetId table of tabs.open(). operation has the shape the project stores, so its fields depend on operation.type. For example, a dataset created with datasets.derive() has an operation with type: 'sql_query' and the SQL in query. Primary datasets, and derived datasets whose operation cannot be edited, such as those saved from a model, do not return operation.
For a derived dataset, saveDataWithProject tells whether the setting to save the computed data in the project file is enabled. Primary datasets do not return saveDataWithProject.
datasets.profile(id)
Get column-level summary statistics for a dataset. Accepts a dataset ID or name (case-insensitive). A derived dataset that has not been evaluated yet, such as one right after opening a saved project or right after overwriting a dataset it depends on, is evaluated before it is read.
const result = await window.midas.datasets.profile('Iris');
// result.data:
// {
// id: 'ds_001',
// name: 'Iris',
// rowCount: 150,
// columns: [
// {
// name: 'sepal_length', type: 'float64', scale: 'ratio',
// nullCount: 0, uniqueCount: 35,
// min: 4.3, max: 7.9, mean: 5.843, median: 5.8, sd: 0.828
// },
// {
// name: 'species', type: 'string', scale: 'nominal',
// nullCount: 0, uniqueCount: 3,
// topValues: [
// { value: 'setosa', count: 50 },
// { value: 'versicolor', count: 50 },
// { value: 'virginica', count: 50 }
// ]
// },
// ...
// ]
// }
Every column includes nullCount and uniqueCount. uniqueCount counts all distinct non-null values. Numeric columns (int64, float64) additionally include min, max, mean, median, and sd (sample standard deviation, Bessel-corrected with n-1 divisor). min through sd are null when no valid numeric values exist. sd is also null when only one valid value exists (n < 2). mean, median, and sd are null when the column spans a range too wide to compute them in double precision. String and enum columns include topValues (up to 5 most frequent values). The internal row number column (Row #) added by MIDAS is not included in the results.
datasets.query(sql, options?)
Execute a SQL query and return rows (read-only, no dataset creation). SQL follows DuckDB syntax.
const result = await window.midas.datasets.query(
'SELECT species, AVG(sepal_length) as avg_sl FROM Iris GROUP BY species'
);
// result.data: { columns: ['species', 'avg_sl'], totalRows: 3, returnedRows: 3, rows: [...] }
Table names in FROM and JOIN are dataset names in the project. Matching is case-insensitive, and dataset IDs are not accepted. Use datasets.list() to see the available names. For dataset names containing spaces or non-ASCII characters, use double quotes in SQL (e.g., SELECT * FROM "My Dataset"). The dataset the query runs against is resolved from the first table reference. If no dataset matches, a DATASET_NOT_FOUND error is returned.
Use options.limit and options.offset for pagination. When limit is omitted, all rows are returned. Even with SELECT *, the internal row number column (Row #) added by MIDAS is not included in the results. To include row numbers, create an explicit column such as SELECT ROW_NUMBER() OVER ().
If the SQL produces an output column whose name starts with the reserved prefix __midas_ (reserved for MIDAS internal columns), the query returns an INVALID_INPUT error. The same error is returned when the SQL names a column Row # (the name of the row number column) anywhere in the query, including subqueries and CTEs. Alias such columns to a non-reserved name.
When the query produces non-finite values (±Infinity, NaN), dates outside the representable range (years 1 to 9999), integers outside the 64-bit integer range, or empty strings, those values become null in the returned rows, and the count is reported per column in the top-level warnings field. Unlike the datasets.* methods that create a dataset (see above), this is not also shown as an on-screen notification, since query() does not create anything the notification could point to.
If DuckDB cannot parse the SQL, a SQL_PARSE_ERROR error is returned. The error message carries the message from DuckDB's parser. For syntax errors it also reports the position of the offending token. Besides syntax errors, statements MIDAS cannot analyze (such as PIVOT) also produce this error. Double-quote identifiers that collide with SQL keywords, such as column aliases (e.g., SELECT x AS "order"). SQL that parses but references no table returns a NO_TARGET error.
Only single SELECT statements are accepted; multiple statements separated by semicolons and DML/DDL statements are rejected.
datasets.derive(sql, name, options?)
Execute a SQL query and save the result as a new derived dataset. SQL follows DuckDB syntax.
const result = await window.midas.datasets.derive(
'SELECT species, AVG(sepal_length) as avg_sl FROM Iris GROUP BY species',
'Species Averages'
);
// result.data: { id: 'derived_...', name: 'Species Averages', rowCount: 3, columnCount: 2, overwrote: false,
// lineage: { traceable: true } }
Table names in FROM and JOIN are dataset names in the project. Matching is case-insensitive, and dataset IDs are not accepted. Use datasets.list() to see the available names. For dataset names containing spaces or non-ASCII characters, use double quotes in SQL (e.g., SELECT * FROM "My Dataset"). The source dataset is resolved from the first table reference. If no dataset matches, a DATASET_NOT_FOUND error is returned. If DuckDB cannot parse the SQL, a SQL_PARSE_ERROR error is returned, and SQL that references no table returns a NO_TARGET error.
The result data is evaluated and held in memory when derive() completes, so it can be read immediately with datasets.fetch() or datasets.profile(). By default, if a derived dataset with the same name exists, it is updated in place. The existing dataset ID is preserved, so references from tabs and dependent datasets remain valid. Caches of derived datasets that depend on the overwritten dataset, along with estimates of dependent models, are invalidated and re-evaluated with the new data on next access. Set options.overwrite to false to prevent overwriting; if a derived dataset with the same name already exists, an error is returned.
If the output name resolves to the same dataset ID as any table referenced in the SQL's FROM / JOIN clauses, or to any ancestor of those referenced tables (a dataset that one of the referenced tables was derived from), the operation would create a dependency cycle and is rejected with a SELF_REFERENCE error (e.g., derive('SELECT species, COUNT(*) FROM Iris GROUP BY species', 'Iris'), or, given a chain Iris → A → B, derive('SELECT * FROM B', 'A')). If the output name matches an existing primary dataset, a NAME_CONFLICT error is returned (primary datasets cannot be overwritten by derived methods). If a derived dataset with the same name was created by a different method (e.g., trying to overwrite an addColumns dataset with derive), an OPERATION_TYPE_MISMATCH error is returned. If a referenced table name or the output name matches multiple existing datasets case-insensitively, an AMBIGUOUS_TABLE_NAME error is returned. If the SQL produces an output column whose name starts with the reserved prefix __midas_ (reserved for MIDAS internal columns), the operation returns an INVALID_INPUT error. The same error is returned when the SQL names a column Row # (the name of the row number column) anywhere in the query, including subqueries and CTEs. Alias such columns to a non-reserved name.
When the SQL references multiple tables (via JOIN or subqueries), all referenced datasets are stored as the derived dataset's parentIds. You can inspect them via datasets.list() — each derived dataset entry includes a parentIds array. The Project Lineage tab shows edges from each parent to the derived dataset.
The lineage field of the result reports whether row lineage of the created dataset — one hop to its parents — can be traced with datasets.traceRowLineage(). The check reads only the structure of the SQL and does not evaluate the data. Queries containing window functions, set operations such as UNION, or subqueries and CTEs that aggregate with GROUP BY cannot be traced. In that case lineage.traceable is false, reason classifies why, and warnings explains it; see datasets.traceRowLineage() for the list of classifications. An untraceable dataset cannot lead back to the original rows through Contributing rows or graph drill-down, so this field lets you decide at creation time to restructure the query, for example by splitting each aggregation step into its own derived dataset. If the check itself fails, lineage is omitted.
Only single SELECT statements are accepted; multiple statements separated by semicolons and DML/DDL statements are rejected. To bring external data in, use datasets.importFromURL or datasets.importFromBuffer.
datasets.importFromURL(url, options?)
Fetch a CSV/TSV file from an external URL and import it as a dataset. The file is read as a primary dataset with all columns as text, and when type detection finds non-string columns, a derived dataset with column type conversion applied is created automatically. The derived dataset inherits the requested name, and the primary dataset gets a (raw) suffix. If no column needs conversion, only the primary dataset is created.
const result = await window.midas.datasets.importFromURL(
'https://example.com/data.csv'
);
// result.data: { id: 'converted_...', name: 'data', rowCount: 100, columnCount: 5,
// sourceDatasetId: 'dataset_...', sourceDatasetName: 'data (raw)' }
The returned id and name point to the converted dataset — the one to analyze. The raw side is reported as sourceDatasetId and sourceDatasetName (included only when a converted dataset was created). Use the returned name for subsequent datasets.query, graphs, and models.
The options properties are:
name: Dataset name after import. When omitted, the name is inferred from the URL.hasHeader: Whether to treat the first row as a header. Defaults totrue. Whenfalse, columns are namedColumn1,Column2, and so on.encoding: Character encoding ("utf-8","shift_jis","euc-jp"). When omitted, encoding is auto-detected from the Content-Type charset and the byte sequence. An unsupported charset is ignored and the byte sequence alone decides. When the data cannot be decoded with the declared charset, the byte sequence alone decides andwarningsreports it. When auto-detected, the result reports the detected encoding asdetectedEncoding.delimiter: Delimiter (",","\t",";","|"). When omitted, it follows the file name extension in the URL: tab for.tsvand.txt, comma otherwise.overwrite: Whether to replace an existing dataset with the same name. Defaults tofalse.
If a dataset with the same name (case-insensitive) already exists, the method returns a DATASET_ALREADY_EXISTS error. Set options.overwrite to true to replace the existing dataset in place (ID preserved). After the replacement, columns with the same name as an existing column keep their column IDs and manual settings (scale, display format), so saved models and derived datasets that reference those columns stay valid. Columns are matched by their current names, so a column renamed after import is carried over only when the new data has a column with the renamed name. Columns whose names do not exist in the existing dataset get new column IDs. When overwriting a pair created by a previous import (converted + raw), both datasets are updated. When the name matches an existing primary dataset and the new data would create a converted dataset, the overwrite is rejected with a NAME_CONFLICT error. The returned columnCount matches the number of columns in the source file. The internal row number column (Row #) added by MIDAS is not included in the count. Column headers that start with the reserved prefix __midas_ (reserved for MIDAS internal columns), and a column header Row # (the name of the row number column), are automatically renamed to non-reserved names, and a warning for each renamed header is included in result.warnings. When a converted dataset is created, result.warnings also includes the per-column counts described above for values that became null during type conversion (failed conversions, dates outside the representable range, non-finite values).
Parse failures (empty data, empty header row, column count mismatch between rows, a quoted value that is not closed correctly, a row longer than 64 MB, mixed line break types, bytes that cannot be decoded with the encoding used for reading, URL validation errors, invalid content type, etc.) return an INVALID_INPUT error. Network failures and timeouts return EXECUTION_ERROR. Files exceeding the size warning threshold (default 10 MB, configurable in Settings) are still imported, but a size warning is included in result.warnings.
URL validation and security restrictions apply. Only HTTP/HTTPS protocols are allowed, and access to cloud metadata endpoints is blocked. Warnings are issued for URLs not in the trusted URL list. If "Block connections to untrusted domains" is enabled in settings, untrusted URLs result in an error. See Privacy and Security for details.
datasets.importFromBuffer(data, options?)
Import CSV/TSV data from an ArrayBuffer or TypedArray (Uint8Array, Node.js Buffer, etc.) as a dataset. Use this when you want to load a local CSV from a Playwright test without spinning up an HTTP server.
// Playwright: load a local CSV via page.evaluate
import { readFileSync } from 'fs';
const csvBytes = Array.from(readFileSync('fixtures/sales.csv'));
const result = await page.evaluate(async (bytes) => {
const buffer = new Uint8Array(bytes).buffer;
return await window.midas.datasets.importFromBuffer(buffer, {
name: 'Sales',
});
}, csvBytes);
// result.data: { id: 'converted_...', name: 'Sales', rowCount: 500, columnCount: 7,
// sourceDatasetId: 'dataset_...', sourceDatasetName: 'Sales (raw)' }
data accepts an ArrayBuffer or any ArrayBufferView (Uint8Array, DataView, Node.js Buffer, etc.). The options properties are:
name: Dataset name after import. Defaults to"Untitled".hasHeader: Whether to treat the first row as a header. Defaults totrue.encoding: Character encoding ("utf-8","shift_jis","euc-jp"). When omitted, encoding is auto-detected from the byte sequence. When auto-detected, the result reports the detected encoding asdetectedEncoding.delimiter: Delimiter (",","\t",";","|"). Defaults to",".overwrite: Whether to replace an existing dataset with the same name. Defaults tofalse.
Specify the delimiter with delimiter; pass "\t" for TSV data. The returned columnCount matches the number of columns in the source file. The internal row number column (Row #) added by MIDAS is not included in the count.
Datasets are created the same way as importFromURL: a primary dataset with all columns as text (named with a (raw) suffix) and a derived dataset with the detected type conversion applied (inheriting the requested name). The returned id and name point to the converted dataset. If a dataset with the same name (case-insensitive) already exists, the method returns a DATASET_ALREADY_EXISTS error. Set overwrite: true to replace the existing dataset in place (ID preserved). Column IDs and manual settings are carried over by the same rules as importFromURL. When overwriting a pair created by a previous import, both datasets are updated; when the name matches an existing primary dataset and the new data would create a converted dataset, the overwrite is rejected with a NAME_CONFLICT error. Column headers that start with the reserved prefix __midas_ (reserved for MIDAS internal columns), and a column header Row # (the name of the row number column), are automatically renamed to non-reserved names, and a warning for each renamed header is included in result.warnings. When a converted dataset is created, result.warnings also includes the per-column counts described above for values that became null during type conversion (failed conversions, dates outside the representable range, non-finite values). Parse failures (empty data, empty header row, column count mismatch between rows, a quoted value that is not closed correctly, a row longer than 64 MB, mixed line break types, bytes that cannot be decoded with the encoding used for reading, etc.) return an INVALID_INPUT error.
datasets.generateSynthetic(spec, options)
Generate a synthetic dataset from a data generating process (DGP). This is the same feature as the Synthetic Data Generator tab; the tab's page explains the meaning of distributions, expressions, factors, By, and presets. This section covers the API-specific format: the spec JSON, the options, and the error codes. listSyntheticPresets returns ready-made specs for common designs.
// Generate linear-regression data: y = 1 + 2x + noise (error sd = 0.5)
const result = await window.midas.datasets.generateSynthetic(
{
nodes: [
{ name: 'x', dist: { kind: 'normal', mean: { type: 'const', value: 0 }, sd: { type: 'const', value: 1 } } },
{
name: 'y',
dist: {
kind: 'normal',
mean: {
type: 'binary', op: '+',
left: { type: 'const', value: 1 },
right: { type: 'binary', op: '*', left: { type: 'const', value: 2 }, right: { type: 'ref', name: 'x' } },
},
sd: { type: 'const', value: 0.5 },
},
},
],
},
{ name: 'Linear', rows: 200, seed: 42 }
);
// result.data: { id: 'ds_...', name: 'Linear', rowCount: 200, columnCount: 2 }
Each element of spec.nodes is one column of the output dataset. Choose a distribution with dist.kind:
normal(mean, sd) /uniform(min, max) /gamma(shape, scale) /weibull(shape, scale): continuous values (float64 columns)poisson(lambda) /bernoulli(p): non-negative integers (int64 columns)categorical(levels, weights?): string labels (string column).weightsare relative; sampling is uniform whenweightsis omitteddeterministic(value): a noise-free transformed column. When the top level of the expression is a comparison, the column is a 0/1 indicator and becomes an int64 column; otherwise it is float64
The row structure is declared with spec.factors (an array of factors, each { name, levels }), spec.rowsPerCell (the rows per cell, 1 when omitted), and spec.time (the time column, { name, length }). These correspond to the Factors section, Rows per cell, and Time column of the tab; when declared, the row count is implied by them and options.rows can be omitted. A node's by (an array of column names) corresponds to the By field of the tab and samples the column once per group. See the tab's page for the meaning of factors and By.
Distribution parameters are expressions (Expr) in JSON, combining the following nodes.
- Constant:
{ type: 'const', value } - Reference to another column:
{ type: 'ref', name } - Binary operation:
{ type: 'binary', op, left, right }.opis one of the arithmetic operators+-*/and the comparisons<<=>>= - Unary minus:
{ type: 'unary', op: '-', operand } - Function:
{ type: 'call', fn, args }.argsis an array of argument expressions;exp,log,sqrt,logistic, andabstake one argument,pow,min, andmaxtake two - Categorical branch:
{ type: 'cases', over, map, default? } - Past reference:
{ type: 'lag', name, k }.kis a positive integer, usable only with atimedimension
The vocabulary is the same as the tab's text expressions. See Expression Syntax and the survival data example for the values comparisons return, the rules of cases and lag, and how to compose survival data.
The options properties are:
name: Dataset name (required).rows: Number of rows to generate (1 or more, up to 10,000,000). Whenspec.factorsorspec.timeis present, this can be omitted and the row count implied by them is used; when given, it must match that row count. Required when the spec has neither.seed: Random seed (required), a signed 32-bit integer. The samespec,rows, andseedreproduce the same data.overwrite: Whether to replace an existing synthetic dataset with the same name. Defaults tofalse.
By default, the generated data is not saved to the MDS file; the spec and seed are kept in the generation operation, and the data is regenerated from that operation on reload. See Editing a Saved Dataset for the scope of reproducibility and how to freeze values.
If a parameter expression evaluates to a value outside its domain during generation, the method returns INVALID_INPUT at that point and fails. This includes an sd of 0 or below, or a p outside [0, 1]. A linear predictor can be mapped into the range 0 to 1 with logistic and to positive values with exp. With a linear predictor extreme enough to exceed the limits of floating-point arithmetic, however, exp evaluates to 0 or infinity and generation fails.
An invalid spec returns INVALID_INPUT. This covers the following cases.
- Cyclic references or references to undefined columns
- Parameters outside their domain, such as an
sdof 0 or below - A
casesthat covers neither every level nor provides adefault - A
bycolumn whose expression references a column that varies within its groups - A
rowsorseedout of range
If a dataset with the same name already exists and overwrite is false, the method returns DATASET_ALREADY_EXISTS. Only a synthetic dataset created the same way can be replaced with overwrite: true: a name clash with a primary dataset returns NAME_CONFLICT, and a clash with a dataset created by a different method returns OPERATION_TYPE_MISMATCH.
datasets.listSyntheticPresets()
List the built-in synthetic data presets. There are three: multilevel (random intercept), repeated measures (subject × time), and correlated random intercept and slope. They are the same presets the Preset selector of the Synthetic Data Generator tab offers.
const presets = await window.midas.datasets.listSyntheticPresets();
// presets.data: [{ id, label, description, spec, rowCount }, ...]
await window.midas.datasets.generateSynthetic(presets.data[0].spec, { name: 'Schools', seed: 42 });
Each entry carries the following fields.
id: the identifier of the presetlabel: the name shown in the UIdescription: a description of the designspec: the DGP specrowCount: the row count implied by the factors
Pass the spec to generateSynthetic as is, or edit it first. Every preset has factors, so the row count is implied by the spec and options.rows is not needed. See Presets for what each preset generates and the constraints on which models MIDAS can fit to it.
datasets.reload(options?)
Reload datasets that were originally imported from a URL or a file, updating them from their original sources.
// Reload all URL- and file-sourced datasets
const result = await window.midas.datasets.reload();
// result.data: { reloaded: [{ datasetId, name, rowCount, previousRowCount, source, clearedExcludedRowCount, clearedRowCommentCount }], failed: [], needsFileSelection: [] }
// Reload a specific dataset only
const result = await window.midas.datasets.reload({
datasetId: 'primary_abc123',
});
URL-sourced datasets are re-fetched from their stored URL. File-sourced datasets are read from the file last selected in the Reload dialog. Re-requesting file read permission needs a user gesture, so the API only reads files whose permission is still granted. Datasets that cannot be reloaded automatically are returned in result.data.needsFileSelection with a reason (no-handle, file-missing, or permission-required); they need a file selection or permission grant in the Reload dialog.
When options.datasetId is specified, only that dataset is reloaded. It accepts a dataset ID or a dataset name (case-insensitive). When omitted, all primary datasets that were imported from a URL or a file are targeted. If the specified dataset does not exist, the method returns a DATASET_NOT_FOUND error. If the dataset exists but was imported neither from a URL nor from a file, it returns an INVALID_INPUT error.
Reload reads the new data with the header setting of the first import and with the delimiter recorded in the dataset, which is usually the one it was last read with (see Reloading a Dataset). To read it with another delimiter, pass options.delimiter (",", "\t", ";", or "|") together with options.datasetId. The delimiter used is saved and used for later reloads. Passing options.delimiter without options.datasetId returns an INVALID_INPUT error, because the files of different datasets can use different delimiters.
Reload preserves the dataset ID, name, derived datasets, and model bindings. Excluded rows and row comments are cleared. A reload fails if the data cannot be parsed as CSV/TSV, under the same conditions as datasets.importFromURL(). It also fails if the CSV at the source is missing any existing column (removed or renamed), or if two columns would end up with the same name after the reload. For a dataset whose columns are typed (anything other than string), a reload also fails if any value cannot be converted to the current column type. Added columns and reordering are allowed.
A derived dataset whose types come from the Convert Column Types tab is not subject to that failure. Values that do not fit the types are handled by the On Error setting when the conversion is re-evaluated, and the reload itself succeeds.
result.data.reloaded contains information about successfully reloaded datasets (datasetId, name, updated rowCount, previousRowCount, and the source URL or file name). Each entry also reports how many excluded rows and row comments that reload cleared, as clearedExcludedRowCount and clearedRowCommentCount. result.data.failed contains information about failed datasets (datasetId, name, source, and error). When some datasets fail, success is still true. success is false only when all reloads fail.
datasets.addColumns(datasetId, input)
Add computed columns to a dataset. datasetId accepts a dataset ID or name (case-insensitive). The result is created as a new derived dataset. expression follows DuckDB SQL expression syntax. SQL functions such as CASE WHEN and CAST are supported.
const result = await window.midas.datasets.addColumns('Iris', {
columns: [
{ name: 'bmi', expression: 'weight / (height * height)' }
]
});
// result.data: { id: 'derived_...', name: '...', rowCount: 150, columnCount: 6 }
A column name that starts with the reserved prefix __midas_ (reserved for MIDAS internal columns), or that is Row # (the name of the row number column), returns an INVALID_INPUT error. Use outputName to specify the output dataset name. If a derived dataset with the same name exists, it is updated in place, preserving the existing dataset ID. If outputName resolves to the source datasetId or any of its ancestors (datasets it was derived from), a SELF_REFERENCE error is returned to prevent a dependency cycle; if it collides with an existing primary dataset, a NAME_CONFLICT error is returned. If the existing dataset was created by a different method, an OPERATION_TYPE_MISMATCH error is returned. If outputName matches multiple existing datasets case-insensitively, an AMBIGUOUS_TABLE_NAME error is returned.
datasets.addOrthogonalPolynomials(datasetId, input)
Add orthogonal polynomial columns to a dataset. datasetId accepts a dataset ID or name (case-insensitive). Used as explanatory variables in polynomial regression.
const result = await window.midas.datasets.addOrthogonalPolynomials('Iris', {
column: 'temperature',
degree: 3
});
// result.data: { id: 'derived_...', name: '...', rowCount: 150, columnCount: 8, columnNames: ['temperature_poly1', 'temperature_poly2', 'temperature_poly3'] }
Maximum degree is 30. Use outputName to specify the output dataset name. If outputName resolves to the source datasetId or any of its ancestors (datasets it was derived from), a SELF_REFERENCE error is returned to prevent a dependency cycle; if it collides with an existing primary dataset, a NAME_CONFLICT error is returned. If the existing dataset was created by a different method, an OPERATION_TYPE_MISMATCH error is returned. If outputName matches multiple existing datasets case-insensitively, an AMBIGUOUS_TABLE_NAME error is returned.
datasets.reshapeWideToLong(datasetId, input)
Unpivot (melt) a dataset from wide to long format. datasetId accepts a dataset ID or name (case-insensitive). idColumns/valueColumns accept column names or IDs. Same transformation as the Reshape tab's Wide to Long mode; the result is created as a new derived dataset.
const result = await window.midas.datasets.reshapeWideToLong('Scores', {
idColumns: ['subject'],
valueColumns: ['test1', 'test2', 'test3'],
variableName: 'test',
valueName: 'score'
});
// result.data: { id: 'derived_...', name: 'Scores (Long)', rowCount: 30, columnCount: 3 }
Columns listed in idColumns are copied as-is; each column in valueColumns is expanded into its own row. Row count becomes the source row count times the number of value columns. variableName (default variable) names the column that holds the original column name; valueName (default value) names the column that holds the value. An empty valueColumns returns an INVALID_INPUT error. So does a variableName/valueName that starts with the reserved prefix __midas_ (reserved for MIDAS internal columns) or is Row # (the name of the row number column). Use outputName to specify the output dataset name. If a derived dataset with the same name exists, it is updated in place, preserving the existing dataset ID. If outputName resolves to the source datasetId or any of its ancestors, a SELF_REFERENCE error is returned; if it collides with an existing primary dataset, NAME_CONFLICT; if the existing dataset was created by a different method, OPERATION_TYPE_MISMATCH; if outputName matches multiple existing datasets case-insensitively, AMBIGUOUS_TABLE_NAME.
datasets.reshapeLongToWide(datasetId, input)
Pivot a dataset from long to wide format. datasetId accepts a dataset ID or name (case-insensitive). idColumns/variableColumn/valueColumn accept column names or IDs. Same transformation as the Reshape tab's Long to Wide mode; the result is created as a new derived dataset.
const result = await window.midas.datasets.reshapeLongToWide('Scores', {
idColumns: ['subject'],
variableColumn: 'test',
valueColumn: 'score'
});
// result.data: { id: 'derived_...', name: 'Scores (Wide)', rowCount: 10, columnCount: 4 }
Rows are grouped by the combination of idColumns values; each distinct value of variableColumn becomes a new column, populated from valueColumn. An INVALID_INPUT error is returned if the combination of idColumns values and variableColumn value is not unique (duplicate entries). Use outputName to specify the output dataset name. If a derived dataset with the same name exists, it is updated in place, preserving the existing dataset ID. If outputName resolves to the source datasetId or any of its ancestors, a SELF_REFERENCE error is returned; if it collides with an existing primary dataset, NAME_CONFLICT; if the existing dataset was created by a different method, OPERATION_TYPE_MISMATCH; if outputName matches multiple existing datasets case-insensitively, AMBIGUOUS_TABLE_NAME.
datasets.dummyCode(datasetId, input)
Convert categorical columns to 0/1 dummy variables (treatment coding). datasetId accepts a dataset ID or name (case-insensitive). columns/includedColumns/keepOriginalColumns accept column names or IDs. Same transformation as the Dummy Coding tab; the result is created as a new derived dataset.
const result = await window.midas.datasets.dummyCode('Iris', {
columns: ['species'],
referenceCategories: { species: 'setosa' }
});
// result.data: { id: 'derived_...', name: 'Iris (Dummy Coded)', rowCount: 150, columnCount: 6,
// encodedColumns: [{ column: 'species', referenceCategory: 'setosa', dummyVariableCount: 2 }] }
Each column in columns is converted from k categories into k-1 dummy variables, omitting a reference category (by default, the first category in the category order). Use referenceCategories to set the reference category per column. includedColumns controls which columns pass through to the output (in addition to the dummy-coded columns); when omitted, all source columns except Row # are included, and columns not listed there are dropped. keepOriginalColumns (a subset of columns) keeps the source column in the output alongside its dummy variables. scaleOverrides sets the measurement scale recorded on pass-through columns in the output; the dummy variable columns themselves are always recorded as ratio scale. String and enum columns take only the nominal and ordinal scales, and specifying interval or ratio for them returns an INVALID_INPUT error. A column with more than 50 unique values is still encoded, and warnings names the column and how many dummy variables it generated — the same check the Dummy Coding tab applies before Create Dataset. An INVALID_INPUT error is returned if a column in columns is not a nominal or ordinal, non-boolean column; has fewer than 2 unique values (including when the dataset has no rows); a referenceCategories entry names a category absent from the data; or keepOriginalColumns includes a column not in columns. Use outputName to specify the output dataset name. If a derived dataset with the same name exists, it is updated in place, preserving the existing dataset ID. If outputName resolves to the source datasetId or any of its ancestors, a SELF_REFERENCE error is returned; if it collides with an existing primary dataset, NAME_CONFLICT; if the existing dataset was created by a different method, OPERATION_TYPE_MISMATCH; if outputName matches multiple existing datasets case-insensitively, AMBIGUOUS_TABLE_NAME.
datasets.filter(datasetId, input)
Filter a dataset's rows by an expression, creating a new derived dataset. datasetId accepts a dataset ID or name (case-insensitive). Same transformation as the Filtered Data tab's Save as Dataset; the filter is re-evaluated whenever the source data changes.
const result = await window.midas.datasets.filter('Iris', {
expression: "species = 'setosa' AND sepal_length > 5"
});
// result.data: { id: 'derived_...', name: 'Iris (Filtered)', rowCount: 22, columnCount: 5 }
expression uses the same syntax as the Filtered Data tab's filter input. Missing values follow the same rule as the Data Table filter. A negated condition returns every row that the condition before negation does not match, so species != 'setosa' includes the rows where species is missing. IS NULL and IS NOT NULL are the only syntax that tests whether a value is missing. An empty expression, invalid syntax, or a reference to a column not in the dataset returns an INVALID_INPUT error. A value that cannot be read as the column's type, NULL written as a value, LIKE or ILIKE on a column other than string and enum, or a LIKE or ILIKE pattern that is not a string also returns an INVALID_INPUT error. Use outputName to specify the output dataset name. If a derived dataset with the same name exists, it is updated in place, preserving the existing dataset ID. If outputName resolves to the source datasetId or any of its ancestors, a SELF_REFERENCE error is returned; if it collides with an existing primary dataset, NAME_CONFLICT; if the existing dataset was created by a different method, OPERATION_TYPE_MISMATCH; if outputName matches multiple existing datasets case-insensitively, AMBIGUOUS_TABLE_NAME.
datasets.setColumnSchema(datasetId, columnId, schema)
Change a column's data type, measurement scale, or enum definition. datasetId accepts a dataset ID or name (case-insensitive).
const result = await window.midas.datasets.setColumnSchema('Iris', 'col_002', {
type: 'enum',
scale: 'nominal',
enumName: 'species_enum'
});
// result.data: { datasetId: 'ds_001', columnId: 'col_002', createdDerived: true, derivedDatasetId: 'derived_...', overwrote: false }
schema accepts type, scale, enumName, and timezone. At least one of type, scale, and enumName is required. String and enum columns take only the nominal and ordinal scales, and specifying interval or ratio for them returns an INVALID_INPUT error. The same applies when the target type of the change is string or enum. Changing the data type involves SQL type conversion, which creates a new derived dataset. If a derived dataset with the same output name already exists, it is updated in place, preserving the existing dataset ID. If outputName points to the source dataset itself or any of its ancestors (datasets it was derived from), a SELF_REFERENCE error is returned to prevent a dependency cycle; if it collides with an existing primary dataset, a NAME_CONFLICT error is returned. If the existing dataset was created by a different method, an OPERATION_TYPE_MISMATCH error is returned. If outputName matches multiple existing datasets case-insensitively, an AMBIGUOUS_TABLE_NAME error is returned. Changing only the measurement scale updates metadata in place without creating a derived dataset.
Only string, int64, and enum columns can be converted to enum type. Converting a float64, boolean, date, or datetime column to enum returns an INVALID_INPUT error; convert it to string with type: 'string' first and check the resulting values before converting to enum. The float64 value 2 becomes 2.0. An int64 column is matched against the enum definition by the decimal notation of each value. These rules are the same as in The Convert Column Types Tab, and The Manage Enums Tab explains them in detail.
When converting to enum type, all column values must be in the enum definition or NULL. If out-of-range values are present, the call is rejected with ENUM_VALUE_MISMATCH. Use Convert Column Types first to null-out or exclude unwanted values, or use enums.update to add the missing values to the enum definition.
When converting a string column to datetime, timezone sets the timezone name (e.g., Asia/Tokyo) in which values without a timezone offset are interpreted. When omitted, values are interpreted as UTC. A value with an offset is interpreted with its own offset regardless of the setting. Specifying timezone for a conversion other than string to datetime, or passing a name MIDAS does not handle, returns an INVALID_INPUT error. The accepted names and the interpretation rules are the same as the Timezone setting in The Convert Column Types Tab.
datasets.rename(id, newName)
Renames a dataset. Accepts a dataset ID or a dataset name (case-insensitive).
const result = await window.midas.datasets.rename('Iris', 'Iris (raw)');
// result.data: { datasetId: 'ds_001', previousName: 'Iris', name: 'Iris (raw)' }
SQL queries of derived datasets that reference this dataset are rewritten to the new name. Only references enclosed in double quotes, such as FROM "sales_2024", are rewritten; a reference written without quotes, such as FROM sales_2024, is left as it is, and that query fails after the rename until you edit it. Once the rename completes, queries that still use the previous name fail. Dataset names are compared without case, so iris is rejected while Iris exists.
datasets.setSaveDataWithProject(id, save)
Sets whether the computed data of a derived dataset is saved in the project file. This is the same setting as Save data with project in the UI, described in Datasets. id accepts a dataset ID or a dataset name (case-insensitive).
const result = await window.midas.datasets.setSaveDataWithProject('Monthly Total', true);
// result.data: { datasetId: 'derived_001', previousSaveDataWithProject: false, saveDataWithProject: true }
When save is true, the next project file you save contains the data that has been computed at the time of saving. Data that has not been computed at that time is not included, and is recomputed when it is first needed after the project is opened. When save is false, the project file contains only the definition of the operation, and MIDAS re-runs the operation when the data is first needed after the project is opened.
Changing the setting changes neither the data nor the project file at that moment. The change does not recompute any dataset. The project file changes the next time you call project.save(), project.exportMds(), or project.downloadMds().
Only derived datasets have this setting. The data of a primary dataset is always included in the project file, so specifying a primary dataset returns an INVALID_INPUT error. A save that is not a boolean and the ID of a temporary internal dataset (ephemeral) also return an INVALID_INPUT error.
datasets.renameColumn(datasetId, columnId, newName)
Renames a column. datasetId accepts a dataset ID or name, and columnId accepts a column ID or name.
const result = await window.midas.datasets.renameColumn('Iris', 'sepal_length', 'sepal length (cm)');
// result.data: { datasetId: 'ds_001', columnId: 'col_001', previousName: 'sepal_length', name: 'sepal length (cm)' }
A column name is also its SQL identifier. SQL queries written against the old name stop matching, so update them yourself after renaming. Renaming invalidates the caches of derived datasets, models, and report elements that depend on this dataset, and they are recomputed.
Columns of a derived dataset cannot be renamed. Their names come from the definition of the dataset, such as its SQL query or its type conversion settings, and every evaluation rebuilds them from that definition. Renaming a column of a derived dataset returns an INVALID_INPUT error whose error.suggestion names where to change the name. To change a name in a SQL derived dataset, change the AS alias in the query. The column names of the typed dataset that a CSV import creates come from the headers of the imported file, so fix the headers in the file and import it again as a new dataset, or build a dataset from a query that names the columns with AS aliases.
datasets.setColumnDisplayFormat(datasetId, columnId, format)
Sets how a column's values are displayed. datasetId accepts a dataset ID or name, and columnId accepts a column ID or name.
const result = await window.midas.datasets.setColumnDisplayFormat('Issues', 'number', {
link: { urlTemplate: 'http://example.com/issues/{number}' }
});
// result.data: {
// datasetId: 'ds_001', columnId: 'col_001',
// displayFormat: { link: { urlTemplate: 'http://example.com/issues/{number}' } }
// }
A display format consists of two kinds, a link display and a number format, and format names only the kinds to change as keys. link: { urlTemplate } displays values as links. Each {column_name} in urlTemplate is replaced with that column's value from the same row, URL-encoded. A row with a missing value in a referenced column shows plain text instead of a link. number: { formatSpec } displays values with a number format, where formatSpec takes a non-empty Python-compatible format spec. A number format can be set on numeric columns (float64 or int64) only.
The two kinds are independent settings. A kind not named in format keeps its current setting, and a kind whose value is null is cleared. Passing null as format itself clears both kinds. A column with both kinds shows the value formatted with the number format as a link. The returned displayFormat is the display format after the change, or null when no kind is left.
The format is set on the column, so it applies wherever the column is shown. A Filtered Data tab showing rows filtered from this dataset uses the same format, data tables already added to a report pick it up as well, and the format survives re-evaluation of a derived dataset. If re-evaluation turns the column into a non-numeric type, the number format is removed. Read the current format from displayFormat in the column information returned by datasets.describe().
A Data Table tab can set a format that applies to that tab alone. Coefficient tables in analysis tabs such as GLM and GLMM have tab-local formats in the same way. These tab formats are view state and are neither readable nor writable from this API. A tab format takes precedence over the column's format kind by kind. A kind set on the tab keeps showing the tab's format even after you change the column's format here, and a kind not set on the tab shows the column's format. When a coefficient table is saved with Save as Dataset, the tab formats at that moment are carried into the dataset as column formats, which this API can then read and write.
Links are rendered only in projects you created yourself and in projects from MDS files with an Official or Trusted signature. In projects from an MDS file with an unknown signer, the format is still stored, but values are shown as plain text instead of links.
datasets.download(id, options)
Downloads a dataset as a CSV, TSV, or JSON file, the same way the Export button in the data table does. Accepts a dataset ID or a dataset name (case-insensitive).
const result = await window.midas.datasets.download('Iris', {
format: 'csv',
fileName: 'iris-export',
encoding: 'shift_jis'
});
// result.data: {
// datasetId: 'ds_001', fileName: 'iris-export.csv', format: 'csv',
// rowCount: 150, unrepresentableCharacters: []
// }
format is required. When fileName is omitted the dataset name is used, and the extension is replaced to match the format. includeHeaders defaults to true and is ignored for JSON. CSV and TSV accept encoding (utf-8, shift_jis, euc-jp) and, for UTF-8, bom. JSON is always UTF-8. Characters the encoding cannot represent are replaced with HTML character references (such as é). Replaced characters are listed in unrepresentableCharacters, at most the first 10 distinct ones, and the warning text carries the total count.
Filters, sorting, and row selection applied in a data table tab are view state rather than part of the dataset, so they cannot be passed here. Narrow the rows with datasets.derive() first when you need a subset.
datasets.create(name, options)
Creates a dataset with the given columns. Every cell starts as a missing value.
const result = await window.midas.datasets.create('Measurements', {
columns: [
{ name: 'subject', type: 'string' },
{ name: 'score', type: 'float64', scale: 'ratio' }
],
rowCount: 3
});
// result.data: { id: 'manual_...', name: 'Measurements', columnIds: ['col_0_...', 'col_1_...'], rowCount: 3 }
When scale is omitted the measurement scale is inferred from the column type. String columns take only the nominal and ordinal scales. Write values with datasets.setCellValues(). A column type is one of string, int64, float64, boolean, date, and datetime. Create an enum column as string, then convert it with datasets.setColumnSchema(), passing enumName.
datasets.setCellValues(id, values)
Sets individual cell values in a dataset that holds its own values. Accepts a dataset ID or a dataset name (case-insensitive).
const result = await window.midas.datasets.setCellValues('Measurements', [
{ row: 0, column: 'subject', value: 'A' },
{ row: 0, column: 'score', value: 12.5 }
]);
// result.data: { datasetId: 'manual_...', updatedCells: 2 }
row is a 0-based row position and column is a column ID or name. Written values cannot be undone. The dataset keeps no record of the previous values, so the original data is no longer recoverable from the project. Derived datasets, models, and report elements that depend on this dataset are invalidated and recomputed.
Editing cells in the data table converts the text you type into the column type, but this API does not convert values. value has to match the data type of the column. An int64 column takes integers from -9007199254740992 to 9007199254740992 (±2^53); values beyond that range are rejected because a JavaScript number cannot hold them exactly. null writes a missing value into a column of any type. In a string or enum column, an empty string is stored as null. A value for an enum column is matched against the enum definition after leading and trailing whitespace is removed, and the trimmed value is stored. A date column takes a YYYY-MM-DD string, and a datetime column takes an ISO 8601 string such as 2024-01-15T10:30:00Z; a datetime without a UTC offset is read as UTC, and both are stored in the same normalized form the data table uses. All input is validated before anything is written, so a single invalid entry leaves the dataset untouched.
Values in a derived dataset come from its operation, so they cannot be set cell by cell. The typed dataset that a CSV import creates is one of these, so address the <name> (raw) side, which holds the raw values, to correct an imported value.
datasets.excludeRows(datasetId, rows, reason?)
Excludes rows of a dataset that holds its own values, the same as Exclude this row in the Data Table. For what exclusion does to the dataset, see Excluding and Restoring Rows. Accepts a dataset ID or a dataset name (case-insensitive).
const result = await window.midas.datasets.excludeRows(
'Measurements (raw)',
{ filter: "subject IN ('P07', 'P12')" },
'Withdrew consent'
);
// result.data: { datasetId: 'ds_001', excludedRowNumbers: [6, 11], rowCount: 98 }
rows takes either an array of 0-based row indices or { filter } with an expression in the Data Table filter syntax, the same forms as datasets.selectRows(). A row index is the position of the row in the data, not in the sorted or filtered order of the Data Table. reason is shown in the Excluded Rows dialog and may be omitted. Leading and trailing whitespace is removed from reason, and a blank reason is stored as no reason. Derived datasets, models, and report elements that depend on the dataset are invalidated and recomputed without the excluded rows. A filter that matches no rows excludes nothing and still succeeds. All input is validated before anything is written, so an array with an index at or beyond the row count leaves the dataset untouched.
The returned excludedRowNumbers are the Row # values of the excluded rows, the row numbers shown in the Data Table. Excluded rows are no longer in the data and have no row index, so datasets.restoreRows() identifies them by Row #. Row # values stay the same when other rows are excluded or restored. A row that another operation excludes or deletes while this method is running is left out of excludedRowNumbers.
While a Data Table tab has the dataset open in Edit Mode, this method returns an EDIT_MODE_ACTIVE error. Edit Mode holds pending edits by row index until Done, and excluding rows in the meantime would move the rows those edits refer to. Ask the user to finish editing, then call the method again.
A derived dataset has no rows of its own to exclude. Exclude the rows from the <name> (raw) side of a CSV import instead; the typed dataset drops them when it is recomputed.
Every column of a <name> (raw) dataset is a string, so a filter on it compares values only as strings, and a numeric comparison such as weight > 200 returns an INVALID_INPUT error. To exclude rows by a numeric condition, select the rows on the typed dataset with datasets.selectRows(), and map them to the rows of the raw dataset with datasets.traceRowLineage().
const selected = await window.midas.datasets.selectRows('Measurements', { filter: 'weight > 200' });
const traced = await window.midas.datasets.traceRowLineage('Measurements', selected.data.selectedRows);
const raw = traced.data.contributions[0];
await window.midas.datasets.excludeRows(raw.datasetName, raw.rowIndices, 'Scale error');
datasets.listExcludedRows(datasetId)
Returns the excluded rows of a dataset that holds its own values. Accepts a dataset ID or a dataset name (case-insensitive).
const result = await window.midas.datasets.listExcludedRows('Measurements (raw)');
// result.data: { datasetId: 'ds_001', excludedRows: [
// { rowNumber: 6, reason: 'Withdrew consent', excludedAt: '2026-10-06T09:15:00.000Z', values: { subject: 'P07', weight: '250' } }
// ] }
excludedRows is in ascending Row # order. reason is null for rows excluded without a reason, excludedAt is the time of the exclusion in ISO 8601, and values holds the values at the time of the exclusion, keyed by column name.
datasets.restoreRows(datasetId, rowNumbers)
Restores excluded rows, the same as Restore, Restore Selected, and Restore All in the Excluded Rows dialog. Accepts a dataset ID or a dataset name (case-insensitive).
const result = await window.midas.datasets.restoreRows('Measurements (raw)', [6, 11]);
// result.data: { datasetId: 'ds_001', restoredRowNumbers: [6, 11], rowCount: 100 }
// Restore every excluded row
await window.midas.datasets.restoreRows('Measurements (raw)', 'all');
rowNumbers takes the Row # values returned by datasets.excludeRows() or datasets.listExcludedRows(), or 'all'. A restored row returns to its position in Row # order. Derived datasets, models, and report elements that depend on the dataset are invalidated and recomputed.
Restoring discards the reason and the time of the exclusion, and they cannot be recovered. The Excluded Rows dialog asks for confirmation before Restore All for this reason, but this method does not ask. Read them with datasets.listExcludedRows() first if they are needed.
A Row # that is not excluded returns an INVALID_INPUT error, and nothing is restored. A row that another operation, such as the Excluded Rows dialog, restores while this method is running is left out of restoredRowNumbers. 'all' on a dataset without excluded rows succeeds without changes. With 'all', restoredRowNumbers lists the rows that were excluded when the rows were restored. In Edit Mode, this method returns an EDIT_MODE_ACTIVE error for the same reason as datasets.excludeRows().
datasets.listRowComments(datasetId)
Returns the row comments of a dataset that holds its own values. Accepts a dataset ID or a dataset name (case-insensitive).
const result = await window.midas.datasets.listRowComments('Measurements (raw)');
// result.data: { datasetId: 'ds_001', comments: [
// { rowNumber: 3, row: 3, comment: 'Checked against the paper form', commentedAt: '2026-10-06T09:20:00.000Z' }
// ] }
comments is in ascending Row # order and includes comments on excluded rows, since excluding a row keeps its comment. row is the current 0-based row index in the data, or null when the row is excluded.
datasets.setRowComments(datasetId, rows, comment)
Sets the same comment on rows of a dataset that holds its own values, the same as Add comment and Edit comment in the Data Table. Accepts a dataset ID or a dataset name (case-insensitive).
const result = await window.midas.datasets.setRowComments('Measurements (raw)', [3], 'Checked against the paper form');
// result.data: { datasetId: 'ds_001', rowNumbers: [3] }
rows takes an array of 0-based row indices or { filter }, the same forms as in datasets.excludeRows(), so excluded rows cannot be targeted. A comment already on a row is replaced. Leading and trailing whitespace is removed from comment, and a blank comment returns an INVALID_INPUT error; remove comments with datasets.removeRowComments(). The returned rowNumbers are the Row # values of the commented rows.
Unlike the Data Table, this method works while the dataset is in Edit Mode. The Data Table hides Add comment and Edit comment in Edit Mode because the rows on screen can differ from the saved rows until Done. This method takes row indices in the saved data and attaches each comment to a Row #, so pending edits do not change which row gets the comment.
datasets.removeRowComments(datasetId, rowNumbers)
Removes row comments of a dataset that holds its own values. Accepts a dataset ID or a dataset name (case-insensitive).
const result = await window.midas.datasets.removeRowComments('Measurements (raw)', [3]);
// result.data: { datasetId: 'ds_001', removedRowNumbers: [3] }
rowNumbers takes the Row # values returned by datasets.listRowComments(). A Row # without a comment returns an INVALID_INPUT error, and nothing is removed.
datasets.remove(id)
Remove a dataset from the project. Accepts a dataset ID or name (case-insensitive). Closes any open tabs that reference the dataset, then cascade-deletes all dependent derived datasets and models.
await window.midas.datasets.remove('Iris');
datasets.fetch(id, options?)
Fetch row data from a dataset without side effects. Accepts a dataset ID or name (case-insensitive).
const result = await window.midas.datasets.fetch('Iris', { limit: 5, offset: 0 });
// result.data: {
// datasetId: 'ds_001', name: 'Iris', totalRows: 150, returnedRows: 5,
// columns: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'],
// rows: [{ sepal_length: 5.1, sepal_width: 3.5, ... }, ...]
// }
Use limit and offset to control the range of rows returned. When omitted, all rows are returned. The internal row number column (Row #) added by MIDAS is not included in the results. A derived dataset that has not been evaluated yet, such as one right after opening a saved project or right after overwriting a dataset it depends on, is evaluated before it is read.
datasets.buildMapping(datasetId, columnId, input)
Generate a value → canonical mapping dataset from unique values in a string or enum column.
// Key Collision: fullwidth normalize → lowercase
const result = await window.midas.datasets.buildMapping('ds_001', 'city', {
method: { type: 'key_collision', normalizers: ['fullwidth', 'case'] }
});
// result.data: { id: 'primary_...', changedCount: 3, valueCount: 7 }
// Nearest Neighbor: edit distance
const result2 = await window.midas.datasets.buildMapping('ds_001', 'city', {
method: { type: 'nearest_neighbor', method: 'levenshtein', threshold: 2 }
});
method.type must be 'key_collision' or 'nearest_neighbor'.
Key Collision applies deterministic normalizer functions in order to produce the initial canonical value. Specify the application order in normalizers. Available normalizers: 'trim' (strip surrounding whitespace), 'fullwidth' (NFKC normalization), 'kana' (katakana → hiragana), 'case' (lowercase), 'fingerprint' (lowercase, strip punctuation, deduplicate tokens, sort tokens).
Nearest Neighbor groups nearby values by distance and sets the most frequent value in each cluster as the initial canonical. method must be 'levenshtein' (edit distance). threshold (default: 2) sets the distance cutoff. Maximum unique values: 10,000.
Use overrides to override the canonical for specific values. Use name to set the dataset name (default: {source}_{column}_mapping).
The result is a Primary DataSet with value and canonical columns.
datasets.normalize(datasetId, columnId, mappingDatasetId, input?)
Normalize source data using a mapping dataset. Applies the value → canonical transformation via SQL JOIN.
const result = await window.midas.datasets.normalize('ds_001', 'city', mp.data.id, {
mode: 'replace'
});
// result.data: { id: 'derived_...', name: 'sales_normalized', rowCount: 100, columnCount: 5,
// lineage: { traceable: true } }
mode is 'add' (default) to append a {column}_normalized column, or 'replace' to replace the original column with COALESCE(canonical, original_value). Use name to set the output dataset name (default: {source}_normalized). Any dataset with value and canonical columns can be used as the mapping dataset. The lineage field of the result is the same static row-lineage check as datasets.derive().
If a derived dataset with the same name and the same definition already exists, normalize() reuses it instead of creating a new dataset. The definition is the same when the source dataset, the column, the mapping dataset, and mode all match. The result then has reused: true, and its id and name are those of the existing dataset. A same-name derived dataset with a different definition returns a DATASET_ALREADY_EXISTS error, and a same-name primary dataset returns a NAME_CONFLICT error.
datasets.traceRowLineage(datasetId, rowIndices)
Trace one hop of row-level lineage: find the parent dataset rows that contributed to the given rows of a derived dataset. For SQL-query datasets, it re-runs the derive query with the top-level aggregation removed and the parent Row # projected, then matches parent rows by the values of the requested rows. Datasets created by Convert Column Types are traced the same way, using an equivalent query rebuilt from the current conversion settings. Datasets built by filter (Save Filtered Data), crosstab, or reshape (wide/long) are traced by matching the parent rows by value, without a probe.
const result = await window.midas.datasets.traceRowLineage('sales_by_region', [0]);
// result.data: { traceable: true, contributions: [{ datasetId: 'ds_001', datasetName: 'sales', rowIndices: [0, 3, 7] }] }
rowIndices are 0-based row indices within the target dataset. Only non-negative integers are accepted; an empty array, a non-integer, or a negative value returns an INVALID_INPUT error. Out-of-range integers are ignored. The returned contributions lists the contributing rows per parent dataset, so a JOIN yields multiple entries. Each rowIndices is sorted and deduplicated. Passing several rows returns their contributions combined, without a per-row breakdown; call once per row to attribute rows individually.
Traceable SQL shapes: GROUP BY aggregations (including expression keys, GROUP BY by ordinal or output alias, and GROUP BY ALL, which keys on the output columns without aggregate functions), JOIN, FROM subquery and CTE, plain projection and WHERE filter, DISTINCT, and whole-table aggregation (all parent rows contribute). When a FROM subquery or CTE is a whole-table aggregation without GROUP BY, every parent row that passes its WHERE and JOIN contributes to each outer row. Datasets created by Convert Column Types are traceable the same way. Datasets built by filter, crosstab, or reshape (wide/long) are also traceable, matched by parent column values.
When a query cannot be traced, it returns traceable: false with a reason.
window-function: contains a window function or a QUALIFY clauseset-operation: contains a set operation such as UNIONnondeterministic: contains TABLESAMPLE or a LIMIT without ORDER BYnested-aggregation: a FROM subquery or CTE aggregates with GROUP BY, GROUP BY ALL, GROUP BY (), or HAVING. A whole-table aggregation without GROUP BY also counts when its FROM contains another subquery or CTE, or when it refers to an outer tablenested-distinct-limit: a FROM subquery or CTE uses DISTINCT followed by LIMIT, so the selected rows cannot be matched to parent rowsambiguous-group-keys: a GROUP BY key is not in the output, so its value cannot be read per row. This includes GROUP BY ALL where*becomes a keyno-parent-table: the FROM cannot be resolved to a registered parent datasetno-parent-dataset: the dataset is derived but has no parent dataset to trace (for example a query with no dataset in its FROM, such asSELECT 1)not-derived: the target is not a derived datasetunsupported-operation: the derived dataset was built by an unsupported operation (other than a SQL query, Convert Column Types, filter, crosstab, or reshape)parse-failed: the query's structure could not be analyzed
Some reasons include a detail string with specifics: the table name that could not be resolved (no-parent-table), the original operation type (unsupported-operation), TABLESAMPLE, or the name of the subquery or CTE where the shape was detected (nested-aggregation, nested-distinct-limit).
datasets.openContributingRows(datasetId, rowIndices, targetDatasetId?)
Drill down from a derived dataset to its contributing rows: trace row-level lineage with the same rules as traceRowLineage, then open the contributing parent rows in a Contributing rows tab. For a JOIN, one tab is opened per contributing parent.
Pass targetDatasetId (a dataset ID or name) to jump straight to a specific ancestor: the hops are composed internally and only that ancestor's contributing-rows view is opened (no intermediate tabs). targetDatasetId must be one of the traceable ancestors of the dataset; otherwise an INVALID_INPUT error is returned. Omitting it drills one hop to the immediate parent(s).
Calling it again on the rows of an opened Contributing rows view drills one more hop up the parent chain (toward the original data). The chain stops when it reaches a primary dataset or a derived dataset with no parent to trace, returning traceable: false with reason: 'no-parent-dataset'. Repeating this walks step by step from an aggregate table back to the original data.
The temporary view shown in a Filtered Data tab also works as a starting point. That view keeps its filter condition and the dataset it was filtered from even before it is saved, so the datasetId of the tab returned by tabs.list() can be passed directly.
const result = await window.midas.datasets.openContributingRows('sales_by_region', [0]);
// result.data: { traceable: true, opened: [{ datasetId: 'dataset-ephemeral-...', parentDatasetId: 'ds_001', parentName: 'sales', rowCount: 12 }] }
// Drill one more hop up from the opened Contributing rows view
const next = await window.midas.datasets.openContributingRows(result.data.opened[0].datasetId, [0]);
// Jump straight to a specific ancestor (opens only that ancestor's view, no intermediate tabs)
const jumped = await window.midas.datasets.openContributingRows('sales_by_region', [0], 'sales_raw');
The handling of rowIndices (0-based integers; an empty array, a non-integer, or a negative value returns an INVALID_INPUT error, and out-of-range integers are ignored) and the traceable/untraceable decision rules are the same as traceRowLineage. When the rows cannot be traced, nothing is opened and the result is traceable: false with a reason. Because this opens a tab, it returns a NO_CONTAINER error when no tab container is active (for example, when called outside a project view).
The opened ephemeral datasets carry their lineage. "Save as Dataset" on the tab promotes one to a persistent dataset (a RowLineageOperation) that re-traces the contributing rows on each re-evaluation. When the view depends on other unsaved Filtered Data or Contributing rows views, for example when it was traced from an unsaved Filtered Data view, the save also stores those views as datasets and points the saved lineage at them, so a saved dataset never refers to an Ephemeral Dataset. A view whose recipe matches an existing derived dataset is not duplicated; the saved lineage refers to the existing dataset.
datasets.selectRows(datasetId, rows)
Selects rows of a dataset. It writes the same selection state the UI reads, so the selected rows are highlighted in the Data Table, Statistics, and Graph Builder tabs showing that dataset. The call corresponds to the UI operations described in How Row Selection Works as follows. An array of 0-based row indices makes the same selection as clicking rows in the Data Table, and { filter } makes a selection that carries a condition, like a rectangle selection in Graph Builder. An empty array clears the selection.
// Select by row index
await window.midas.datasets.selectRows('bike_daily', [0, 1, 2]);
// Select by condition (same syntax as the Data Table filter)
const result = await window.midas.datasets.selectRows('bike_daily', { filter: 'cnt > 8000' });
// result.data: { datasetId: 'ds_001', selectedRows: [ ... ], selectedColumns: [], selectionExpression: '"cnt" > 8000' }
// Clear the selection
await window.midas.datasets.selectRows('bike_daily', []);
Row indices passed in rows must be non-negative integers below the row count of the dataset. A non-integer, a negative value, or a value at or above the row count returns an INVALID_INPUT error. Duplicates are removed and the indices are stored in ascending order. The expression in rows.filter is validated and evaluated against the columns of the dataset. A syntax error, a reference to a column the dataset does not have, a value that cannot be read as the column's type, NULL written as a value, LIKE or ILIKE on a column other than string and enum, or a LIKE or ILIKE pattern that is not a string returns an INVALID_INPUT error.
When the selection is made by condition, the result and the selection state carry selectionExpression. It is not the string you passed but a normalized form with column names in double quotes, and it can be passed back to rows.filter as is. A selection with a condition can be opened as a Filtered Data tab by right-clicking a chart in the Statistics tab or a graph in Graph Builder and choosing Open as Filtered Data, and Save as Dataset there promotes it to a derived dataset. A selection made by row index carries no condition, and that menu item is not shown.
Besides persistent datasets, the temporary views shown in a Filtered Data tab or a Contributing rows tab can be targeted. Pass the datasetId of the tab returned by tabs.list().
The selection is view state and is not saved in the project file. Statistics in the Statistics tab and model estimation are not affected by it. To compute on a subset of rows, create a derived dataset with datasets.filter() or datasets.derive().
datasets.getSelection(datasetId)
Returns the current selection state of a dataset. Selections made in the UI (clicking rows or columns in the Data Table, rectangle selection in Graph Builder, clicking histogram bins in Statistics) and selections made with selectRows() are read from the same state.
const result = await window.midas.datasets.getSelection('bike_daily');
// result.data: { datasetId: 'ds_001', selectedRows: [3, 8, 21], selectedColumns: ['cnt'], selectionExpression: '"cnt" > 8000' }
selectedRows is an ascending array of 0-based row indices into the dataset. When the Data Table narrows the rows with a filter, the indices still refer to positions in the original dataset, not positions after the filter. selectedColumns holds the names of the selected columns. selectionExpression is present only when the selection was made by condition. When nothing is selected, selectedRows and selectedColumns are empty arrays. As with selectRows(), datasetId also accepts a temporary view.
enums
enums.create(name, values)
Create an enum definition. Up to 50 values can be specified.
const result = await window.midas.enums.create('color', ['red', 'green', 'blue']);
// result.data: { name: 'color', valueCount: 3 }
enums.list()
List all enum definitions.
const result = await window.midas.enums.list();
// result.data: [{ name: 'color', values: ['red', 'green', 'blue'] }, ...]
enums.update(name, values)
Update the values of an existing enum definition.
await window.midas.enums.update('color', ['red', 'green', 'blue', 'yellow']);
Up to 50 values can be specified. Removing values is rejected with ENUM_VALUE_MISMATCH if any dataset still contains the removed values in a column of this enum type. This preserves the invariant that enum column values are always in the definition or NULL. Use Convert Column Types first to null-out or exclude those values, or keep the values in the enum definition. A value that remains only in a column of an ephemeral dataset, such as the one a Filtered Data tab holds, is not rejected and becomes null in that column.
enums.remove(name)
Remove an enum definition. Returns an ENUM_IN_USE error if columns still reference this enum. When only columns of an ephemeral dataset reference it, the removal is not rejected and those columns become string columns.
await window.midas.enums.remove('color');
tabs
tabs.list()
List all open tabs.
const result = await window.midas.tabs.list();
// result.data: [{ id, type, title, paneId, isActive, datasetId?, targetDatasetId?, editingDatasetId?, reportId?, modelId? }, ...]
paneId is the ID of the pane holding the tab. isActive is true for the tab shown at the front of that pane. One tab is at the front of each pane, so with several panes open, several tabs have isActive: true. Use layout.get() to see how the panes are arranged.
When a tab displays or references a resource, the entry carries its ID as datasetId (the dataset the tab displays), targetDatasetId (the dataset an analysis tab works on), editingDatasetId (the dataset whose operation an edit tab edits), reportId, or modelId. These keys appear only on tabs that have them. Statistics and Selected Columns tabs, however, hold no dataset and show the dataset of the active Data Table tab, so datasetId does not tell which dataset these two tabs show. Filter by these IDs to tell which tabs use a given dataset, report, or model.
// Find the tabs displaying or referencing dataset ds_001
const tabs = (await window.midas.tabs.list()).data.filter(
t => t.datasetId === 'ds_001' || t.targetDatasetId === 'ds_001' || t.editingDatasetId === 'ds_001'
);
tabs.activate(tabId)
Bring an open tab to the front, as clicking the tab does. The pane holding the tab becomes the active pane, so later calls to tabs.open() put their new tab there.
await window.midas.tabs.activate('tab_001');
tabs.open(config)
Open a new tab. datasetId accepts a dataset ID or name (case-insensitive). For a graph-builder tab, the dataset is bound to the tab, so it shows in the dataset selector and in tabs.getGraphBuilder().
Some tabs are opened only once per workspace. The content of report, model-detail, glm-diagnostics, sql-query-viewer, computed-column, column-type-conversion, and orthogonal-polynomials is determined by the target you pass, and the content of project-overview, project-lineage, project-diff, enum-definition, and help is determined by the tab type alone. When a tab with the same content is already open, it is brought to the front instead of a new tab being created, and the returned id is the ID of that existing tab. If that tab sits in another pane, that pane becomes the active one. Later tabs.open() calls then put their new tab there. Settings other than the target (title, datasetId, tabViewState) are not applied to the existing tab; they are reported in warnings.
The statistics and selected-columns tabs are opened only once per pane. They are meant to sit next to a data table, so activating another pane and calling tabs.open() again opens a second one there. When the active pane already holds a tab of the same type, that tab is brought to the front. These two tabs have no dataset of their own: they show the dataset of the active Data Table tab, including its filter. Passing datasetId for them returns an INVALID_INPUT error. To see the statistics of a specific dataset, open that dataset in a data-table tab first.
Passing the ID or name of a derived dataset as editingDatasetId opens the same edit tab as Edit Operation... in Project Overview. Saving in the edit tab updates the definition of the operation while keeping the dataset ID. Set type to the tab that edits the dataset's operation, as listed in the table below. The operation type is available as operation.type from datasets.describe().
| Operation type | Tab type |
|---|---|
sql_query | sql-editor |
synthetic_generation | synthetic-data-generator |
crosstab | crosstab |
dummy_coding | dummy-coding |
wide_to_long, long_to_wide | reshape |
computed_column | computed-column |
column_type_conversion | column-type-conversion |
filter | filtered-data |
tabs.open() rejects datasets that cannot be edited and settings that do not fit an edit tab. A type that differs from the table returns an INVALID_TAB_TYPE_FOR_OPERATION error, whose suggestion names the right type. A primary dataset, or a derived dataset whose operation cannot be edited, such as one saved from a model, returns an INVALID_INPUT error. The content of an edit tab is determined by the operation being edited alone, so passing datasetId, modelId, reportId, initialQuery, initialOutputName, or tabViewState together with it also returns an INVALID_INPUT error. Edit tabs of sql-editor, synthetic-data-generator, computed-column, and column-type-conversion for the same dataset are opened only once per workspace.
Every tab not listed above opens a new tab on each call. This covers tabs that hold their own input, such as sql-editor opened without a dataset to edit.
// Open Graph Builder
const result = await window.midas.tabs.open({
type: 'graph-builder',
title: 'My Graph',
datasetId: 'ds_001'
});
// result.data: { id: 'tab_...', type: 'graph-builder', title: 'My Graph' }
// Open SQL Query Editor
const result2 = await window.midas.tabs.open({
type: 'sql-editor',
initialQuery: 'SELECT * FROM Iris LIMIT 10',
initialOutputName: 'Preview'
});
// Open the edit tab of a derived dataset created from SQL
const result3 = await window.midas.tabs.open({
type: 'sql-editor',
editingDatasetId: 'Iris Subset'
});
Available tab types:
| Type | Description |
|---|---|
graph-builder | Graph Builder |
sql-editor | SQL Query Editor |
synthetic-data-generator | Synthetic Data Generator |
glm | GLM |
glmm | GLMM |
random-forest | Random Forest |
linear-regression | Linear Regression |
pca | PCA |
statistics | Descriptive Statistics |
crosstab | Crosstab |
anova | ANOVA |
kaplan-meier | Kaplan-Meier |
cox-regression | Cox Regression |
arima | ARIMA |
data-table | Data Table |
report | Report (requires reportId) |
computed-column | Computed Column |
dummy-coding | Dummy Coding |
orthogonal-polynomials | Orthogonal Polynomials |
reshape | Reshape |
column-type-conversion | Type Conversion |
enum-definition | Enum Definition |
project-overview | Project Overview |
project-lineage | Project Lineage |
selected-columns | Selected Columns |
filtered-data | Filtered Data |
model-detail | Model Detail (requires modelId) |
glm-diagnostics | GLM Diagnostics (requires modelId) |
glm-prediction | GLM Prediction (requires modelId) |
sql-query-viewer | SQL Query Viewer |
variant-normalization | Normalize Variants |
apply-mapping | Apply Mapping |
project-diff | Project Diff |
help | Help |
report tabs require reportId, and model-detail, glm-diagnostics, and glm-prediction tabs require modelId. Set modelId to the ID of a saved model obtained from models.list(). Omitting it returns an INVALID_INPUT error.
The column selection that the selected-columns tab displays is view state for humans operating the screen. The API can read it with datasets.getSelection() but has no operation to write it. Agents can get column statistics with datasets.profile() instead.
// Open a Model Detail tab
const result3 = await window.midas.tabs.open({
type: 'model-detail',
modelId: 'model_001'
});
The glm-diagnostics tab opens both GLM and linear regression (linear_regression) models. For a linear regression model, it hides the Deviance/Pearson residual toggle and changes the heading to Residual Diagnostics.
models.run() supports nine model types: glm, glmm, random_forest, arima, linear_regression, anova, pca, kaplan_meier, and cox_regression. The corresponding tabs (glm, glmm, random-forest, arima, linear-regression, anova, pca, kaplan-meier, cox-regression) plus other analysis tabs (crosstab, statistics) can all be opened with tabs.open(), but only the nine types above can be run programmatically — the rest must be configured and run through the GUI.
tabs.duplicate(tabId)
Duplicate an analysis tab, opening an independent copy next to it. The copy keeps the current settings (predictors, orders, response, and so on) but not the run results or the saved-model state, so you can change a parameter and re-run to compare fits side by side. Only analysis tabs can be duplicated: glm, glmm, anova, linear-regression, cox-regression, kaplan-meier, pca, random-forest, and arima. Other tab types return an INVALID_TAB_TYPE_FOR_OPERATION error. In the UI, the same action is available by right-clicking a tab and choosing Duplicate Tab.
const result = await window.midas.tabs.duplicate('arima_001');
// result.data: { id, type, title }
tabs.close(id)
Close a tab.
await window.midas.tabs.close('tab_001');
tabs.closeOthers(keepTabId)
Close all tabs except the specified one.
const result = await window.midas.tabs.closeOthers('tab_001');
// result.data: { closedCount: 3 }
tabs.getGraphBuilder(tabId)
Get Graph Builder tab configuration.
const result = await window.midas.tabs.getGraphBuilder('tab_001');
// result.data: { tabId, graphType, datasetId, config, aspectRatio,
// availableColumns, lineageTargetDatasetId, availableLineageTargets, renderWarnings? }
renderWarnings holds the diagnostics shown above the Graph Builder preview for the current configuration, one message per element. Examples are a group left out because no values remained to plot, an x position dropped by position fill because the composition is undefined there, a group whose statistic or interval could not be computed together with the reason, points dropped because they could not be computed, duplicate X values within a group, a shape or linetype aesthetic with more categories than distinct assigned marker shapes or line styles, an area layer with position stack or fill whose series has both positive and negative values, a Boxplot statistic with a group that has fewer than 5 valid observations or an interquartile range of 0, a Label layer whose labelContent field has no value on any of its points, and a layer whose filter expression cannot be parsed. Messages about left-out and not-computed groups name the values that identify each group in quotes. The diagnostics listed above do not prevent the graph from rendering. A layer that reports the labelContent diagnostic draws none of its labels, and a layer that reports the filter diagnostic draws nothing, while the rest of the graph is rendered. The one case where the graph is not rendered is the facet panel limit: when a warning reports that the panel count exceeds the limit configured in Settings (Max Facet Panels), the graph is not rendered. The field is omitted when there is no diagnostic. It is evaluated only when graphType is 'custom' and a dataset is selected. A diagnostic that depends on the rendered size (a legend placed at right or left that does not fit the graph height, so some categories or a whole legend are not shown, or one that does not fit the graph width, so no legend is shown) appears only in the on-screen warning and is not included in this field. For a graph with facets, diagnostics are evaluated per panel and arrive in the same form as the preview warning strip: each line is prefixed with the title of the panel that reported it, and a warning common to all panels is merged into a single All panels: line.
tabs.addGraphLayer(tabId, layer, options?)
Add a layer to a custom graph. Only works when graphType is 'custom'.
On a tab in multi-panel mode, name the target panel with panelIndex in the third argument options. Only the layers of each panel are rendered in multi-panel mode, so a call that omits panelIndex fails with a message asking for the panel. On success, result.data holds layerIndex (the index within that panel) and panelIndex. See panels under configureGraph for how to set up multi-panel mode.
await window.midas.tabs.addGraphLayer('tab_001', {
geom: { type: 'line' },
aes: { y: 'pop' }
}, { panelIndex: 1 });
// result.data: { layerIndex: 1, panelIndex: 1 }
const result = await window.midas.tabs.addGraphLayer('tab_001', {
geom: { type: 'point' },
aes: { x: 'sepal_length', y: 'sepal_width', color: 'species' }
});
// result.data: { layerIndex: 0 }
Aesthetic mappings (aes) accept column names or column IDs. Column names are resolved case-insensitively. Available properties are x, y, color, fill, stroke, size, shape, alpha, linetype, ymin, ymax, label, group, and weight. Not all properties apply to every geom type — for example, Point and Line do not support fill. weight maps a numeric column of frequency weights; it does not affect how the geom is drawn, and only stats that support weights (roc) read it. aes accepts column references only; set a fixed color, size, or opacity in geom.defaults, not in aes. The keys each geometry accepts in geom.defaults are listed in the Custom Graph Reference. Passing a fixed value through aes (such as { fixedColor: '#FF0000' } or a number) returns an INVALID_INPUT error. When stats is omitted, identity is used by default. When position is omitted, bar geom defaults to { type: "stack" } (stacked); other geoms default to identity. The allowed position types are determined by the combination of the geom and the primary stat (the first entry of stats) — for example, line allows only identity, and the Boxplot stat cannot be combined with stack / fill. Specifying a disallowed position returns an INVALID_INPUT error. When position is omitted and the geom default cannot be combined with the stat, identity is set and a warning is returned. Use scales to configure per-layer scales (color, fill, shape, linetype, size, alpha). See configureGraph for details.
Before it is saved, the layer goes through the per-layer validation the renderer runs. The validation covers the required aesthetics of each geom (such as label for the Text geom, or ymin / ymax for Error Bar), the type of the column mapped to each axis, and the compatibility between the palette and the scale type. When the axis scale that applies to the layer has type: 'log' or type: 'sqrt', the validation also checks that the column mapped to x or y on that axis has no values outside the scale's valid range, and that the domain specified for that axis lies within the range. The valid range is finite values greater than 0 for log, and finite values of 0 or more for sqrt. As in the renderer, this check reads only the rows that remain after the tab's filter expression (filterExpression) is applied. When the data of a derived dataset has not been computed yet, the column values cannot be read, so only the specified domain is checked and a warning says that the values were not checked. A layer that fails validation is still saved and the call succeeds, so a layer can be built up over several calls. Anything that failed validation is reported in warnings, and the graph does not render until it is fixed.
When using the Label geom ({ type: 'label' }) with aggregating stats (summary, count, bin, etc.), the column mapped via aes.label is lost during aggregation. Use geom.defaults.labelContent to reference stat output variables instead.
await window.midas.tabs.addGraphLayer(tabId, {
geom: {
type: 'label',
defaults: {
labelContent: { field: '$y', format: '.1f', prefix: 'Mean: ' }
}
},
stats: [{ type: 'summary', params: { outputs: [{ fun: 'mean', to: 'y' }] } }],
});
labelContent properties:
| Property | Type | Description |
|---|---|---|
field | string | Field to display. A stat variable ($x, $y, $n, etc.), a column name, or a column ID. Column names are resolved case-insensitively. A value that cannot be resolved returns a COLUMN_NOT_FOUND error |
format | string | d3-format specifier (e.g. .2f, ,.0f) |
prefix | string | String prepended to the formatted value |
suffix | string | String appended to the formatted value |
When labelContent is set, aes.label is not required.
The Text geom ({ type: 'text' }) requires aes.label. Only the Label geom reads labelContent; setting it on a Text geom does not change the displayed text.
Layers also accept these optional properties:
| Property | Type | Description |
|---|---|---|
name | string | Display name for the layer |
filter | string | Filter expression that narrows this layer's data. Uses the same syntax as the Data Table filter input (see The Data Table Tab) and is validated by the same rules, so an expression with a syntax error, an unknown column, a value that does not match the column's type, NULL written as a value, LIKE or ILIKE on a column other than string and enum, or a LIKE or ILIKE pattern that is not a string returns INVALID_INPUT |
visible | boolean | Show or hide the layer (default true) |
yAxis | 'primary' | 'secondary' | Which Y axis to use |
showLegend | 'auto' | 'show' | 'hide' | Legend visibility for this layer |
clickSelection | boolean | Enable click-to-select on data points |
tooltip | array | { content: 'encoding' } | Content shown when hovering a data point. See below |
Set tooltip to an array of field definitions to show specific values on hover, or to { content: 'encoding' } to auto-generate fields from the layer's aes. When the layer's position is fill, the auto-generated $y field is labeled with (% of total) and its value is shown as a percentage. A $y field given in the array is also shown as a percentage when format is omitted, and labeled % of total when label is omitted. The denominator is the sum of absolute values at each x position. With the same setting, the auto-generated title of the proportion axis also gets the (% of total) suffix and its ticks use percent format. See Stack as 100% Proportions for details. Omitting tooltip shows no tooltip.
await window.midas.tabs.addGraphLayer(tabId, {
geom: { type: 'point' },
aes: { x: 'sepal_length', y: 'sepal_width' },
tooltip: [
{ field: '$x', label: 'Sepal Length' },
{ field: 'species' }
]
});
Tooltip field properties:
| Property | Type | Description |
|---|---|---|
field | string | Stat variable ($x, $y, $n, etc.) or a column name or ID |
label | string | Custom label (omit for no label, value only) |
format | string | d3-format specifier (e.g. .2f, ,.0f) |
type | 'datetime' | 'date' | Formats the field as a date or datetime string |
See Custom Graph Reference for the list of geom, stat, and position types. Each Statistic's params are documented there with their accepted values and defaults. For facets, coordinates, and other graph-level options, see Custom Graph.
tabs.updateGraphLayer(tabId, layerIndex, layer, options?)
Partially update an existing layer. Only the specified fields are changed; omitted fields retain their current values. The fields are the same as in addGraphLayer: name, geom, aes, stats, position, scales, tooltip, filter, visible, yAxis, showLegend, and clickSelection can be changed with the same meaning. On a tab in multi-panel mode, name the target panel with panelIndex in the fourth argument options; layerIndex is then the index within that panel.
await window.midas.tabs.updateGraphLayer('tab_001', 0, {
geom: { type: 'line' }
});
When the geom or stats change and the current position (or the geom default when position is unset) is not allowed by the resulting geom and stat combination, position is automatically reset to identity and a warning is returned. Explicitly specifying a disallowed position returns an INVALID_INPUT error.
The updated layer also goes through the same validation as addGraphLayer. The validation looks at the result of merging the update into the existing layer, not at the update itself. Required aesthetics that a geom change leaves missing, existing aes properties the new geom does not read (they stay in the configuration but are not rendered), and a layer scaleType that no longer matches the column mapped by the replaced aes are all reported in warnings. An update that changes aes or yAxis also re-runs the check of the axis scale's valid range, because these two fields decide which column is mapped to an axis and whether the Y or the Y2 scale applies.
Pass scales: null to remove layer-specific scales and fall back to the default scales. Pass position: null to reset position to the default (unset). Pass tooltip: null to remove the tooltip. Pass filter: null or an empty string to remove the layer filter.
tabs.removeGraphLayer(tabId, layerIndex, options?)
Remove a layer. On a tab in multi-panel mode, name the target panel with panelIndex in the third argument options; layerIndex is then the index within that panel. The last layer of a panel can be removed, but the graph does not render until every panel has a layer again, and warnings reports this.
await window.midas.tabs.removeGraphLayer('tab_001', 0);
tabs.moveToPane(tabId, toPaneId)
Move a tab to a different pane. Take the ID of the destination pane from layout.get(), or, when you create a pane to lay tabs out side by side, from what layout.split() returns.
await window.midas.tabs.moveToPane('tab_001', 'pane_002');
tabs.setDataset(tabId, datasetId)
Switch a tab's dataset. datasetId accepts a dataset ID or name (case-insensitive). For a graph-builder tab, the new dataset shows in the dataset selector and in tabs.getGraphBuilder(). statistics and selected-columns tabs cannot be the target of this method, because they show the dataset of the active Data Table tab. Calling it on one of them returns an INVALID_INPUT error. Of the other tab types, this method accepts only those whose open tab follows the change: data-table, sql-query-viewer, graph-builder, crosstab, reshape, variant-normalization, glm, glmm, linear-regression, random-forest, pca, anova, arima, kaplan-meier, and cox-regression. Any other tab type returns an INVALID_INPUT error, because an open tab of that type does not follow the change.
computed-column, dummy-coding, orthogonal-polynomials, and column-type-conversion tabs belong to this rejected group. Their dataset can be chosen again in the tab's own selector, but an open tab of these types does not follow a change made through this method. The Agent API runs the same operations on a dataset directly with datasets.addColumns(), datasets.dummyCode(), datasets.addOrthogonalPolynomials(), and datasets.setColumnSchema().
Settings that refer to columns follow the same rules as choosing a dataset in the tab's own selector. A crosstab tab keeps fields whose name exists in the new dataset (value fields also need a numeric column), and a reshape tab keeps column settings whose name exists there. A graph-builder tab clears its filter expression and lineageTargetDatasetId. For a Custom Graph, the layers are also checked against the new dataset for values outside the valid range of a log or sqrt axis, as in configureGraph, and problems are reported in result.warnings. Tabs that select variables by column ID, such as regression, survival analysis, and PCA, clear their variable selections. Settings that were dropped are listed in result.warnings. Passing the tab's current dataset leaves its settings unchanged.
const result = await window.midas.tabs.setDataset('tab_001', 'ds_002');
// result.warnings: ["Removed fields not present in the selected dataset: region"]
tabs.configureGraph(tabId, config)
Configure a Graph Builder tab in one call. Select the chart type with graphType. datasetId accepts a dataset ID or name (case-insensitive); pass an empty string to clear it. Column names are resolved case-insensitively. Properties not recognized by GraphConfigInput or LayerDefInput are reported in result.warnings.
Layers passed in layers go through the same validation as addGraphLayer, and problems such as missing required aesthetics are reported in warnings. When layers is omitted and only globalAes is replaced, the existing layers are re-validated against the new globalAes. When any of scales, layers, panels, globalAes, datasetId, or filterExpression is passed, every layer of the graph also goes through the check of the axis scale's valid range, against the configuration and filter expression as saved. Hidden layers are checked too, because the renderer also rejects a hidden layer that fails this check. For a layer inside a panel, the panel's yScale, when set, takes the place of scales.y in this check. In all of these cases, each warning from this validation starts with the label of the layer it concerns. The one exception is the warning that column values were not checked because the dataset's data has not been computed yet; it concerns the whole graph and has no label. The label is the layer's name, and a layer without a name is labeled by its position counted from 0, as in Layer 0. For a layer inside a panel of panels, the label joins the panel label and the layer label with /, as in "Top"/Layer 0, and a panel without a name is labeled by its position, as in panels[1]. filterExpression sets a filter expression that narrows the rows of the whole graph, in the same syntax as the Data Table filter input.
await window.midas.tabs.configureGraph('tab_001', {
graphType: 'custom',
datasetId: 'ds_001',
layers: [
{ geom: { type: 'point' }, aes: { x: 'weight', y: 'height', color: 'group' } }
],
aspectRatio: '4:3'
});
Use coordinates to set the coordinate system. 'flipped' swaps the X and Y axes (useful for horizontal bar charts with long category labels). 'cartesian' resets to the default Cartesian coordinates.
await window.midas.tabs.configureGraph('tab_001', {
coordinates: 'flipped'
});
Use scales to configure axis scales. When calling configureGraph with scales, only the specified axes are updated; unspecified axes retain their existing settings. An axis you do not specify gets its scale inferred from the column mapped to it: date and datetime columns become time, categorical columns become categorical, and numeric columns become linear. On an ordinal enum column, the inferred categorical scale gets limits set to the enum definition order. Scales you specify explicitly take precedence.
await window.midas.tabs.configureGraph('tab_001', {
scales: { y: { type: 'log', title: 'Log scale' } }
});
Each axis (x, y, y2) in scales accepts:
| Property | Type | Description |
|---|---|---|
type | 'linear' | 'log' | 'sqrt' | 'time' | 'categorical' | Scale type |
title | string | Axis title |
domain | { min?, max? } | Range for continuous scales (not applicable for categorical) |
tickCount | number | Number of ticks (not applicable for categorical) |
limits | string[] | Category display order (categorical only) |
breaks | string[] | Subset of categories to display (categorical only) |
labels | Record<string, string> | Custom display names for categories (categorical only) |
labelRotation | 'auto' | 0 | 45 | 90 | Label rotation angle |
zoomEnabled | boolean | false keeps this axis fixed during zoom and pan (not applicable for categorical; default true) |
titlePadding | number | Gap between the axis title and the tick labels (pixels; default 15 on the X axis and 6 on the Y axis) |
labelPadding | number | Gap between the tick labels and the tick marks (pixels; default 3) |
limits sets the category display order on the axis. The values listed in limits come first in that order, and values not listed follow them. Without limits, categories follow the enum definition order on enum columns, numeric order on numeric columns, and string comparison order on every other column.
Per-layer scales are set via layers[].scales, which accepts the color, fill, shape, linetype, size, and alpha aesthetics. Per-layer tooltips are set via layers[].tooltip (see addGraphLayer).
await window.midas.tabs.configureGraph('tab_001', {
graphType: 'custom',
datasetId: 'ds_001',
layers: [{
geom: { type: 'tile' },
aes: { x: 'col_x', y: 'col_y', fill: 'col_value' },
scales: { fill: { scaleType: 'sequential', paletteId: 'viridis' } }
}]
});
Each color / fill scale accepts:
| Property | Type | Description |
|---|---|---|
scaleType | 'categorical' | 'sequential' | 'diverging' | 'threshold' | Color scale type |
paletteId | string | Palette ID. See Custom Graph Reference for available values |
domain | { min?, max?, center? } | Domain for continuous scales |
legendPosition | 'right' | 'left' | 'top' | 'bottom' | 'none' | Legend position |
legendTitle | string | Legend title |
thresholds | number[] | Threshold values (threshold type) |
thresholdColors | string[] | Colors for each region (threshold type, length = thresholds.length + 1) |
thresholdVariable | 'x' | 'y' | Variable used for threshold comparison (threshold type, default 'y') |
The effective scaleType is determined from the specified value and the column type. When omitted, fill uses sequential for numeric columns and categorical for other columns, while color always uses categorical. When sequential or diverging is specified for a column that is not numeric, MIDAS gives priority to the column type, renders the aesthetic with a categorical scale, and reports the mismatch in the warnings of the configuring call's response and in the diagnostics (renderWarnings). However, when a diverging paletteId is also specified, the palette is incompatible with the effective categorical scale, so validation fails and the graph is not rendered. A sequential palette remains usable with a categorical scale and does not cause this error. The incompatible combination is reported in the warnings of the configuring call's response, and reports.addGraph / reports.updateElement return renderStatus: 'error'.
Each shape / linetype scale accepts:
| Property | Type | Description |
|---|---|---|
shapes | ('circle' | 'square' | 'triangle' | 'diamond' | 'cross' | 'plus')[] | Shapes assigned to categories, in order (shape aesthetic) |
linetypes | ('solid' | 'dashed' | 'dotted')[] | Line styles assigned to categories, in order (linetype aesthetic) |
legendPosition | 'right' | 'left' | 'top' | 'bottom' | 'none' | Legend position |
legendTitle | string | Legend title |
The first entry in the list goes to the first category. When there are more categories than entries, the list repeats from the start. Unknown shape and line style names are dropped and reported in warnings. The legend position applies to the whole graph: it is taken from the first legend-showing layer, searching its color, fill, shape, and linetype scales in that order. Only aesthetics with a column mapped are searched, so a legendPosition set on an aesthetic without a column does not affect the legend and is reported in warnings.
The size / alpha scales accept a range only. Neither aesthetic draws a legend.
| Property | Type | Description |
|---|---|---|
range | { min?, max? } | Range of drawn values. Defaults are 2-20 (pixels) for size and 0.2-1 for alpha |
The size range stays at 0 or above and the alpha range stays within 0 to 1. Values outside those bounds are clamped and reported in warnings.
await window.midas.tabs.configureGraph('tab_001', {
graphType: 'custom',
datasetId: 'ds_001',
layers: [{
geom: { type: 'point' },
aes: { x: 'weight', y: 'mpg', shape: 'origin', size: 'horsepower' },
scales: {
shape: { shapes: ['square', 'triangle', 'diamond'], legendTitle: 'Origin' },
size: { range: { min: 4, max: 30 } }
}
}]
});
Use facets to split the graph into panels by one or two categorical variables. Two modes are supported: wrap (single variable, auto-arranged panels) and grid (row and/or column variables).
// Facet wrap: split by one variable
await window.midas.tabs.configureGraph('tab_001', {
facets: { type: 'wrap', variable: 'species', ncol: 3, scales: 'free_y' }
});
// Facet grid: split by row and/or column variables
await window.midas.tabs.configureGraph('tab_001', {
facets: { type: 'grid', rows: 'region', cols: 'year' }
});
// Remove facets
await window.midas.tabs.configureGraph('tab_001', { facets: null });
Use panels to set up multi-panel mode. Multi-panel mode is the same feature as Enable Multi-Panel Mode in the Multi-Panel section of the Graph Builder: it stacks several panels vertically, shares the X axis among them, and moves zoom and pan together. It suits graphs that show several quantities against the same X axis, such as control charts and time series decompositions. Each panel holds the layers to draw in layers. yScale is the panel's own Y axis scale and accepts the same properties as each axis in scales; a panel without it uses scales.y. heightRatio is the relative height (default 1), and name is the label shown at the top left of the panel.
In multi-panel mode only the layers of each panel are rendered; the graph-level layers and facets are not. A call that passes layers or facets to a tab with panels therefore fails. Edit the layers of a panel with addGraphLayer / updateGraphLayer / removeGraphLayer, passing panelIndex. Setting panels removes any existing facets, which warnings reports. Pass panels: null to return to a single panel; when layers is not passed in the same call, the layers of all panels are concatenated in order into the graph's layers.
await window.midas.tabs.configureGraph('tab_001', {
graphType: 'custom',
datasetId: 'ds_001',
globalAes: { x: 'year' },
panels: [
{ name: 'Life expectancy', layers: [{ geom: { type: 'line' }, aes: { y: 'lifeExp' } }], heightRatio: 2 },
{ name: 'Population', layers: [{ geom: { type: 'line' }, aes: { y: 'pop' } }], yScale: { type: 'log' } }
]
});
// Return to a single panel
await window.midas.tabs.configureGraph('tab_001', { panels: null });
Use brush to set the direction of brush selection. Brush selection is the operation of drawing a rectangle in the Select mode of the graph toolbar to select points. With direction 'xy' the selection is a two-dimensional rectangle, with 'x' it is a range along the X axis, and with 'y' it is a range along the Y axis. The default is 'xy'. Pass brush: null to return to the default.
await window.midas.tabs.configureGraph('tab_001', { brush: { direction: 'x' } });
Omitting facets preserves the current setting.
Facet wrap properties:
| Property | Type | Description |
|---|---|---|
type | 'wrap' | Facet wrap mode |
variable | string | Column name or ID to facet by (required) |
ncol | number | Number of columns, an integer of 1 or more (auto-calculated if omitted) |
nrow | number | Number of rows, an integer of 1 or more (auto-calculated if omitted) |
complete | boolean | Fill all combinations to show empty panels |
scales | 'fixed' | 'free_x' | 'free_y' | 'free' | Axis scale sharing across panels (default: 'fixed') |
Facet grid properties:
| Property | Type | Description |
|---|---|---|
type | 'grid' | Facet grid mode |
rows | string | Column name or ID for row faceting |
cols | string | Column name or ID for column faceting |
complete | boolean | Fill all combinations to show empty panels |
scales | 'fixed' | 'free_x' | 'free_y' | 'free' | Axis scale sharing across panels (default: 'fixed') |
At least one of rows or cols is required for facet grid.
For the simple graph types — histogram, scatter, timeseries, bar, pairplot, and datetime_histogram — set the fields specific to that type via graphType. layers, globalAes, scales, coordinates, and facets apply only to custom.
await window.midas.tabs.configureGraph('tab_001', {
graphType: 'histogram',
datasetId: 'ds_001',
column: 'sepal_length',
bins: 20
});
Column fields such as column, xColumn, and categoryColumn accept either a column name or a column ID. The type-specific fields are:
| Graph type | Fields |
|---|---|
histogram | column, bins, relativeFrequency, showDensity, orientation, groupByColumn, groupMode, facetNcol, showAnnotations |
scatter | xColumn, yColumn, colorColumn, sizeColumn, referenceLines, xScaleType, yScaleType, density options (displayMode, densityVisualization, densityBandwidth, contourLevels, densityColorScale) |
timeseries | xColumn, yColumns, rangeColumns, rangeOpacity |
bar | categoryColumn, valueColumns, aggregations, orientation, showValues, stackMode, sortOrder, topN |
pairplot | columns |
datetime_histogram | column, interval, showTrend |
The bins field of histogram is an integer between 1 and 100. A value outside that range or a non-integer value is stored rounded into the range and reported in warnings. A value that is not a finite number is not stored, and is likewise reported in warnings.
For bar, valueColumns accepts aggregation target columns plus '$count' for a row count, and aggregations sets the aggregation method per column (sum, average, median, min, or max). Row counts are represented by the '$count' value column, not by an aggregation value: specifying count in aggregations returns an INVALID_INPUT error.
The boxplot and heatmap graph types cannot be configured; passing either returns an INVALID_GRAPH_TYPE error. For a box plot, use graphType: "custom" with a layer of geom: { type: "boxplot" } and stats: [{ type: "boxplot" }].
Every configurable graph type accepts lineageTargetDatasetId (ID or name). When datasetId is a derived dataset (for example a GROUP BY aggregate, or a filter or reshape of the original rows), set this to an ancestor so that clicking an element selects the underlying rows and double-clicking opens their contributing rows in a Contributing rows tab. The value must be a contributing-rows ancestor of datasetId; check the candidates with getGraphBuilder's availableLineageTargets. Pass an empty string or null to reset to the graph's own dataset.
Even a valid ancestor can be untraceable at runtime if a hop between datasetId and the target uses a window function or a set operation. The call still succeeds; warnings names the untraceable hop and the reason.
models
models.list()
List the models saved in the project, including ones waiting to be re-estimated.
const result = await window.midas.models.list();
// result.data: [{ id, type, name, datasetId, family, estimationState, estimationError }, ...]
type is one of 'glm', 'glmm', 'random_forest', 'arima', 'linear_regression', or 'anova'.
family is present for GLM, GLMM, and linear regression models. The family is part of the model specification rather than a result of estimation, so the same value is present whether estimationState is 'estimated' or 'pending'.
estimationState tells you whether the model holds estimates. A model that holds them is 'estimated'; a model whose fitting dataset changed, whose estimates were discarded, and which is waiting to be re-estimated is 'pending'.
Methods that read estimates do not accept a 'pending' model. Passing the ID of a 'pending' model to models.describe(), models.predict(), models.saveAsDataset(), or reports.addModelSummary() returns an INVALID_INPUT error.
estimationError carries the reason a re-estimation failed. It is present only on a model that a failed re-estimation left without estimates, so a 'pending' model without estimationError is still being re-estimated.
models.run(config)
Run a model. Set config.type to 'glm', 'glmm', 'random_forest', 'arima', 'linear_regression', 'anova', 'pca', 'kaplan_meier', or 'cox_regression'. datasetId accepts a dataset ID or name (case-insensitive) and must refer to a primary or derived dataset (specifying an ephemeral dataset by ID returns an INVALID_INPUT error). Columns can be specified by name (case-insensitive). The result is returned directly without opening a tab. To persist the model for later use with models.list() and models.describe(), call models.save() with the returned runId. Unsaved run results are kept in memory, up to 20. Beyond that limit, the oldest unsaved results are discarded first, and a discarded runId can no longer be passed to models.save(). All unsaved results are also lost on page reload.
GLM, GLMM, linear regression, and Random Forest accept only the columns that the selectors on the corresponding tab can pick. The predictors (xColumns), the response (yColumn), and the GLM offsetColumn, successesColumn, and trialsColumn must be numeric columns: int64, float64, or boolean columns whose measurement scale is interval or ratio. The Random Forest response is the exception: classification accepts a column of any type, and only regression requires a numeric column. The GLMM groupColumn must be a nominal or ordinal column. A column also cannot take two roles in one model: the response cannot be a predictor, the GLM offsetColumn cannot be the response or a predictor, and successesColumn and trialsColumn must be two different columns that are not predictors. Passing any other column, or the same predictor twice, returns an INVALID_INPUT error, and the error message names the column and the reason. A categorical predictor is used by creating dummy variables with datasets.dummyCode() and passing the generated columns. When a rejected predictor can be dummy-coded, the error's suggestion says so. When a Random Forest regression rejects the response, the suggestion points to taskType: 'classification' instead.
GLM (type: 'glm'):
const result = await window.midas.models.run({
type: 'glm',
datasetId: 'ds_001',
yColumn: 'sepal_length',
xColumns: ['sepal_width', 'petal_length'],
family: 'gaussian'
});
// result.data:
// {
// type: 'glm',
// runId: '...',
// family: 'gaussian',
// link: 'identity',
// coefficients: [
// { variable: '(Intercept)', estimate: 2.25, se: 1.02, ciLower: 0.23, ciUpper: 4.27, expEstimate: null, expCiLower: null, expCiUpper: null },
// ...
// ],
// inference: { distribution: 't', df: 147 },
// fit: { deviance: 42.3, nullDeviance: 234.7, aic: 183.94, bic: 193.47, iterations: 5, converged: true },
// inSampleAccuracy: { kind: 'continuous', n: 150, r2: 0.82, rmse: 0.53, mae: 0.42 },
// diagnosticSummary: { nObservations: 150, nIncomplete: 0, degreesOfFreedom: 147, dispersionParameter: 0.29 },
// warnings: []
// }
inSampleAccuracy holds prediction accuracy metrics computed on the fitting data itself, using the same calculation as the In-Sample Prediction Accuracy Metrics shown on the GLM tab right after Run. kind follows the family: gaussian returns 'continuous' (r2, rmse, mae), binomial returns 'binary' (brier, auc, with n being the total number of trials), and gamma, poisson, and negative-binomial return 'deviance' (rmse, mae, meanDeviance). Values that cannot be computed are null, and the whole field is null when no valid rows remain. The in-sample ROC curve data is not returned because there is no dataset accessible through the API.
Fields in coefficients:
| Field | Description |
|---|---|
estimate | Coefficient estimate on the link scale |
se | Standard error of the estimate |
ciLower / ciUpper | Lower and upper bounds of the Wald confidence interval. The reference distribution is reported in inference; see the family-by-family table below |
expEstimate / expCiLower / expCiUpper | The exp() transformation of the estimate and confidence interval. See below for interpretation and the links for which these are null |
diagnosticSummary.dispersionParameter is the estimate of the dispersion parameter φ. For gaussian it is the deviance divided by the residual degrees of freedom n−p; for gamma it is Pearson χ² divided by n−p. For these families it is used to compute the SEs and confidence intervals. For poisson and binomial, SEs are computed with φ = 1, and this field instead contains deviance/(n−p) as an overdispersion diagnostic; it is null when the residual degrees of freedom is 0. For negative-binomial, it is 1.0 when θ is estimated automatically and Pearson χ²/(n−p) when θ is fixed.
GLMM (type: 'glmm'):
Specify the random-intercept grouping variable with groupColumn. family accepts 'gaussian', 'binomial', 'poisson', or 'gamma' (default 'gaussian'). link, includeIntercept, and confidenceLevel can also be specified with the same meaning as for GLM. maxIterations defaults to 100 and tolerance to 1e-6. These two set the iteration limit and the tolerance of the golden-section part of the search over the relative covariance parameter . The evaluations at points over the search range are fixed in number and do not count toward the iteration limit. The iterations that compute the fixed effects and random effects for each have their own limits. See GLMM for background on the model.
const result = await window.midas.models.run({
type: 'glmm',
datasetId: 'ds_001',
yColumn: 'sepal_length',
xColumns: ['sepal_width', 'petal_length'],
groupColumn: 'species',
family: 'gaussian'
});
// result.data:
// {
// type: 'glmm',
// runId: '...',
// family: 'gaussian',
// link: 'identity',
// fixedEffects: [{ variable: '(Intercept)', estimate: 2.35, se: 0.87, df: 2.31, ... }, ...],
// inference: { distribution: 't-per-coefficient', method: 'kenward-roger' },
// randomEffects: { groupColumn: 'species', variance: 0.42, residualVariance: 0.14, icc: 0.75, blup: [...] },
// fit: { logLikelihood: -72.1, aic: 154.2, bic: 169.3, iterations: 8, converged: true },
// diagnosticSummary: { nObservations: 150, nGroups: 3, nFixedEffects: 3, nIncomplete: 0, groupSizes: [...] },
// warnings: []
// }
link, includeIntercept, confidenceLevel are also available as optional parameters, with the same meaning as in GLM. maxIterations defaults to 100 and tolerance to 1e-6.
The reference distribution of GLMM fixed-effect confidence intervals depends on the family and link. For gaussian + identity (LMM), the standard errors are corrected by the Kenward-Roger method, inference is { distribution: 't-per-coefficient', method: 'kenward-roger' }, and each coefficient's df carries the degrees of freedom of its t-distribution. A coefficient whose df is null has no computable degrees of freedom and its confidence interval is also null. When the correction cannot be computed numerically, warnings reports it and the result falls back to the unadjusted standard errors with { distribution: 'normal', df: null }. All other combinations use Wald approximations based on the standard normal distribution, with inference set to { distribution: 'normal', df: null }. When the number of groups is small, intervals based on the standard normal approximation can fall below the nominal coverage. See GLMM Fundamentals: Fixed Effect Inference for details.
When the estimation iterations cannot continue and the estimate cannot be determined, GLMM returns no result and raises a NUMERICAL_ERROR. This failure occurs, for example, when an inverse link applied to gamma cannot keep the linear predictor positive, or when the iterations that compute the fixed effects and random effects do not converge near the optimal relative covariance parameter. See GLMM tab: Convergence Issues for details.
Fields in randomEffects:
variance— variance of the random intercepts, σ²_uresidualVariance— forgaussian, the residual variance σ²_e; forgamma, the estimated dispersion parameter φ. Not returned forbinomialandpoisson, where φ is fixed at 1 by theoryicc— intraclass correlation coefficient σ²_u / (σ²_u + σ²_e). σ²_e is the REML-estimated residual variance forgaussian+identity, π²/3 forbinomial+logit, and 1 forbinomial+probit— the latter two are latent-scale values. For all other family-link combinations, no latent-scale residual variance is defined andiccisnullblup— predicted random intercepts per group. For LMM (gaussian+identity) these are BLUPs (Best Linear Unbiased Predictions): the group mean of the residuals from the fixed-effects prediction (y − Xβ̂), shrunk toward 0. For the other family and link combinations they are the conditional modes of the random intercepts at the estimated fixed effects and variance components. In both cases, groups with fewer observations are shrunk more toward 0.standardErrorquantifies the prediction uncertainty: for LMM it is the conditional prediction error standard deviation, and for the other combinations it is an approximation based on the Laplace approximation.rankis the rank in descending order ofestimate
For LMM (gaussian + identity), fit.logLikelihood is the REML log-likelihood, and fit.aic and fit.bic are based on it. REML-based AIC/BIC cannot be used to compare models with different fixed-effects structures. For the other family and link combinations, the log-likelihood is a Laplace approximation.
Random Forest (type: 'random_forest'):
Set taskType to 'classification' or 'regression'; any other value returns an INVALID_INPUT error. The metrics field contains fitting-set (resubstitution) evaluation metrics. For an estimate of generalization performance, use oobScore.
const result = await window.midas.models.run({
type: 'random_forest',
datasetId: 'ds_001',
yColumn: 'species',
xColumns: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width'],
taskType: 'classification',
nEstimators: 100,
randomState: 42
});
// result.data:
// {
// type: 'random_forest',
// runId: '...',
// taskType: 'classification',
// tuningParameters: { nEstimators: 100, maxDepth: null, ... },
// featureImportances: [{ feature: 'petal_length', importance: 0.45 }, ...],
// permutationImportances: [{ feature: 'petal_length', importance: 0.38 }, ...],
// metrics: { taskType: 'classification', accuracy: 0.96, precision: 0.96, recall: 0.96, f1Score: 0.96, nClasses: 3 },
// nSamples: 150,
// oobScore: 0.95,
// responseDegenerate: false,
// warnings: []
// }
For regression tasks, metrics contains { taskType: 'regression', mse, rmse, mae, r2 }. metrics values are computed on the fitting data (resubstitution) and are not available from models.describe(). For an estimate of generalization performance, use oobScore (OOB accuracy for classification, OOB R² for regression). For regression, when the response has effectively no variation, R² is undefined, so r2 and the regression oobScore are null. For both task types, oobScore is null when no samples or only one sample has out-of-bag predictions, which can occur when the number of samples is very small. For regression, oobScore is also null when the responses of the out-of-bag samples have effectively no variation. In each case, warnings states the reason. For classification, when the response has only one class, the metrics values are trivially 100%, oobScore is 100% unless it is null for one of the reasons above, and the importances are all 0; warnings reports this. responseDegenerate is true in both degenerate cases (a regression response with effectively no variation, a classification response with only one class), so a script can detect them without matching the warning text. permutationImportances is the mean decrease in OOB prediction accuracy when each predictor is shuffled. Values can be negative, which means shuffling that predictor did not reduce OOB prediction accuracy.
| Parameter | Type | Default | Description |
|---|---|---|---|
nEstimators | number | 100 | Number of trees |
maxDepth | number | null | null | Maximum tree depth (null for unlimited) |
minSamplesSplit | number | 2 | Minimum samples to split a node |
minSamplesLeaf | number | 1 | Minimum samples in a leaf node |
maxFeatures | 'sqrt' | 'log2' | null | number | 'sqrt' | Number of predictors to consider at each split |
randomState | number | 42 | Random seed |
The Random Forest run in models.run() executes on a Web Worker and does not block the main thread, as does the Random Forest prediction in models.predict(). The run cannot be cancelled once started, because the Random Forest config has no signal parameter.
inference identifies the reference distribution used for ciLower and ciUpper. distribution: 't' indicates a t distribution with df degrees of freedom; distribution: 'normal' indicates a standard normal distribution, in which case df is null. Only the GLMM LMM case (gaussian + identity) uses distribution: 't-per-coefficient', because the degrees of freedom differ per coefficient; they are reported in each coefficient entry's df rather than at the model level.
The family-by-family mapping is shown below. The critical value follows t(n−p) exactly only for Gaussian with the identity link. Other dispersion-estimating families apply a t distribution as a small-sample convention, while families marked asymptotic use the standard normal approximation.
| family | link | Reference distribution | Nature |
|---|---|---|---|
gaussian | identity | t(n−p) | Exact |
gaussian | non-identity | t(n−p) | Small-sample convention |
gamma | any | t(n−p) | Small-sample convention |
negative-binomial | any (fixed θ) | t(n−p) | Small-sample convention |
poisson | any | Standard normal | Asymptotic |
binomial | any | Standard normal | Asymptotic |
negative-binomial | any (estimated θ) | Standard normal | Asymptotic |
ciLower and ciUpper are the Wald confidence interval estimate ± criticalValue × se for each coefficient. The confidence level follows the confidenceLevel parameter (default 95). The critical value is the (1 + confidenceLevel/100) / 2 quantile of the reference distribution reported in inference.
expEstimate, expCiLower, and expCiUpper are the exp() transformation of the link-scale estimate and confidence interval. For logit link, these correspond to odds ratios (OR); for Poisson and Negative Binomial with log link, incidence rate ratios (IRR); for Gamma and Gaussian with log link, multiplicative effects. For identity, inverse, and probit links, these fields are null.
fit.aic and fit.bic are number | null. They are null when the log-likelihood constant is undefined (e.g., non-integer weights in Binomial, or a saturated model).
The response may include two kinds of warnings. Top-level result.warnings contains data preparation warnings such as exclusion of rows with missing values. result.data.warnings contains model execution warnings such as convergence issues. Rows with missing values in any response or explanatory variable are excluded from analysis. The number of excluded rows is available in diagnosticSummary.nIncomplete.
When the model does not converge within the maximum number of iterations, no error is raised; the result is returned with fit.converged: false, and message also reads "did not converge". When complete or quasi-complete separation is suspected, a warning is included in data.warnings. When a result is returned, coefficient se values are never null. When SEs cannot be computed — for example, a non-positive-definite covariance matrix, a rank-deficient design matrix, or a variance that overflows or becomes undefined because a predictor or the response spans an extreme range — no result is returned and a NUMERICAL_ERROR error is raised instead. For the extreme-range case, rescale or standardize the predictors and response, then refit. A NUMERICAL_ERROR is also raised when the fitted mean falls outside the valid range for the family (for example, an identity link applied to Poisson or Gamma); choose a link that keeps the fitted mean in range, such as log. A NUMERICAL_ERROR is also raised when the iteration diverges or when the linear predictor leaves the domain of the link function. The iteration is judged to diverge when a coefficient exceeds 1e10 in absolute value, or when a Negative Binomial iteration produces a fitted mean outside the valid range. The linear predictor leaves the link's domain, for example, when an inverse link applied to Gamma makes the linear predictor zero or negative.
Specify family as 'gaussian' (default), 'binomial', 'poisson', 'gamma', or 'negative-binomial'. See GLM for guidance on choosing a family. Use link to set the link function. When omitted, the default link for the family is used. Only the links listed under "Available links" below are accepted; any other combination returns an INVALID_INPUT error.
| family | Default link | Available links |
|---|---|---|
gaussian | identity | identity, log |
binomial | logit | logit, probit |
poisson | log | log, identity |
gamma | inverse | inverse, log, identity |
negative-binomial | log | log |
Optional parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
includeIntercept | boolean | true | Include intercept term |
maxIterations | number | 100 | Maximum number of iterations |
tolerance | number | 1e-6 | Convergence tolerance |
binomialResponse | object | - | Binomial response format. See below |
theta | number | - | Shape parameter θ of the negative binomial. Must be a positive finite number; any other value returns an INVALID_INPUT error. When omitted, estimated via profile likelihood |
offsetColumn | string | - | Offset column (e.g., exposure for Poisson regression) |
confidenceLevel | number | 95 | Confidence level for confidence intervals (50–99.99) |
When theta is omitted and estimated automatically, coefficient SEs and confidence intervals are computed treating the estimated θ as fixed. The estimation uncertainty of θ itself is not propagated into the SEs.
binomialResponse specification:
Specifies the response variable format when family: 'binomial'. When binomialResponse is omitted, binary format is assumed.
{ format: 'binary' }-- Binary 0/1 data. Specify the response variable withyColumn{ format: 'grouped', successesColumn: '...', trialsColumn: '...' }-- Successes/trials pair.yColumncan be omitted in this case
// Grouped Binomial example
const result = await window.midas.models.run({
type: 'glm',
datasetId: 'ds_001',
binomialResponse: { format: 'grouped', successesColumn: 'defects', trialsColumn: 'inspected' },
xColumns: ['temperature', 'pressure'],
family: 'binomial',
link: 'logit'
});
ARIMA (type: 'arima'):
Fit an ARIMA(p,d,q) model to a single time-series column. Specify order as [p, d, q] for manual order selection, or use autoSelect for automatic order selection. Setting seasonalPeriod to 2 or more turns the model into ARIMA(p,d,q)(P,D,Q)[s] (SARIMA) with seasonal orders. Automatic selection sets the differencing orders by tests first and chooses the remaining orders by AIC or BIC under that differencing. The differencing order d is set with the KPSS test. In seasonal models, the seasonal differencing order D is set first with the OCSB test, and KPSS is applied to the seasonally differenced series. For why differencing orders are not compared across by AIC/BIC, see Automatic ARIMA Order Selection; for the tab workflow, see The ARIMA Tab.
| Parameter | Type | Default | Description |
|---|---|---|---|
seriesColumn | string | (required) | Column name for the time series. A numeric column: int64, float64, or boolean with an interval or ratio scale |
order | [number, number, number] | — | [p, d, q] — AR order, differencing order, MA order. Omit to use autoSelect |
seasonalPeriod | number | 1 | Seasonal period s: the number of observations per cycle, e.g. 12 for the yearly cycle of monthly data. Integer from 1 to 366. 1 means non-seasonal |
seasonalOrder | [number, number, number] | [0, 0, 0] | [P, D, Q] — seasonal AR order, seasonal differencing order, seasonal MA order. P and Q range 0–2, D ranges 0–1. Used when seasonalPeriod is 2 or more and order is specified |
autoSelect | object | { maxP: 3, maxD: 1, maxQ: 3, maxSeasonalP: 1, maxSeasonalQ: 1, criterion: 'aic' } | Auto order selection. Used when order is omitted |
autoSelect.maxP | number | 3 | Maximum AR order to search, 0–5 |
autoSelect.maxD | number | 1 | Maximum differencing order, 0–2; the KPSS-based choice of d never exceeds it |
autoSelect.maxQ | number | 3 | Maximum MA order to search, 0–5 |
autoSelect.maxSeasonalP | number | 1 | Maximum seasonal AR order to search, 0–2. Effective when seasonalPeriod is 2 or more |
autoSelect.maxSeasonalQ | number | 1 | Maximum seasonal MA order to search, 0–2. Effective when seasonalPeriod is 2 or more |
autoSelect.criterion | 'aic' | 'bic' | 'aic' | Information criterion for choosing the remaining orders under fixed differencing |
includeIntercept | boolean | true | Include a constant term: the series mean when d + D = 0, a drift when d + D = 1. Omitted when d + D is 2 or more, where it would correspond to a polynomial trend |
includeTrend | boolean | false | Include a deterministic linear trend term. The time regressor is generated internally from the position of each valid observation (t = 1, 2, ...), and the slope is estimated only when d + D = 0 (the intercept is then the level at t = 0). When d + D = 1 the slope is represented by the drift term if a constant is included, and when d + D is 2 or more differencing removes the trend; in both cases it is not estimated separately and a warning is reported. In automatic selection, when no seasonal difference is taken (D = 0), the KPSS test on the undifferenced series switches to the variant whose null hypothesis is trend stationarity (decision threshold 0.146) |
confidenceLevel | number | 95 | Confidence level for coefficient CIs |
signal | AbortSignal | — | Used to cancel the run in progress |
The ARIMA run in models.run() executes on a Web Worker and does not block the main thread. Passing an AbortSignal as signal lets the caller request cancellation while the run is in progress; a cancelled run returns a USER_CANCELLED error.
The response includes coefficients (AR/MA/SAR/SMA/Intercept/Trend with CIs), seasonalOrder, fit (AIC, BIC, log-likelihood, σ², convergence), residualDiagnostics (ACF and PACF of residuals), and nObservations. When the fit did not converge, fit.aic and fit.bic are null, because the likelihood is not maximized. A coefficient whose standard error cannot be computed has a null se and null CI bounds; this happens, for example, when coefficients are pinned at the stationarity or invertibility boundary and the variance-covariance matrix is unavailable. When autoSelect is used, differencingSelection records the KPSS-based choice of d (the selected d, the KPSS statistic at each order, and the variant and decision threshold used at each order), and orderSearch contains the AIC/BIC for all candidate (p, q, P, Q) orders searched under that differencing. Every entry in orderSearch shares the same (d, D). Each entry carries a status of converged, not-converged, or failed. A not-converged candidate is one whose iteration stopped before converging, and a different order or differencing order may converge. A failed candidate is one that could not be fitted at all, for example because too few observations remain after differencing or the differenced series has zero variance, and error holds the reason. A candidate whose status is not converged has null aic and bic, because its likelihood is not maximized. The number of non-converged candidates is reported in orderSearchNonConvergedCount and the number of failed candidates in orderSearchFailedCount. When seasonalPeriod is 2 or more, seasonalDifferencingSelection also records the OCSB-based choice of D (the selected D, the OCSB statistic, and the decision threshold).
The residualDiagnostics.acf and .pacf arrays start at lag 0, and a lag at which the sample autocorrelation cannot be computed holds null. When the residuals have zero variance, every element including lag 0 is null, because the sample autocorrelation is a ratio whose numerator and denominator are both zero. The partial autocorrelation is also null at every lag beyond one at which the innovation variance of the Durbin-Levinson recursion reaches zero.
When the fit degenerates (too few observations, or zero variance after differencing), fit.error holds a message. In that case the coefficients, σ², and similar fields are meaningless values (zero), fit.aic and fit.bic are null, and residualDiagnostics.acf/.pacf are empty arrays. This is distinct from non-convergence (converged: false with no fit.error); failing to converge alone does not set fit.error.
Non-finite values (NaN, Infinity, null) in the series are dropped. When observations are dropped, a warning is included in the response. When fewer than 10 valid observations remain, the call returns an INSUFFICIENT_DATA error, the same minimum the ARIMA tab requires. A warning is also included when the model's total differencing d + D is 2 or more, where the constant term is omitted; when includeTrend is requested but d + D is 1 or more, where the trend slope is not estimated separately; when a coefficient's standard error could not be computed; when an autoregressive or moving-average root is close to the unit circle, where estimates and their normal-approximation confidence intervals may be unreliable (a near-noninvertible moving-average root may indicate overdifferencing); and when a distinctly different set of coefficients fits the data almost as well as the reported one, where the coefficients are weakly identified. A fit that did not converge is also reported in warnings: the message from fit.error when set, or a generic non-convergence warning otherwise. models.describe() returns the same warnings for a saved model.
// Manual order
const result = await window.midas.models.run({
type: 'arima',
datasetId: 'ds_001',
seriesColumn: 'temperature',
order: [1, 1, 1]
});
// Auto order selection
const result2 = await window.midas.models.run({
type: 'arima',
datasetId: 'ds_001',
seriesColumn: 'temperature',
autoSelect: { maxP: 5, maxD: 2, maxQ: 5, criterion: 'bic' }
});
Linear Regression (type: 'linear_regression'):
Fit a linear regression by ordinary least squares.
| Parameter | Type | Default | Description |
|---|---|---|---|
yColumn | string | (required) | Response variable column name |
xColumns | string[] | (required) | Explanatory variable column names |
includeIntercept | boolean | true | Include intercept term |
confidenceLevel | number | 95 | Confidence level for confidence intervals |
In the response, coefficients has the same shape as for GLM, and inference is always a t distribution. fit contains the same fields as for GLM plus rSquared, adjustedRSquared, residualStdError, and mae. residualStdError is the square root of the residual sum of squares divided by the residual degrees of freedom n−p, and mae is the mean of the absolute residuals (divided by n). residualStdError is a different quantity from the rmse returned among the GLM prediction accuracy metrics, which divides the residual sum of squares by the number of observations n. rSquared, adjustedRSquared, residualStdError, and mae are null when the response has effectively no variation (responseDegenerate: true), in which case the coefficient se values and confidence intervals are also null, and warnings explains why. warnings also reports a Type I sum of squares that came out negative from numerical error beyond the range of rounding error and was replaced with 0 (the same condition described for the ANOVA Table on the Linear Regression page); the affected predictor is named in the message. A saturated model (as many predictors as observations) cannot estimate the dispersion parameter and returns an error instead of a result. For a model without an intercept, rSquared is an uncentered R² and is not comparable to the centered R² of an intercept model. mae is only available on the run result; it is not included in models.describe() for saved models.
const result = await window.midas.models.run({
type: 'linear_regression',
datasetId: 'ds_001',
yColumn: 'sepal_length',
xColumns: ['sepal_width', 'petal_length']
});
// result.data: { type: 'linear_regression', runId, coefficients, inference,
// fit: { ..., rSquared: 0.84, adjustedRSquared: 0.84, residualStdError: 0.33, mae: 0.26 }, diagnosticSummary, warnings }
ANOVA (type: 'anova'):
Run a one-way or two-way analysis of variance. The number of elements in factorColumns determines which.
| Parameter | Type | Default | Description |
|---|---|---|---|
responseColumn | string | (required) | Response variable column name |
factorColumns | string[] | (required) | Factor columns. One element for one-way, two for two-way |
includeInteraction | boolean | true | Include the interaction term in two-way ANOVA |
ssType | 'I' | 'III' | 'III' | Sum-of-squares decomposition type for two-way ANOVA |
confidenceLevel | number | 95 | Confidence level for Tukey HSD (50–99.99) |
postHoc | boolean | true | Compute Tukey HSD (one-way only; specifying true for two-way ANOVA returns INVALID_INPUT) |
Column eligibility follows the measurement scale, the same rule the ANOVA tab selectors use. responseColumn accepts columns with an interval or ratio scale, and factorColumns accepts columns with a nominal or ordinal scale. A column that does not meet the requirement returns INVALID_INPUT, and the error message names the column's current scale. Date and datetime columns cannot be used as responseColumn even though their scale is interval. String and enum columns cannot be used as responseColumn because their values are stored as text, and their scale cannot be changed to interval or ratio. In these cases the error message names the data type instead of the scale.
The response includes mode ('one-way' or 'two-way'), anovaTable (sum of squares, degrees of freedom, mean square, and the effect sizes η² and ω² per effect (partial η² and partial ω² for two-way), plus residuals and total), groupStatistics (n, mean, standard deviation, minimum, and maximum per group), nObservations, nExcluded, and exclusions. For one-way ANOVA with postHoc enabled, tukeyHSD (pairwise mean differences with SEs and confidence intervals) is also included. The postHoc setting is stored with models saved via models.save and is preserved when the model is re-estimated after a dataset reload. Because two-way ANOVA never computes Tukey HSD, the stored value is always false for two-way models.
When the model reproduces every response value exactly, the error variance cannot be estimated, and the quantities that depend on it are null. A residual sum of squares below floating-point precision relative to the response variation counts as an exact reproduction, so a response whose values are identical within each group is covered. In that case anovaTable.residuals.ms and the effect sizes are null, tukeyHSD is omitted even with postHoc enabled, the sums of squares and group means are still returned, and warnings explains why.
exclusions breaks down the rows excluded for missing or invalid values. byGroup counts excluded rows per group (factor level for one-way, level combination for two-way), and factorMissing counts excluded rows whose factor value is missing and which therefore belong to no group. When exclusion removes every row of a level, droppedLevelsA and droppedLevelsB list the label and row count; when both factor levels survive but every row of a cell is excluded, droppedCells lists it. Each of these removals also appears as a message in warnings. In two-way ANOVA with the interaction term, however, a cell emptied by exclusion makes the run itself fail with an empty-cell error, so droppedCells and its warnings are returned only without the interaction term.
const result = await window.midas.models.run({
type: 'anova',
datasetId: 'ds_001',
responseColumn: 'sepal_length',
factorColumns: ['species']
});
// result.data: { type: 'anova', runId, mode: 'one-way',
// anovaTable: { ssType, rows: [{ source, ss, df, ms, etaSquared, omegaSquared }, ...], residuals, total },
// groupStatistics: [{ label, n, mean, std, min, max }, ...],
// tukeyHSD: { comparisons: [{ group1, group2, meanDiff, se, ciLower, ciUpper }, ...], confidenceLevel },
// nObservations: 150, nExcluded: 0,
// exclusions: { byGroup: [], factorMissing: 0, droppedLevelsA: [], droppedLevelsB: [], droppedCells: [] },
// warnings: [] }
PCA (type: 'pca'):
Runs a principal component analysis. Rows where any of the selected columns is missing are excluded, and their count is returned as nExcluded.
| Parameter | Type | Default | Description |
|---|---|---|---|
columns | string[] | (required) | Numeric columns to analyze. At least 2 |
preprocessing | 'standardize' | 'center' | 'none' | 'standardize' | Preprocessing |
nComponents | number | all components | Maximum number of components to return |
The response contains components (eigenvalue, explained variance ratio, and cumulative variance ratio per component) and loadings (the coefficient of each variable on each component). Component scores are not returned because they are as long as the data. Use Save as Dataset on the PCA tab when you need them.
const result = await window.midas.models.run({
type: 'pca',
datasetId: 'ds_001',
columns: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
});
// result.data: { type: 'pca', runId, nObservations: 150, nVariables: 4, nComponents: 4,
// nExcluded: 0, preprocessing: 'standardize',
// components: [{ component: 1, eigenvalue, explainedVarianceRatio, cumulativeVarianceRatio }, ...],
// loadings: [{ variable: 'sepal_length', values: [...] }, ...], warnings: [] }
Kaplan-Meier (type: 'kaplan_meier'):
Estimates the survival function with the Kaplan-Meier method. eventColumn must be an int64 or boolean column, and 1 or true counts as an observed event. If eventColumn contains values other than 0/1 (false/true for boolean columns), or timeColumn contains zero or negative values, the call returns an INVALID_INPUT error listing the offending values and their row counts. With groupColumn the estimation is done per group; without it the result has a single group named 'All'. groupColumn accepts columns whose measurement scale is nominal or ordinal. Rows whose group value is missing are collected into a group named 'Unknown'.
| Parameter | Type | Default | Description |
|---|---|---|---|
timeColumn | string | (required) | Observation time column |
eventColumn | string | (required) | Event indicator column |
groupColumn | string | none | Column that defines the groups |
confidenceLevel | number | 95 | Confidence level (50-99.99) |
tau | number | smallest of the maximum observed times across groups | RMST restriction time (a positive number) |
Each entry of groups carries the number of observations and events, the median survival time with its confidence interval, and the event times (times) with the matching survival probabilities, confidence bands, risk set sizes, event counts, and censoring counts.
rmst carries the restricted mean survival time (RMST), the area under the survival curve from 0 to tau. tau is the restriction time actually used and defaultTau is its default. groups lists the RMST estimate, standard error, and confidence interval per group; in a group with no event time at or before tau, the estimate, standard error, and interval are null. differences lists, for every pair of groups, the difference group1 minus group2 with its standard error and confidence interval, and is empty with a single group. These differences are not adjusted for multiple comparisons. The standard errors are based on the Greenwood variance, and both intervals are normal-approximation Wald intervals. When tau exceeds the default, the survival curve is extrapolated as constant beyond the last observed time, which can overstate the RMST and understate its uncertainty; warnings reports this. A tau that is not a positive number returns an INVALID_INPUT error.
const result = await window.midas.models.run({
type: 'kaplan_meier',
datasetId: 'ds_001',
timeColumn: 'time',
eventColumn: 'DEATH_EVENT',
groupColumn: 'sex'
});
// result.data: { type: 'kaplan_meier', runId, confidenceLevel: 95, nExcluded: 0,
// groups: [{ group: '0', nObservations, nEvents, medianSurvivalTime, medianSurvivalTimeCI,
// times: [...], survival: [...], survivalCILower: [...], survivalCIUpper: [...],
// nRisk: [...], nEvent: [...], nCensor: [...] }, ...],
// rmst: { tau, defaultTau,
// groups: [{ group: '0', rmst, se, ciLower, ciUpper }, ...],
// differences: [{ group1: '0', group2: '1', difference, se, ciLower, ciUpper }] },
// warnings: [] }
Cox regression (type: 'cox_regression'):
Fits a Cox proportional hazards model. eventColumn must be an int64 or boolean column, and covariates must be columns whose measurement scale is interval or ratio. If eventColumn contains values other than 0/1 (false/true for boolean columns), or timeColumn contains zero or negative values, the call returns an INVALID_INPUT error listing the offending values and their row counts. Rows where the time, the event, or any covariate is missing are excluded, and their count is returned as nExcluded. The exclusions field carries the breakdown: missingTimeOrEvent counts rows dropped for a missing time or event value, and missingCovariates counts rows dropped for a missing covariate. The two are counted exclusively, so they sum to nExcluded. Tied event times are handled with the Efron method, and passing tiesMethod returns an INVALID_INPUT error.
| Parameter | Type | Default | Description |
|---|---|---|---|
timeColumn | string | (required) | Observation time column |
eventColumn | string | (required) | Event indicator column |
covariates | string[] | (required) | Numeric covariate columns. At least 1 |
maxIterations | number | 100 | Maximum Newton-Raphson iterations |
tolerance | number | 1e-9 | Convergence tolerance |
confidenceLevel | number | 95 | Confidence level for hazard ratios (50-99.99) |
Each entry of coefficients carries the log hazard ratio, its standard error, and the hazard ratio with its confidence interval. The response also reports the log partial likelihood, AIC, the concordance index with its standard error, the convergence state, and the iteration count. The intervals are Wald intervals on the log hazard ratio, exponentiated. warnings reports signs that the estimates are diverging (quasi-separation).
When the iteration stops before meeting the convergence criterion, the result is returned with converged: false. This result has null for the values that assume the maximum partial likelihood estimate: each coefficient's coefficient, standardError, hazardRatio, hazardRatioCILower, and hazardRatioCIUpper, and logPartialLikelihood, aic, concordance, concordanceSE, diagnostics, and baseline. The coefficients at the point where the iteration stopped are in lastIterateCoefficient, and the partial log-likelihood there is in lastIterateLogPartialLikelihood; neither is an estimate. stopReason gives the reason the iteration stopped: 'max-iterations' (maxIterations was reached), 'step-halving-failed' (each of the 10 step lengths tried along the update direction decreased the partial log-likelihood by more than the acceptance threshold, which is 1e-10 times one plus the absolute value of the partial log-likelihood at the previous iteration), or 'direction-unsolvable' (the information matrix could not be solved, so the update direction could not be computed). When the fit converged, stopReason is null. warnings holds one warning that states the reason and names the covariates whose coefficients were extreme when the iteration stopped. When separation, a singular information matrix, or a variance that is not a finite positive value is found at the point where the iteration stopped, a NUMERICAL_ERROR error is returned instead of a result that did not converge.
diagnostics carries the proportional hazards diagnostics. proportionalHazards lists rho per covariate, the Pearson correlation between the scaled Schoenfeld residuals and the time transform g(t) = 1 − KM(t−); it is null with fewer than 2 events, or when the residuals or the time transform have zero variance. rho alone does not show how large the departure from proportional hazards is or what pattern it takes, so judge it together with schoenfeldResiduals. schoenfeldResiduals lists the raw (raw) and scaled (scaled) residuals per event, both in the order of covariates, and timeTransform lists g(t) per event. The Schoenfeld residual scatter plots in the Diagnostics section of the Cox regression tab plot time of schoenfeldResiduals on the x-axis and scaled on the y-axis. schoenfeldTrends holds, per covariate, the curve that smooths scaled against time with LOESS, and the tab draws this curve over each scatter plot. The LOESS is a local linear regression with tricube weights and a span of 0.75. Each element has the covariate name name, the times time, and the LOESS predictions value at those times. time holds the distinct event times. When there are more than 101 distinct event times, time holds no more than 200 of them: the 101 event times whose ranks in ascending order are evenly spaced, together with the event time nearest to each of 101 evenly spaced points from the first to the last event time. The log(−log S) plot in the same section is a display-only plot that groups the observations by one covariate chosen in the tab (one group per value when the covariate has five or fewer distinct values, otherwise two groups split at the median), and the data behind it are not returned.
baseline carries the baseline survival function: for each event time in times, the cumulative baseline hazard cumHazard (H₀(t)), the baseline survival survival (S₀(t) = exp(−H₀(t))), the risk set size nRisk, and the event count nEvent. cumHazardCILower/cumHazardCIUpper and survivalCILower/survivalCIUpper are pointwise confidence intervals at confidenceLevel, constructed on the log H₀ scale (the log(−log S₀) scale for S₀) with a variance that combines the baseline hazard increments and the coefficient estimates (formulation). They are individual intervals at each time point, not a simultaneous band, and both bounds are null at a time point where the interval cannot be computed. The baseline is the estimate for an observation whose covariates are all 0. For a covariate such as age whose observed range does not include 0, S₀(t) is an extrapolation, so compute S(t | X) = exp(−H₀(t)·exp(β'X)) from cumHazard at realistic covariate values X, such as the sample means, to check the survival function. Compute it from cumHazard rather than from survival, because S₀(t) rounds to exactly 1 when H₀(t) is smaller than about 1e-16, and the power S₀(t)^exp(β'X) then returns 1 for every X. When the covariate origin X = 0 is far from the data, S₀(t) is 0 or 1 at every time to 6 decimal places, and survivalDegenerate is true.
const result = await window.midas.models.run({
type: 'cox_regression',
datasetId: 'ds_001',
timeColumn: 'time',
eventColumn: 'DEATH_EVENT',
covariates: ['age', 'ejection_fraction']
});
// result.data: { type: 'cox_regression', runId, confidenceLevel: 95,
// coefficients: [{ name: 'age', coefficient, standardError, hazardRatio,
// hazardRatioCILower, hazardRatioCIUpper }, ...],
// logPartialLikelihood, aic, concordance, concordanceSE, converged, stopReason: null, iterations,
// nObservations, nEvents, nExcluded,
// exclusions: { missingTimeOrEvent, missingCovariates },
// diagnostics: { proportionalHazards: [{ name: 'age', rho }, ...],
// schoenfeldResiduals: [{ time, raw: [...], scaled: [...] }, ...], timeTransform: [...],
// schoenfeldTrends: [{ name: 'age', time: [...], value: [...] }, ...] },
// baseline: { times: [...], cumHazard: [...], cumHazardCILower: [...], cumHazardCIUpper: [...],
// survival: [...], survivalCILower: [...], survivalCIUpper: [...], nRisk: [...], nEvent: [...],
// survivalDegenerate: false },
// warnings: [] }
PCA, Kaplan-Meier, and Cox regression results are not stored as models. Passing their runId to models.save() returns INVALID_INPUT. Read the results directly from the return value of models.run().
models.save(runId, name?)
Save a model run result to the project. After saving, the model is available via models.list() and models.describe(). PCA, Kaplan-Meier, and Cox regression results are not saved as models; passing one of their runId values returns INVALID_INPUT.
const run = await window.midas.models.run({ ... });
const saved = await window.midas.models.save(run.data.runId, 'My Model');
// saved.data: { id: '...', name: 'My Model' }
A diagnostic dataset is created on demand when you open the GLM Diagnostics tab. Key columns include fitted_values, deviance_residuals, pearson_residuals, standardized_residuals, leverage, and cooks_distance. Visualize these with reports.addGraph() or Graph Builder for residual analysis and diagnostic plots.
models.saveAsDataset(id, artifact, options?)
Saves a model artifact as a derived dataset, so that later SQL, graphs, and reports can use it directly.
The artifacts are coefficients (GLM, linear regression, and GLMM fixed effects), covariance (the covariance matrix of GLM and linear regression), and blup (GLMM random effects). Asking for an artifact the model does not have returns UNSUPPORTED_MODEL_TYPE.
| Option | Type | Default | Description |
|---|---|---|---|
name | string | derived from the model name and artifact | Name of the output dataset. A name already in use gets a numeric suffix |
confidenceLevel | number | 95 | Interval level of the coefficient table (50-99.99). Unused for covariance and blup |
The dataset carries the same operation the GUI Save as Dataset writes, so it is re-derived from the model in the same way. Fitted values and predictions are not covered here; use models.predict().
const result = await window.midas.models.saveAsDataset('model_001', 'coefficients', {
confidenceLevel: 99,
});
// result.data: { id: 'derived_...', name: 'My GLM Coefficients', rowCount: 3,
// columnCount: 7, artifact: 'coefficients', modelType: 'glm' }
models.predict(id, config)
Predicts new data with a saved model and stores the result as a derived dataset. GLM, linear regression, and Random Forest are supported; other model types return UNSUPPORTED_MODEL_TYPE. datasetId accepts a dataset ID or name and must contain every predictor column the model was fitted with.
| Parameter | Type | Default | Description |
|---|---|---|---|
datasetId | string | (required) | Dataset to predict |
name | string | derived from the model name | Name of the output dataset. A name already in use gets a numeric suffix |
includeOriginalData | boolean | true | Keep the columns of the source dataset in the output |
confidenceLevels | number[] | [95] | Levels for the confidence interval of the mean (50-99.99) |
predictionLevels | number[] | [95] | Levels for the prediction interval of a single new observation (50-99.99) |
For GLM and linear regression the output adds the fitted values, the linear predictor, and the standard error, plus a pair of bound columns for each level in confidenceLevels and predictionLevels. Rows with a missing predictor or offset keep their row with null values. For Binomial GLMs, when the dataset contains the response column (and the trials column for the successes-and-trials format), the output also adds observed-outcome columns regardless of includeOriginalData: glm_observed for binary responses, or glm_observed_proportion and glm_trials for the successes-and-trials format.
When includeOriginalData is true and the dataset already has a column with the same name as one the output adds, the call returns INVALID_INPUT without predicting. This keeps the output from having two columns with the same name. The error message lists the colliding names. To predict on a prediction result again, set includeOriginalData to false or rename the colliding columns.
For Random Forest the output keeps the predictor columns the model was fitted with and adds the predicted value (and one probability column per class for classification). Columns that are not predictors, including the response, are dropped. The output has as many rows as the source; rows with a missing predictor get a null prediction and are counted in nSkipped. confidenceLevels, predictionLevels, and includeOriginalData have no meaning for Random Forest; they are ignored and reported in warnings.
const result = await window.midas.models.predict('model_001', {
datasetId: 'New Patients',
confidenceLevels: [95],
predictionLevels: [90, 95],
});
// result.data: { id: 'ds_...', name: 'GLM Predictions', rowCount: 40,
// columnCount: 12, modelType: 'glm', nSkipped: 0,
// accuracy: { kind: 'continuous', n: 40, r2: 0.78, rmse: 0.61, mae: 0.47 },
// onFittingData: false, warnings: [] }
accuracy holds the prediction accuracy metrics computed on the response column of the predicted dataset, using the same calculation as the Prediction Accuracy Metrics on the GLM Prediction tab. GLM and linear regression return it when the dataset contains the response column (and the trials column for the successes-and-trials format), computed on the rows with a valid response and prediction. The shape is the same as inSampleAccuracy of a GLM models.run(): gaussian returns 'continuous' (r2, rmse, mae), binomial returns 'binary' (brier, auc, with n being the total number of trials), and gamma, poisson, and negative-binomial return 'deviance' (rmse, mae, meanDeviance). Values that cannot be computed are null. rmse divides by the number of rows n; fit.residualStdError of a linear regression models.run() divides by the residual degrees of freedom n - p, so the two differ on the same data. accuracy is null when the dataset has no response column, for Random Forest, when no row has a valid response and prediction, and when the trials column is missing; in the last two cases warnings states the reason. A ROC curve can be drawn when kind is 'binary' and auc is not null. Its data are the glm_predicted column and the observed-outcome columns of the created dataset, so the roc stat of a graph can draw the same curve as the tab.
onFittingData is true when the predicted dataset is the one the model was fitted on. accuracy is then an in-sample measure, the case in which the GLM Prediction tab shows a note below the metrics. Metrics on data not used for fitting are typically less favorable.
models.describe(id)
Get model details. Supports GLM, GLMM, Random Forest, ARIMA, Linear Regression, and ANOVA. The response structure varies by model type (check result.data.type). Models can be created via models.run() + models.save() or through the GUI.
GLM returns coefficients, fit statistics (AIC, BIC, deviance), diagnostic summary, and metadata.
GLMM returns fixed effects (same format as GLM coefficients), random effects (group variable, variance, ICC, BLUP), fit statistics (log-likelihood, iterations, convergence), and diagnostic summary. Warnings raised during fitting are returned in warnings: a singular fit, a variance-component estimate that reached the upper bound of the search range, a search or fixed-effect optimization that did not converge, and a Kenward-Roger correction that could not be computed and fell back to the normal approximation. It is omitted for models with no warnings and for models saved before this information was persisted.
Random Forest returns task type (classification/regression), tuningParameters, the OOB score, MDI variable importances (if available), and OOB permutation importances (when computed). oobScore is OOB accuracy for classification and OOB R² for regression. It is null when the score cannot be computed, and warnings states the reason; the conditions are the same as described for Random Forest under models.run(). responseDegenerate is true when the response was degenerate at fitting time (no variation for regression, a single class for classification); models saved before this field was persisted omit it.
ARIMA returns the orders (p, d, q and the seasonal orders), coefficients (AR/MA/SAR/SMA/Intercept/Trend with CIs), fit statistics (AIC, BIC, log-likelihood, σ², convergence), and residualDiagnostics (ACF and PACF of residuals). The residual diagnostics and fit.error from models.run() are persisted, so they are available without refitting. The acf and pacf arrays are empty either for models saved before this was added or for degenerate fits (with fit.error set), and a lag at which the autocorrelation cannot be computed holds null. For how to interpret coefficients, fit statistics, and null lags, see the ARIMA notes under models.run().
Linear Regression returns coefficients (same shape as GLM; inference is always a t distribution), fit statistics (R², adjusted R², Residual Std. Error, AIC, BIC), diagnostic summary, metadata, and responseDegenerate. When responseDegenerate is true, rSquared, adjustedRSquared, residualStdError, and the coefficient se values and confidence intervals are null, as in models.run().
ANOVA returns the ANOVA table (sum of squares, degrees of freedom, mean square, and the effect sizes η² and ω², which are partial η² and partial ω² for two-way), group statistics, Tukey HSD (only when computed), and metadata. The response structure is the same as described for ANOVA under models.run(). exclusions is omitted for models saved before this information was persisted.
const result = await window.midas.models.describe('model_001');
// GLM example - result.data:
// {
// type: 'glm',
// family: 'gaussian',
// link: 'identity',
// id: 'model_001',
// name: 'My Model',
// metadata: {
// createdAt: '2025-01-15T10:30:00Z',
// fittingDatasetId: 'ds_001',
// predictors: ['sepal_width', 'petal_length'],
// response: 'sepal_length',
// sampleSize: 150
// },
// coefficients: [
// { variable: '(Intercept)', estimate: 2.25, se: 1.02, ciLower: 0.23, ciUpper: 4.27, expEstimate: null, expCiLower: null, expCiUpper: null },
// { variable: 'sepal_width', estimate: 0.60, se: 0.24, ciLower: 0.13, ciUpper: 1.07, expEstimate: null, expCiLower: null, expCiUpper: null },
// ...
// ],
// inference: { distribution: 't', df: 147 },
// fit: { deviance: 42.3, nullDeviance: 234.7, aic: 183.94, bic: 193.47, iterations: 5, converged: true },
// diagnosticSummary: { ... }
// }
For GLMM, the coefficients under fixedEffects have the same shape as GLM coefficients. inference follows the same rule as models.run(): LMMs estimated with Kenward-Roger inference report { distribution: 't-per-coefficient', method: 'kenward-roger' } with a per-coefficient df, while other families and models saved before Kenward-Roger was introduced report { distribution: 'normal', df: null }. Random Forest does not include inference, coefficients, or fit fields.
// GLMM example - result.data:
// {
// type: 'glmm',
// family: 'gaussian',
// link: 'identity',
// id: 'model_002',
// name: 'Mixed Model',
// metadata: { createdAt: '2025-01-15T10:30:00Z', fittingDatasetId: 'ds_001', predictors: ['x1'], response: 'y', sampleSize: 200 },
// fixedEffects: [
// { variable: '(Intercept)', estimate: 3.14, se: 0.85, df: 8.7, ciLower: 1.21, ciUpper: 5.07, expEstimate: null, expCiLower: null, expCiUpper: null },
// { variable: 'x1', estimate: 0.52, se: 0.18, df: 182.4, ciLower: 0.16, ciUpper: 0.88, expEstimate: null, expCiLower: null, expCiUpper: null }
// ],
// inference: { distribution: 't-per-coefficient', method: 'kenward-roger' },
// randomEffects: {
// groupColumn: 'school',
// variance: 1.23,
// residualVariance: 4.56,
// icc: 0.212,
// blup: [{ groupId: 'A', estimate: 0.45, standardError: 0.21, rank: 1 }, { groupId: 'B', estimate: -0.32, standardError: 0.19, rank: 2 }]
// },
// fit: { logLikelihood: -447.05, aic: 902.1, bic: 915.3, iterations: 12, converged: true },
// diagnosticSummary: { nObservations: 200, nGroups: 10, nFixedEffects: 2, nIncomplete: 0, groupSizes: [{ groupId: 'A', size: 20 }, { groupId: 'B', size: 15 }, { groupId: 'C', size: 25 }] }
// }
// Random Forest example - result.data:
// {
// type: 'random_forest',
// id: 'model_003',
// name: 'RF Classifier',
// metadata: { createdAt: '2025-01-15T10:30:00Z', fittingDatasetId: 'ds_001', predictors: ['x1', 'x2', 'x3'], response: 'species', sampleSize: 150 },
// taskType: 'classification',
// tuningParameters: {
// nEstimators: 100,
// maxDepth: null,
// minSamplesSplit: 2,
// minSamplesLeaf: 1,
// maxFeatures: 'sqrt',
// randomState: 42
// },
// featureImportances: [
// { feature: 'x1', importance: 0.45 },
// { feature: 'x2', importance: 0.35 },
// { feature: 'x3', importance: 0.20 }
// ],
// permutationImportances: [
// { feature: 'x1', importance: 0.38 },
// { feature: 'x2', importance: 0.42 },
// { feature: 'x3', importance: 0.12 }
// ]
// }
featureImportances is MDI (Mean Decrease in Impurity). permutationImportances is OOB permutation importance — the mean decrease in OOB prediction accuracy when each predictor is shuffled; it is undefined when not computed. Values can be negative, which means shuffling that predictor did not reduce OOB prediction accuracy. Both arrays follow the order of metadata.predictors.
models.remove(id)
Remove a model from the project. Closes any open tabs that reference the model or the resources deleted with it, then cascade-deletes associated derived datasets (diagnostic, ANOVA, coefficient tables, etc.), any datasets derived from them, and any models fitted on those derived datasets. Report elements referencing the deleted datasets or models are also removed.
await window.midas.models.remove('model_001');
models.configure(tabId, config)
Configures a model tab. Set config.type to the model type, which must match the type of the tab. The available types are glm, glmm, random_forest, arima, linear_regression, anova, pca, kaplan_meier, and cox_regression. Every field other than type is optional, and only the fields you pass are changed. The dataset name passed as datasetId and column names are resolved case-insensitively.
The available fields are those of models.run() for that model type that the tab holds as settings. You can set the dataset, the variable selection, the model settings, the convergence controls, and the confidence level. Fields the tab has no setting for are given only when running with models.run(); passing one here returns INVALID_INPUT rather than being ignored. ANOVA postHoc is one such field, so whether Tukey HSD is computed is decided by models.run(). Cox regression maxIterations and tolerance are the others: the Cox regression tab has no inputs for the convergence controls. On a negative binomial GLM tab, passing theta switches the tab to a manual theta (useManualTheta: true), and passing useManualTheta: false returns it to automatic estimation of theta. On GLM and GLMM tabs, changing family without passing link resets the link to the family's default, as choosing a family again in the UI does. When the resulting family and link are not a combination listed in the models.run() table, the call returns an INVALID_INPUT error and leaves the tab settings unchanged.
The columns you pass must be of a kind the GUI selector can pick. PCA columns, Cox regression covariates, and the survival timeColumn must be numeric columns, eventColumn must be an int64 or boolean column, and the Kaplan-Meier groupColumn must be a categorical column. The GLM, GLMM, linear regression, Random Forest, ARIMA, and ANOVA columns follow the same rules as in models.run(). Passing a column of another kind, or the same column twice, returns an INVALID_INPUT error. When the call passes a column field, the rule that a column takes only one role is checked against the tab settings after the update, so passing only yColumn with a column that the tab already has among its predictors also returns INVALID_INPUT. A call that passes no column field, such as one that changes only confidenceLevel, is not checked against this rule. On a Random Forest tab, the response is checked against the taskType after the update, so changing only taskType to 'regression' while the tab's response column is not numeric also returns INVALID_INPUT.
PCA nComponents is the maximum number of components to keep, and passing null removes the limit so that all components are kept. Kaplan-Meier tau is the RMST restriction time (a positive number), and passing null returns it to the default (the smallest of the maximum observed times across groups). Changing timeColumn also returns tau to the default. Passing null as groupColumn stops grouping and estimates a single curve.
When the tab already shows results and you change a setting that affects them, those results no longer belong to the current settings, so they are removed from the view and replaced with a message prompting another run. Restoring the previous settings brings the results back. Use models.run() to get results, or run the analysis again in the GUI. Which settings affect the results is the same as on each tab's page; confidenceLevel on GLM, GLMM, linear regression, Cox regression, and ARIMA tabs only changes how the intervals are displayed, so the results are kept.
await window.midas.models.configure('glm_001', {
type: 'glm',
family: 'binomial',
link: 'logit',
yColumn: 'outcome',
xColumns: ['age', 'treatment'],
});
await window.midas.models.configure('arima_001', {
type: 'arima',
seriesColumn: 'sales',
order: [1, 1, 1],
seasonalPeriod: 12,
});
reports
Report text content can be modified with two methods: addContent() appends Markdown to the end of the existing content; setContent() replaces the entire content. Both methods preserve the report's elements — only the text is affected.
reports.create(name, description?)
Create a new report.
const result = await window.midas.reports.create('Analysis Report');
// result.data: { id: 'report_...', name: 'Analysis Report' }
reports.list()
List reports.
const result = await window.midas.reports.list();
// result.data: [{ id, name, elementCount }, ...]
reports.getContent(reportId)
Get report content.
const result = await window.midas.reports.getContent('report_001');
// result.data: { content: '## Analysis Results\n...', elements: [{ id, type, title, renderStatus, renderStatusMessage?, renderWarnings? }, ...] }
renderStatus is included for every element. 'ok' means no issue preventing rendering was detected, 'empty' means data rows exist but no renderable points remain, and 'error' means the element cannot render due to a missing dataset, unloaded data, a validation error in the configuration (a column type the axis does not accept, a palette incompatible with the scale type, etc.), or a facet panel count above the limit configured in Settings (Max Facet Panels). The point-level pre-render check applies only to Custom Graph elements (graphConfig.type === 'custom'). Non-custom graph elements are only checked for dataset existence and load state; their 'ok' does not mean the rendered output was verified.
Custom Graph elements also carry renderWarnings when the graph has diagnostics. The kinds of diagnostic are the same as in tabs.getGraphBuilder(). The same diagnostics also appear as a warning above the graph in the report view.
reports.setContent(reportId, content)
Replace all text content of the report. Text added by addContent() or addModelSummary() is also replaced. If the content contains {{type:id}} element references whose IDs are not registered in the report's elements, those are reported via result.warnings.
Elements whose {{type:id}} references are removed from the content are not automatically deleted. They remain in the report's elements and can be re-referenced by inserting {{type:id}} back into the content. To permanently remove an element, use reports.removeElement().
const result = await window.midas.reports.setContent('report_001', '## Updated Results\n...');
// result.data: { contentLength: 42 }
// result.warnings: ['Element reference {{data_table:xxx}} not found in report elements'] // when unregistered references exist
reports.addContent(reportId, markdown)
Append Markdown text to the end of a report.
await window.midas.reports.addContent('report_001', '## Analysis Results\n\nThe model shows...');
reports.addDataTable(reportId, datasetId, options?)
Add a dataset to a report as a data table element. datasetId accepts a dataset ID or name (case-insensitive). The element is registered as type: 'data_table' under report.elements, and a {{data_table:elementId}} reference is appended to the content.
const result = await window.midas.reports.addDataTable('report_001', 'Species Averages', {
columns: ['species', 'avg_sl'],
maxRows: 10
});
// result.data: { elementId, reportId, renderStatus, renderStatusMessage? }
Options:
columns(string[] | 'all') — Columns to display, as an array of column names or column IDs. With'all', the element shows every column of the dataset at render time and automatically follows later column additions, renames, and removals. When omitted, the element stores an explicit list of all columns at creation time and does not follow later schema changes. A column given more than once, for example by name and by ID, is shown once, in the order of first appearancemaxRows(number) — Maximum number of rows to display. A positive integer, capped at 1000. When omitted, all rows are displayed (up to 1000)
A derived dataset that has not been evaluated yet is evaluated before the table is added. renderStatus is 'empty' when the dataset has no rows, and 'ok' otherwise.
reports.updateDataTable(reportId, elementId, options)
Change the display columns and maximum rows of an existing data_table element. The element keeps its ID and its position in the report content. This API corresponds to the edit modal opened from the element menu in the report editor.
const result = await window.midas.reports.updateDataTable('report_001', 'table-xxx', {
columns: 'all'
});
// result.data: { elementId, reportId }
Options:
columns(string[] | 'all') — Columns to display. Same meaning as inaddDataTable(). When given as an array, column names are validated against the current schema of the dataset the element referencesmaxRows(number) — Maximum number of rows to display. A positive integer, capped at 1000
Specify at least one of columns and maxRows; omitting both returns an INVALID_INPUT error. Passing an element of any other type returns an error. Use updateElement() to change graph_builder elements.
reports.addModelSummary(reportId, modelId)
Add a model summary to a report. Supports GLM, GLMM, Linear Regression, Random Forest, ARIMA, and ANOVA.
All model types use the element-reference scheme (the same as addGraph). For GLM, GLMM, and Linear Regression, the coefficient table is added as a type: 'data_table' element under report.elements, and a {{data_table:elementId}} reference is inserted into the report content. For Random Forest, Feature Importance is added as a type: 'data_table' element (Feature and MDI columns, plus a Permutation column when permutation importances are available; sorted by Permutation descending when available, otherwise by MDI descending) when featureImportances is available. The underlying derived dataset is registered in project.datasets, so it also appears in the Data tab listing. Every data_table element this method registers (including the BLUP, ANOVA Type I/III, Prediction Intervals, ARIMA coefficient, and ANOVA tables described below) has display columns 'all', so it shows every column of the derived dataset at render time (the same as columns: 'all' in addDataTable()). Deleting a model automatically removes associated coefficient datasets and prunes any report element that references the deleted datasets or model — data_table, model_stats, graph_builder, crosstab, statistics_summary, and anova. The modelId argument must refer to a saved model; otherwise the call returns APIResult.success === false.
For GLM, GLMM, and Linear Regression, the Model Fit / Random Effects / OLS Fit summary is registered as a type: 'model_stats' element under report.elements, and a {{model_stats:elementId}} reference is inserted into the report content. The element stores only the model id and resolves values from project.models[modelId] at render time, so when the same model id is refitted and project.models is overwritten, existing reports automatically reflect the new values. If the model is deleted after the report is created, the model_stats element renders a "Model not found" placeholder.
GLM and GLMM coefficient tables share the same base columns: Variable, Estimate, Std. Error, Lower N%, Upper N%. LMMs (gaussian + identity GLMMs) estimated with Kenward-Roger inference add a df column after Std. Error. For logit and log links, exp-transformed columns are appended: OR / IRR / exp(Est.), exp(Lower N%), exp(Upper N%). N is the confidence level saved with the model (confidenceLevel, default 95). GLM coefficient tables also include a trailing VIF column (null for the intercept row, for models without an intercept, and for every predictor at once when the correlation matrix of the coefficients is singular). Linear Regression coefficient tables additionally include Std. Coef. and VIF. Std. Coef. is null for the intercept row, and null for every predictor row at once for models without an intercept, for a degenerate response, and when a standard deviation cannot be computed in double precision. Confidence intervals are Wald-type (estimate ± criticalValue × SE). For GLM, the critical value depends on family and link: families that estimate the dispersion parameter from data use , while families with use (see the table under models.run()). For GLMM fixed effects, LMMs with Kenward-Roger inference use a t distribution with per-coefficient degrees of freedom, and other combinations use the standard normal distribution. Linear Regression always uses the t distribution.
What each model type renders:
- GLM: The
model_statselement renders a Model Fit section with AIC, BIC, Residual Deviance, Null Deviance, and Convergence (with the iteration count). - GLMM: When BLUP data exists, a BLUP table (Group, Conditional Mode, Std. Error, Rank — sorted by estimate descending) is added as a
data_tableelement, with a### Random Effects (BLUP)heading and{{data_table:blupElementId}}reference in the content (result.data.blupElementIdreturns the id). Themodel_statselement renders a Random Effects section with Group Variable, Number of Groups, Random Intercept Variance, ICC, and, for families that estimate a dispersion parameter, a residual variance row; and a Model Fit section with Log-Likelihood, AIC, BIC, Convergence (with the iteration count), Observations, and N incomplete (only when rows were excluded for missing values). For LMM (Gaussian + identity link), labels read REML Log-Likelihood and ICC; for every other family+link combination, they read Log-Likelihood (Laplace) and ICC (latent scale). The ICC row shows a value only for LMM and Binomial + logit/probit. For Poisson, Gamma, and Gaussian + log, no theoretically grounded latent-scale residual variance exists, so the ICC row reads N/A (ICC not defined). The residual variance row appears for Gaussian and Gamma. For Gaussian, the value of this row is the estimate of the conditional variance of the response whatever the link, so the row reads Residual Variance. For Gamma, the value of this row is the estimate of the dispersion parameter , and the conditional variance of the response is , so the row reads Dispersion (φ). A Warnings section follows when the model has warnings (singular fit, a variance-component estimate at the upper bound of the search range, non-convergence, a Kenward-Roger correction that fell back to the normal approximation). - Linear Regression: Five elements are registered — coefficient table, ANOVA Type I, ANOVA Type III, Prediction Intervals (per-observation prediction / confidence intervals), and a
model_statselement. Themodel_statselement renders an OLS Fit section with R², Adjusted R², Residual Std. Error, and Observations, plus an Information Criteria section with AIC and BIC. Convergence is not shown because it is trivial for OLS. - Random Forest: A Feature Importance
data_tableelement (Feature and MDI columns, plus a Permutation column when permutation importances are available; sorted by Permutation descending when available, otherwise by MDI descending) whenfeatureImportancesis available, and amodel_statselement rendering a Model Configuration section (Task Type, Number of Trees, Max Depth, Min Samples Split, Min Samples Leaf, Max Features) and OOB Accuracy (classification) or OOB R² (regression). - ARIMA: The coefficient table (AR, MA, SAR, SMA, Intercept or Drift, and Trend terms with confidence intervals) is added as a
data_tableelement, and themodel_statselement renders a Model Fit section with Order (including the seasonal (P, D, Q)[s] part for seasonal models), Log-Likelihood, AIC, BIC, σ², Converged, and Observations. - ANOVA: ANOVA Table and Group Statistics are registered as
data_tableelements. For one-way ANOVA with a computed Tukey HSD, a Tukey HSD table is also added. Themodel_statselement renders an ANOVA Fit section with Design (one-way or two-way, and whether the two-way model includes the interaction), Sum of Squares (two-way only, Type I or Type III), Observations, and N excluded (only when rows were excluded), followed by a Warnings section when the model has warnings (degenerate response, condition number, dropped factor levels, and so on). N excluded carries notes for the excluded rows that no Group Statistics row accounts for: rows with a missing factor value, and rows in groups left with no valid observations. Group Statistics has Group, N, Excluded (only when the model has excluded rows), Mean, Std. Dev., Lower/Upper N% (Individual) (an individual CI per group using the pooled MSE from the ANOVA table, not adjusted for multiple comparisons), Min, and Max columns. Tukey HSD has Group 1, Group 2, Mean Diff, Std. Error, Lower/Upper N% (Simultaneous) (the family-wise CI from the Tukey-Kramer method), plus q Critical, MSE, and DF columns (the pooled values used to compute the CI, repeated on every row); the content also notes that Std. Error is the standard error of the mean difference with CI half-width q_critical × Std. Error / √2, and that Tukey HSD assumes equal population variances.
const result = await window.midas.reports.addModelSummary('report_001', 'model_001');
// result.data: { reportId, addedText, elementId?, statsElementId?, anovaTypeIElementId?, anovaTypeIIIElementId?, groupStatisticsElementId?, tukeyHSDElementId?, predictionIntervalsElementId?, blupElementId? }
// - elementId: id of the coefficient / Feature Importance / ANOVA Table data_table element (RF: only when featureImportances is non-empty)
// - statsElementId: id of the model_stats element that renders Model Fit / Random Effects / OLS Fit / Model Configuration / ANOVA Fit
// - blupElementId: id of the BLUP data_table element for GLMM (only when BLUP data exists)
// - anovaTypeIElementId / anovaTypeIIIElementId / predictionIntervalsElementId: Linear Regression only
// - groupStatisticsElementId: id of the Group Statistics table for ANOVA
// - tukeyHSDElementId: id of the Tukey HSD table for ANOVA (only when Tukey HSD is computed)
When called multiple times for the same model, the coefficients dataset is reused when an existing derived dataset has both the same name and an identical operation definition. Calling this method after refitting the same model with different fit conditions returns APIResult.success === false with a Dataset with name "X" already exists error, because the derived dataset would have the same name but a different operation definition. To record summaries for multiple fit configurations, either delete the previous model or save the new model under a different name before calling this method. Report elements and text are added anew each time.
reports.addGraph(reportId, config)
Add a graph to a report as a report element. Creates a Custom Graph without opening a tab. datasetId accepts a dataset ID or name (case-insensitive). Column names are also resolved case-insensitively. Properties not recognized by AddGraphInput or LayerDefInput are reported in result.warnings. Each warning from the layer validation starts with the label of the layer it concerns, in the same form as configureGraph. Axis scales (scales), per-layer scales (layers[].scales), per-layer tooltips (layers[].tooltip, see addGraphLayer), facets (facets), and the brush selection direction (brush) can be specified. Passing panels instead of layers creates a multi-panel graph. Each panel needs at least one layer. A panel without yScale uses scales.y, and when scales.y is also omitted, its Y axis scale is inferred from the type of the y column mapped in the panel's first layer. See configureGraph for the facets, panels, and brush property reference. globalAes sets the aes shared by every layer. filterExpression sets a filter expression that narrows the rows of the whole graph, in the same syntax as the Data Table filter input. For a graph that draws an intermediate aggregate, set lineageTargetDatasetId (an ancestor of datasetId) to make selection and drill-down act on the underlying data (see configureGraph).
const result = await window.midas.reports.addGraph('report_001', {
datasetId: 'ds_001',
layers: [
{ geom: { type: 'point' }, aes: { x: 'weight', y: 'height' } }
],
title: 'Weight vs Height',
aspectRatio: 'custom',
height: 500,
});
// result.data: { elementId, reportId, renderStatus, renderStatusMessage?, renderWarnings? }
renderStatus indicates the rendering outcome. 'ok' means data points exist and all specified aes properties are applied. 'partial' means data points exist and will be drawn, but some aes properties were not supported by the geom and were ignored (the graph may not match the intended visualization). 'empty' means data rows exist but no renderable points remain (for example, all rows were removed by filters). 'error' means the graph fails validation (a column type the axis does not accept, a palette incompatible with the scale type, a missing required aesthetic, etc.) and cannot render. renderStatusMessage provides a reason when the status is not 'ok'. renderWarnings is also included when the graph has diagnostics. The kinds of diagnostic are the same as in tabs.getGraphBuilder(), and the same content also appears as a warning above the graph in the report view.
Faceted graphs are evaluated with the same data processing as the actual rendering. MIDAS splits the data into panels, computes the domains shared across all panels, and then evaluates each panel individually. States that appear only after panel splitting are detected by this evaluation, so renderStatus and renderWarnings match the rendered result. Each line of renderWarnings carries the title of the panel that reported it, and a warning reported identically by every panel is merged into a single line prefixed with All panels:. When the number of panels exceeds the limit configured in Settings (Max Facet Panels), the graph is not rendered and renderStatus is 'error'.
The graph is stored as a report element and a {{graph_builder:elementId}} reference is appended to the report content.
See Custom Graph Reference for the list of geom, stat, and position types. Each Statistic's params are documented there with their accepted values and defaults. For facets, coordinates, and other graph-level options, see Custom Graph.
coordinates accepts 'flipped' or 'cartesian'. When 'flipped' is specified, the X and Y axes are swapped (e.g., vertical bars become horizontal bars). Defaults to 'cartesian'.
aspectRatio accepts '16:9', '4:3', '1:1', '3:4', '9:16', or 'custom'. Defaults to '16:9'. When a preset value other than 'custom' is specified, the aspect ratio determines the displayed height and height is not used for rendering. When 'custom' is specified, height sets the height. height defaults to 400, minimum 200, maximum 5000.
reports.updateElement(reportId, elementId, config)
Replace the configuration of an existing graph_builder element (full replacement). The element retains its ID and position in the report content. config has the same structure as addGraph (datasetId accepts a dataset ID or name). The element's width and layout are not part of config and are preserved after the update.
const result = await window.midas.reports.updateElement('report_001', 'graph-xxx', {
datasetId: 'ds_001',
layers: [
{ geom: { type: 'bar' }, aes: { x: 'category', y: 'count' } }
],
title: 'Updated Chart',
});
// result.data: { elementId, reportId, renderStatus, renderStatusMessage?, renderWarnings? }
renderStatus, renderStatusMessage, and renderWarnings mean the same as in addGraph().
Specifying an element type other than graph_builder (e.g. data_table, model_stats) returns an error. The display columns and maximum rows of a data_table element can be changed with updateDataTable().
reports.removeElement(reportId, elementId)
Remove an element from a report and its {{type:elementId}} reference from the content. Supports all element types (graph_builder, data_table, model_stats, crosstab, statistics_summary, anova). Associated resources (derived datasets, models) are not deleted.
await window.midas.reports.removeElement('report_001', 'graph-xxx');
// result.data: { elementId, reportId }
reports.scrollTo(reportId, target)
Move the report view to an element or a heading. Moving to a heading does the same thing as choosing that heading in the Contents menu in the report header. Specify exactly one of elementId and heading in target. A heading resolves from either an anchor name such as model-fit or the heading text itself. After the move, the target stays in view until the user starts scrolling, even when a table above it finishes evaluating and changes height.
The report must be open in a tab. When it is not, the call returns NOT_FOUND with a suggestion pointing to tabs.open(). When it is open, the tab is brought to the front before scrolling.
A heading that does not exist and a target that is not rendered both return NOT_FOUND. An element that exists but has no {{type:elementId}} reference in the content returns INVALID_INPUT, because such an element is not rendered.
Elements added with reports.addGraph(), reports.addDataTable(), or reports.addModelSummary() come into view automatically the next time the report is shown. Use this method to move to an arbitrary position.
await window.midas.reports.scrollTo('report_001', { heading: 'Model Fit' });
// result.data: { reportId, anchor: 'model-fit' }
await window.midas.reports.scrollTo('report_001', { elementId: 'graph-xxx' });
// result.data: { reportId, elementId: 'graph-xxx' }
reports.remove(reportId)
Remove a report from the project. Closes any open tabs that reference the report.
await window.midas.reports.remove('report_001');
layout
layout.split(config)
Split a pane to create a new area.
const result = await window.midas.layout.split({
tabId: 'tab_001',
direction: 'horizontal' // 'horizontal' or 'vertical'
});
// result.data: { newPaneId: 'pane_...', originalPaneId: 'pane_...' }
Use the returned newPaneId with tabs.moveToPane() to place tabs in the new pane.
layout.get()
Get how the panes are arranged. To move a tab into a pane you did not create with layout.split() yourself, take its ID from here and pass it to tabs.moveToPane().
const result = await window.midas.layout.get();
// result.data: { rootPane, activePaneId }
rootPane is a tree of panes. Each node is either a split or a pane: type is 'split' for a split and 'container' for a pane.
A split describes how its area is divided. direction is 'horizontal' for a left-right split and 'vertical' for a top-bottom one. splitRatio is the share taken by the first of the two children. children holds them in order: left then right for a horizontal split, top then bottom for a vertical one.
A pane holds tabs. Each entry of tabs has the same shape as in tabs.list(). activeTabId is the tab shown at the front, or null when the pane has no tabs. activePaneId names the pane operated on last, so compare a pane's id against it to tell whether that pane is the active one.
Both kinds of node carry rect, the position and size within the layout area expressed as fractions of the whole, with the origin at the top-left. It comes from the split ratios, so it does not account for the width of the resizers between panes.
A pane also carries viewportRect, the measured position and size of its DOM element in viewport coordinates, in pixels and on the same basis as getBoundingClientRect(). It is null before the pane is rendered. Resizing the window and dragging a resizer both change it, so read it again when matching it against a screenshot.
// In a layout whose right half is split top and bottom, move a tab into the lower pane
const { rootPane } = (await window.midas.layout.get()).data;
const [, right] = rootPane.children;
const [, rightBottom] = right.children;
await window.midas.tabs.moveToPane('tab_001', rightBottom.id);
Error Codes
| Code | Description |
|---|---|
INTERNAL_ERROR | Internal error (unexpected exception) |
NO_PROJECT | No project is loaded |
NOT_FOUND | Specified resource not found |
DATASET_NOT_FOUND | No dataset matching the table name |
COLUMN_NOT_FOUND | Column not found |
INVALID_TAB_TYPE | Invalid tab type |
INVALID_TAB_TYPE_FOR_OPERATION | Tab type does not support this operation |
INVALID_GRAPH_TYPE | Layer operation attempted on non-custom graph |
INVALID_INPUT | Invalid input parameter. Also returned when the input itself is valid but the target is not in a state the operation requires, such as a model waiting to be re-estimated |
INDEX_OUT_OF_RANGE | Layer index out of range |
DATASET_ALREADY_EXISTS | Dataset with the same name (case-insensitive) already exists (when overwrite is false) |
SELF_REFERENCE | The dataset being overwritten is a dependency (ancestor) of the operation |
NAME_CONFLICT | Output name of a derived method collides with an existing primary dataset |
OPERATION_TYPE_MISMATCH | The existing derived dataset was created by a different method. Each derived method (derive, addColumns, setColumnSchema, addOrthogonalPolynomials, reshapeWideToLong, reshapeLongToWide, dummyCode, filter) writes its own operation type. Overwriting across types is rejected — e.g., calling derive() on a dataset originally created by addColumns(), or calling addColumns() on a dataset created by derive() |
AMBIGUOUS_TABLE_NAME | Multiple datasets match the table name case-insensitively |
EXECUTION_ERROR | SQL execution error |
UNSUPPORTED_MODEL_TYPE | Unsupported model type |
MODEL_EXECUTION_ERROR | Model execution error |
PREDICTION_DATASET_FAILED | Could not build the prediction dataset from the result |
NUMERICAL_ERROR | Numerical computation error (e.g., matrix singularity or a diverging iteration) |
INSUFFICIENT_DATA | Not enough data for the analysis (e.g., too few valid observations or factor levels) |
NO_DATA | Dataset has no data loaded |
NO_CONTAINER | No active container |
NO_TARGET | No table reference in SQL |
SQL_PARSE_ERROR | DuckDB could not parse the SQL. message carries the message from DuckDB's parser |
NO_CONFIG | No Graph Builder configuration |
SPLIT_FAILED | Pane split failed |
DUPLICATE_FAILED | Tab duplication failed |
SANDBOX_MODE | Cannot save in sandbox mode |
NO_SIGNING_KEY | Cannot export or download because no signing key is configured |
ENUM_ALREADY_EXISTS | Enum definition already exists |
ENUM_NOT_FOUND | Enum definition not found |
ENUM_IN_USE | Enum definition is referenced by columns |
ENUM_VALUE_MISMATCH | Data contains values outside the enum definition |
FETCH_ERROR | URL fetch failure (network error, timeout, HTTP error) |
USER_CANCELLED | The user or caller cancelled the operation (e.g. declined unsaved changes confirmation, or aborted an ARIMA run in models.run() via signal) |
EDIT_MODE_ACTIVE | A Data Table tab has the dataset open in Edit Mode, so rows cannot be excluded or restored |
Reference
- Live reference: Run
window.midas.help()in the project screen
Also available as a Markdown file.