Matlab Choose Big Data Solution
MathWorksOfficialCreated by MathWorks
- Skill
- Guidelines
- Data and AI
- BSD-2-Clause
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured decision framework for handling large tabular data in MATLAB when standard in-memory functions like readtable fail. It guides users through selecting the appropriate datastore pattern based on data format and processing requirements, such as continuous dataset analysis via tall arrays or independent file-level processing via transform. It also covers advanced scenarios like building custom datastores for proprietary formats and migrating legacy in-memory code to scalable datastore-based workflows.
What it does
- Identifies the correct datastore type for CSV, Parquet, Excel, and MDF files.
- Determines whether to use tall arrays for continuous data or transform for per-file processing.
- Provides migration paths for code hitting out-of-memory errors.
- Guides the implementation of custom datastore classes for non-standard or proprietary formats.
- Advises on parallel execution strategies using parallel pools or mapreducer.
- Enforces a pre-flight check to prevent unnecessary complexity for small datasets.
When to use it
- When processing large tabular files that cause out-of-memory errors.
- When working with multiple tabular files that need to be treated as a single dataset.
- When you need to scale up existing in-memory data processing workflows.
- When you need to build a custom datastore for proprietary or non-standard file formats.
- When you need to process files independently (per-file or per-row-group).
Advantages
- Prevents out-of-memory errors by processing data in manageable chunks.
- Provides a unified interface for diverse file formats.
- Enables seamless scaling from local execution to parallel or cluster computing.
- Reduces development time by providing clear decision paths and code patterns.
- Ensures efficient data handling by avoiding unnecessary full-file loads.
Usage examples
- “My MATLAB code crashes with an out-of-memory error when reading a 20GB CSV file. How can I fix this?”
- The agent will guide you to use tabularTextDatastore combined with tall arrays to process the file in chunks without loading it entirely into memory.
- “I have a folder of 500 Excel files and I need to calculate the mean of each file independently. What is the best way?”
- The agent will recommend using spreadsheetDatastore with the transform function, setting the read granularity to file-level to process each file separately.
- “I have a custom binary file format that MATLAB does not support natively. How do I create a datastore for it?”
- The agent will provide a guide on subclassing matlab.io.Datastore and implementing the necessary methods to integrate your custom format into the datastore ecosystem.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/matlab/matlab-choose-big-data-solution/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip matlab-choose-big-data-solution.zip -d .agents/skills
Documentation by MathWorks
Choose Big Data Solution
Help users and agents select the right MATLAB tool for large tabular data in file-based formats (CSV, Parquet, delimited text, spreadsheets, MDF). The skill encodes a decision flowchart — recommend one clear path, not a menu of options.
When to Use
User or agent has large tabular data in file-based formats (CSV, Parquet, delimited text, spreadsheets, MDF)
User or agent says data is "large", "big", or "huge" (even without a specific size)
User or agent hits an out-of-memory error with
readtable,parquetread, or similarUser or agent has multiple tabular files to process as a single dataset
User or agent has multiple tabular files to process independently (per-file)
User or agent asks how to scale up an existing workflow on tabular file data
User or agent asks about datastores, tall arrays, or mapreduce
User or agent needs to build a custom datastore class for a proprietary or non-standard format
User or agent has large XML, JSON, HTML, or Word document files —
readtablesupports these formats but the built-in datastores do not. Scaling these requires a custom datastore (see "Formats Without a Built-in Datastore" and "Custom Datastores" sections below)
When NOT to Use
Single file that fits in memory (file-size-to-RAM ratio < 0.5) — see Pre-Flight Check and
matlab-import-export-dataskillUser or agent is working with MAT files —
matfileprovides partial I/O for large.matfiles, different workflowUser or agent is working with databases (SQL, ODBC) — use Database Toolbox. See the
matlab-use-databaseandmatlab-use-duckdbskills in thereporting-and-database-accesscategoryUser or agent needs GPU acceleration — different domain
Deep learning training workflows — datastore is correct for data loading, but use
minibatchqueuefor batching (not tall or transform)
Pre-Flight Check
ALWAYS run this check BEFORE recommending datastore or tall array patterns. Estimate the file-size-to-available-RAM ratio (accounting for in-memory expansion of the file format).
| Condition | Route | Rationale |
|---|---|---|
| Single file, ratio < 0.5 | Native MATLAB I/O (readtable, parquetread) | Fits in memory; datastore/tall adds unnecessary complexity |
| Single file, ratio ≥ 0.5, OR user/agent reports OOM | Continue to Decision Flowchart below | Data may not fit in memory |
| Multiple files | Continue to Decision Flowchart below | Datastore patterns provide unified multi-file access |
| Size unknown and user/agent describes data as "large", "big", or "huge" | Continue to Decision Flowchart below | Assume large until proven otherwise |
If the data fits in memory (single file, ratio < 0.5): recommend native I/O and stop. Mention that datastore and tall array patterns exist if the data grows beyond memory in the future, but do not implement them now.
Decision Flowchart
Follow this flowchart strictly. Present ONE recommended path, not multiple alternatives. Only mention alternatives if the situation is ambiguous.
Is the data described as "large" or causing OOM?
│
├── YES
│ │
│ ├── Is the goal to process all data as ONE continuous dataset?
│ │ │
│ │ └── YES → Datastore + Tall Arrays
│ │ Choose datastore by format:
│ │ CSV/delimited text → tabularTextDatastore
│ │ Parquet → parquetDatastore
│ │ Excel (.xlsx/.xls) → spreadsheetDatastore
│ │ MDF (.mf4/.mdf) → mdfDatastore (requires Vehicle Network Toolbox)
│ │ Other formats → Custom Datastore
│ │
│ └── Is the goal to process each unit INDEPENDENTLY?
│ │
│ ├── One read = one FILE
│ │ CSV/delimited text → tabularTextDatastore
│ │ Excel (.xlsx/.xls) → spreadsheetDatastore
│ │ Other formats → Custom Datastore or fileDatastore
│ │
│ └── One read = one ROW GROUP
│ Parquet → parquetDatastore
│
└── NO / UNCLEAR
└── STOP. Ask: (1) what is the file format? (2) do you need to process
all data as one dataset, or each file/unit independently?
Do NOT show code until these are answered.
Optional (requires Parallel Computing Toolbox):
├── Speed up tall arrays locally → Open a parallel pool
├── Speed up readall on transforms → UseParallel
└── Speed up tall arrays on Hadoop/Spark → mapreducer (also requires MATLAB Parallel Server)Critical rules:
When the processing goal is unclear (continuous vs per-file), STOP and ask before writing any code. Do not show code examples, do not generate scripts, do not demonstrate both approaches. Ask which applies and wait for the answer. The correct response to ambiguity is a short clarifying question, not a menu of options with code for each.
When data is described as "large", NEVER lead with
readtableorparquetreadFor per-file or per-row-group processing, NEVER suggest tall arrays (tall merges all data into one dataset and has no concept of file or row group boundaries)
For Parquet, the natural independent unit is the row group, not the file —
parquetDatastoredefaults toReadSize = "rowgroup". Do NOT forceReadSize = "file"on a Parquet datastore unless the workflow truly needs whole-file granularityFor parallelism, recommend parallel pool FIRST; mapreducer is only for Hadoop/Spark
Do NOT present manual chunking (while/read loops) as the first option — tall arrays handle chunking automatically
Do NOT present multiple options — follow the flowchart and recommend ONE clear path
Datastore + Tall Arrays (continuous dataset)
Use when: processing one large file OR multiple files as a single dataset. Use the datastore matching the format (see Decision Flowchart above).
% Single file
ds = tabularTextDatastore("largedata.csv");
tt = tall(ds);
% Multiple files
ds = tabularTextDatastore("data/*.csv");
tt = tall(ds);
% For example, compute statistics with the tall array and gather results.
% Tall handles chunking automatically. Specify DataVars to avoid errors
% on non-numeric columns.
result = groupsummary(tt, "GroupVar", {"mean", "std"}, "NumericVar");
result = gather(result);Key points:
Most common functions work on tall:
mean,std,min,max,groupsummary,sortrows,topkrowsCombine multiple gathers:
[a, b] = gather(tallA, tallB)
For effective tall usage — deferring evaluation to minimize passes, previewing
without exhausting memory, indexing rules, known-good workarounds when a function
does not support tall, and validating tall output against the in-memory result —
see references/tall-array-effective-use.md. Consult it before hand-rolling a
workaround for an unsupported operation: an incorrect workaround can silently
produce wrong results.
DuckDB alternative (R2026a+): When the goal is to filter, aggregate, deduplicate, or sample a large file down to a small in-memory result, a single DuckDB query may be faster than datastore + tall. DuckDB queries CSV/Parquet/JSON files directly without loading them into memory. Requires Database Toolbox.
conn = duckdb();
result = fetch(conn, "SELECT * FROM read_csv_auto('large.csv') WHERE Region = 'West'");
close(conn);For complex DuckDB workflows, see the matlab-use-duckdb skill in the
reporting-and-database-access category.
Migrating from readtable (OOM on large files):
% Before:
T = readtable("large.csv");
stats = groupsummary(T, "Category", "mean");
% After:
ds = tabularTextDatastore("large.csv");
tt = tall(ds);
stats = gather(groupsummary(tt, "Category", "mean"));tabularTextDatastore uses a stricter textscan-based parser. Key gotchas:
TextTypemust be set at creation time — read-only after constructionTrimNonNumericunsupported — read as%q, strip on the tall array%ferrors on non-numeric content — read as%q, convert withstr2doubleExtraColumnsRuledoes not exist — append"%*[^\r\n]"toTextscanFormatsdetectImportOptionsdoes not apply — configure via datastore properties directly
See references/readtable-to-datastore-migration.md for the full property mapping.
Migrating from parquetread: Replace with parquetDatastore + tall. Types
are preserved exactly — no format specifier issues. All parquetread options
map 1:1 to datastore properties. See references/parquetread-to-datastore-migration.md.
Datastore + Transform (per-file)
Use when: each file in a folder should be processed independently (per-file statistics, per-file transformations, file-level aggregation).
% Set up datastore to read one file at a time
ds = tabularTextDatastore("data/*.csv");
ds.ReadSize = "file";
% For example, compute statistics by transforming the datastore with a custom function.
tds = transform(ds, @computeFileStats);
results = readall(tds);
function out = computeFileStats(data)
numVars = vartype("numeric");
out = table( ...
min(data{:, numVars}, [], 1, "omitmissing"), ...
max(data{:, numVars}, [], 1, "omitmissing"), ...
mean(data{:, numVars}, 1, "omitmissing"), ...
VariableNames=["Min", "Max", "Mean"]);
endFor Excel files, use spreadsheetDatastore instead of tabularTextDatastore.
spreadsheetDatastore defaults to ReadSize = "file" so no override is needed.
For per-sheet processing (e.g., statistics per worksheet), set ReadSize = "sheet"
so each read returns exactly one sheet's data.
Key points:
For
tabularTextDatastore, setReadSize = "file"so eachreadreturns exactly one file's data. The defaultReadSizeis a row count, which can split a single file across multiple reads but it will not go across file boundariestransformapplies a function to each read — the datastore output contains only the transformed results, not the original data. The transform function does not need to return the same number of rows as its input (e.g., computing the mean of each variable produces a single row per read)readallon the transformed datastore collects all per-file resultsUse
"omitmissing"for missing-data flags (R2023a+). On R2022b and earlier, use"omitnan"instead.
Do NOT use tall arrays for per-file processing. Tall arrays treat all files as one continuous dataset — they have no concept of file boundaries.
Datastore + Transform (per-row-group, Parquet)
Use when: a Parquet dataset is partitioned by row group (e.g., one row group per item, sensor, region, or time bucket) and each row group must be processed independently. Row groups are the natural unit of independence in Parquet — data from different row groups should not be mixed when the partitioning encodes a meaningful grouping.
% parquetDatastore defaults ReadSize to "rowgroup" — one read = one row group.
% Do NOT set ReadSize = "file": that would mix row groups within the same file.
pds = parquetDatastore("data/");
% For example, compute statistics by transforming the datastore with a custom function.
tds = transform(pds, @computeRowGroupStats);
results = readall(tds);
function out = computeRowGroupStats(data)
out = table( ...
unique(data.item), ...
mean(data.val, "omitmissing"), ...
std(data.val, "omitmissing"), ...
VariableNames=["Item", "Mean", "Std"]);
endKey points:
parquetDatastoredefaults toReadSize = "rowgroup". Eachreadreturns exactly one row group — leave the defaultA row group is the smallest independently readable unit inside a Parquet file. Writers commonly assign one row group per partition key (item, sensor, day), so per-row-group processing preserves those boundaries
Do NOT set ReadSize = "file" on a Parquet datastore for this workflow —
it merges all row groups in a file into a single read and silently mixes data
that was meant to stay separate. Use file-level granularity only when each file
already contains exactly one logical group.
Parallelizing readall (transform workflows)
The readall call in both per-file and per-row-group transform workflows
supports parallel execution when Parallel Computing Toolbox is installed and
licensed. Suggest this only when the toolbox is available:
results = readall(tds, UseParallel=true);UseParallel=trueparallelizes reads across workers in an open parallel pool. If no pool is open, MATLAB opens one automaticallyOnly suggest
UseParallelif Parallel Computing Toolbox is available
Parallel Pool (local parallelism for tall arrays)
Use when: the goal is to speed up a tall array workflow with local cores. Only recommend if Parallel Computing Toolbox is installed and licensed.
% Start a thread or process pool
parpool("Threads");
% parpool("Processes");
ds = tabularTextDatastore("data/*.csv");
tt = tall(ds);
result = gather(mean(tt, "omitmissing"));Tall array computations automatically distribute across available workers when a parallel pool is open. No code changes needed beyond opening the pool.
Do NOT suggest parpool or parallel pool workflows unless Parallel Computing
Toolbox is available — parpool errors without it.
Mapreducer (Hadoop/Spark only)
Use ONLY when the user or agent explicitly has a Hadoop or Spark environment. Requires both Parallel Computing Toolbox and MATLAB Parallel Server.
mr = mapreducer(cluster);
ds = tabularTextDatastore("hdfs:///data/*.csv");
tt = tall(ds);
result = gather(mean(tt, "omitmissing"));Do NOT recommend mapreducer for local parallel processing — a parallel pool is simpler and sufficient for local multi-core execution.
Do NOT suggest mapreducer for Hadoop/Spark execution unless the user or agent
has Parallel Computing Toolbox and a Hadoop or Spark environment with MATLAB
Parallel Server installed and licensed.
Key Functions
| Function | Purpose | When to Use |
|---|---|---|
tabularTextDatastore | Multiple reads per file for CSV/text | Large CSV/text files |
parquetDatastore | Multiple reads per file for Parquet | Large Parquet files |
spreadsheetDatastore | Multiple reads per file for Excel | Large .xlsx/.xls files |
mdfDatastore | Multiple reads per file for MDF | Large .mf4/.mdf files (requires Vehicle Network Toolbox) |
tall | Lazy evaluation over a datastore | Continuous dataset processing |
gather | Execute deferred tall computations | Collect results into memory |
transform | Apply function to each datastore read | Per-file (text) or per-row-group (Parquet) processing |
groupsummary | Grouped statistics (tall-compatible) | Aggregation by category |
parpool | Open parallel worker pool | Speed up tall computations |
mapreducer | Connect to Hadoop/Spark cluster | Hadoop/Spark environments only |
Formats Without a Built-in Datastore
Built-in datastores (such as tabularTextDatastore, parquetDatastore,
spreadsheetDatastore, mdfDatastore) do NOT support JSON, XML, HTML, or Word
documents. If the user or agent has an OOM error with readtable on one of these formats,
do NOT recommend converting to Parquet/CSV first — readtable itself would OOM
during the conversion, creating a circular dependency.
Why not fileDatastore with readtable? A common instinct is to use
fileDatastore(@readtable) — but if readtable OOMs on the file, wrapping it
in fileDatastore does not help because the read function still loads the
entire file into memory.
Instead, recommend building a custom datastore that performs multiple
reads per file (chunked reading), processing it in manageable pieces without
loading it entirely into memory (e.g., reading N lines of XML at a time, or
parsing JSON arrays incrementally). A fileDatastore with such a read
function is one option; implementing the full matlab.io.Datastore interface
is another. See the "Custom Datastores" section below for implementation guidance.
A custom datastore can then be used with tall or transform just like the
built-in datastores.
Custom Datastores
If a file format is not supported by one of the built-in datastores, build a
custom datastore by subclassing matlab.io.Datastore. This applies to:
Proprietary binary formats
Non-standard text formats (custom log files, sensor dumps)
Formats requiring multiple reads per file (large XML, JSON, HTML)
Any format needing custom iteration, partitioning, or stateful reads
Before building a custom datastore, check whether fileDatastore with a
custom ReadFcn is sufficient (one file = one read, each file fits in memory,
no state needed). Set UniformRead=true at construction so readall returns
a concatenated table (read-only after construction). For partial reads within
a file, fileDatastore with ReadMode="partialfile" supports chunked
iteration (see references/custom-datastore/implementation.md for the
required 3-output ReadFcn signature). Only build a full custom datastore class
when those simpler patterns are insufficient.
For full implementation guidance, read references/custom-datastore/implementation.md.
It covers:
Decision flowchart (fileDatastore vs custom class)
Requirement gathering (mixins, FileSet vs BlockedFileSet)
Complete code patterns (FileSet and BlockedFileSet templates)
Common mistakes and conventions
Testing guidelines (
references/custom-datastore/testing-guidelines.md)Mixin API reference (
references/custom-datastore/mixin-decision-tree.md)
When the task asks for tests, always write a separate test-class file — do not
stop after delivering the datastore class. Read
references/custom-datastore/testing-guidelines.md first and follow its list of
required core tests.
Copyright 2026 The MathWorks, Inc.
Files
- ATTRIBUTION.mdAdded by AutoGineer1.1 KiB
- LICENSELicence file added by AutoGineer1.4 KiB
- SKILL.md18.8 KiB
- manifest.yaml576 B
- references/custom-datastore/implementation.md20.0 KiB
- references/custom-datastore/mixin-decision-tree.md7.0 KiB
- references/custom-datastore/testing-guidelines.md5.2 KiB
- references/parquetread-to-datastore-migration.md2.2 KiB
- references/readtable-to-datastore-migration.md3.7 KiB
- references/tall-array-effective-use.md8.4 KiB
references/custom-datastore/implementation.md
Create Custom Datastore
Build custom datastore classes that integrate correctly with MATLAB's datastore ecosystem (tall, parallel, readall, preview).
When to Use
The task involves reading a proprietary or non-standard file format via datastores
The task requires subclassing
matlab.io.DatastoreA datastore is needed that works with
tallor parallel poolsThe task references
FileSet,BlockedFileSet,DsFileSet,DsFileReader, or extensible datastores
When NOT to Use
Built-in datastores cover the format:
tabularTextDatastore(CSV/text),parquetDatastore(Parquet),imageDatastore(images),audioDatastore(audio),fileDatastore(generic files)User wants to read from a database — use
databaseDatastoreinsteadThe task involves tall array algorithms or operations on an existing datastore — this reference only covers creating the datastore class itself
A custom datastore already exists and the task is to use it
Decision Flowchart
flowchart TD
A[Need to read a large dataset?] --> B{Built-in datastore covers format?}
B -->|Yes| Z1[Use built-in datastore]
B -->|No| C{Database source?}
C -->|Yes| Z2[Use databaseDatastore]
C -->|No| D{Unsure if datastore is right approach?}
D -->|Yes| Z3[Use the decision flowchart in the main skill]
D -->|No| E{Reads confined to one file, stateless?}
E -->|Yes| F{"Need multiple reads per file (chunked reading)?"}
F -->|No| Z4["Use fileDatastore with ReadMode=file"]
F -->|Yes| G{"Need custom iteration/partitioning?"}
G -->|No| Z5["Use fileDatastore with ReadMode=partialfile or bytes"]
G -->|Yes| H[Create custom datastore]
E -->|No| H
H --> I{Fixed-size records within files?}
I -->|Yes| Z6[Use BlockedFileSet pattern]
I -->|No| Z7[Use FileSet pattern]Custom Datastore vs FileDatastore
fileDatastore | Custom datastore | |
|---|---|---|
| Use when | One file = one read, or simple partial reads via ReadMode | Stateful reads, non-file sources, or custom iteration/partitioning |
| Effort | Minimal — just supply a reader function | Full class implementation |
| Flexibility | File-per-read or partial reads (ReadMode="partialfile" or "bytes") | Complete control over read granularity and state |
If the user's format can be read with a single function call per file (e.g.,
load, imread, jsonread), recommend fileDatastore with a custom
ReadFcn first. Set UniformRead=true at construction so that readall
returns a concatenated table instead of a cell array (UniformRead is
read-only after construction).
fds = fileDatastore("data/*.json", ...
ReadFcn=@(f) struct2table(jsondecode(fileread(f))), ...
UniformRead=true);
results = readall(fds); % single concatenated tableFor multiple reads per file (chunked reading), fileDatastore with
ReadMode="partialfile" supports stateful iteration within each file. The
ReadFcn signature changes to 2 inputs and 3 outputs:
% [data, userdata, done] = readFcn(filename, userdata)
% - First call: userdata is []
% - Set done=true on the last chunk to advance to the next file
fds = fileDatastore("data/*.bin", ...
ReadMode="partialfile", ...
ReadFcn=@myChunkedReader);Only escalate to a custom datastore class when fileDatastore patterns are
insufficient (e.g., custom partitioning, non-file sources, or complex state).
Workflow
1. Gather Requirements
STOP — do not write any code until this step is complete.
Before writing any code, determine which mixins and file management utility to use. Follow this decision process:
Check if requirements are already specified — if the prompt explicitly states mixin needs (e.g., "I need tall array support", "use defaults for everything else", "no parallel needed"), use them directly. Phrases like "use defaults" or "defaults are fine" count as explicit specification.
Detect automatically — run
versionin MATLAB to determine the installed release, if MATLAB is available.Ask the user — if the prompt does NOT explicitly address tall/parallel support or other mixin needs, you MUST ask before implementing. Present the unresolved questions as a checklist and wait for answers. Do not silently apply defaults — the user needs to confirm their requirements.
When to ask vs. when to proceed:
Prompt says nothing about tall, parallel, partition, shuffle, Hadoop, or write → ask about at minimum: (1) tall/parallel support, and (2) whether Shuffleable, HadoopLocationBased, or FileWritable are needed.
Prompt explicitly states requirements or says "use defaults" → proceed without asking.
User says "don't ask questions" or requirements are fully specified → proceed without asking.
Sub-agent mode only: If there is no human in the loop (e.g., you are a sub-agent in an automated pipeline), apply the defaults shown below for any unresolved questions instead of asking. Even in sub-agent mode, you MUST still present the full list of requirement decisions: state which mixins you are including, which you are declining, and why. Explicitly mention Partitionable, Shuffleable, HadoopLocationBased, and FileWritable in your decision summary.
MATLAB version:
| Question | Why it matters |
|---|---|
What MATLAB version are you targeting? (Run version in MATLAB if available; default to R2021a if undetermined.) | Determines available classes (Subsettable requires R2022b+). This reference requires R2021a or later. |
Mixins (superclasses):
Note: matlab.io.Datastore already inherits from matlab.mixin.Copyable — do
not add it separately. The copyElement method is always required (see Step 3).
| Mixin | Question | Default |
|---|---|---|
matlab.io.datastore.Partitionable | Do you need tall array or parallel computing support? | Yes |
matlab.io.datastore.Subsettable | Do you need to extract non-adjacent or specific data slices by index, randomized shuffling, or parallel minibatch processing? (R2022b+; requires every read to be independently addressable) | No. Subsumes Partitionable and Shuffleable — do not add those separately if using this. |
matlab.io.datastore.HadoopLocationBased | Does the data live on HDFS or need Hadoop/Spark? | No |
matlab.io.datastore.Shuffleable | Do you need random shuffling for deep learning training? (Not needed if using Subsettable) | No |
matlab.io.datastore.FileWritable | Does the datastore need to write data back to files? | No |
matlab.io.datastore.FoldersPropertyProvider | Do you need a Folders property for folder-based access or writing? | No |
File management utility (pick one — used internally, not inherited):
| Utility | Question | Default |
|---|---|---|
matlab.io.datastore.FileSet | Is each file one observation (whole-file reads)? | Yes (R2020a+) |
matlab.io.datastore.BlockedFileSet | Are there fixed-size records/blocks within files? | No (R2020a+) |
Auto-inference rules — select automatically and inform the user:
Data is large relative to memory → include
PartitionableUser needs non-adjacent data slices by index, randomized shuffling, or parallel minibatch processing AND R2022b+ AND every read is independently addressable → include
Subsettableinstead ofPartitionableandShuffleableFile paths use HDFS URIs (
hdfs://) → includeHadoopLocationBasedUser mentions deep learning or training AND not using
Subsettable→ includeShuffleableData spans multiple folders or datastore needs write support → include
FoldersPropertyProviderUser needs
writeallor export → includeFileWritableFixed-size records packed sequentially in files → use
BlockedFileSetUser targets < R2022b → cannot use
Subsettable
2. Select Class Hierarchy
Based on requirements, construct the classdef line:
% Minimum (always include Partitionable unless user explicitly declines):
classdef MyDatastore < matlab.io.Datastore & ...
matlab.io.datastore.PartitionableSee references/custom-datastore/mixin-decision-tree.md for the full
combination table and required methods per mixin.
3. Implement the Datastore
Follow this layered approach — implement serial first, then add parallel/other:
Always required (serial foundation):
hasdata— checkCurrentFileIndex <= obj.FileSet.NumFilesread— load data fromobj.FileSet.FileInfo.Filename(CurrentFileIndex), advance indexreset— setCurrentFileIndex = 1progress(Hidden) — compute(CurrentFileIndex-1)/obj.FileSet.NumFilescopyElement(protected) — deep copyFileSetviacopy(obj.FileSet). Always required becausereadallandpreviewinternally copy the datastore.
If fixed-size block reads are needed (records within files):
Use
BlockedFileSetinstead ofFileSethasdata— delegate tohasNextBlock(obj.BlockedFileSet)read— usenextblockto get offset/size, then call the custom read functionreset— delegate toreset(obj.BlockedFileSet)
If Partitionable:
partition(public) —copyself, thenpartition(obj.FileSet, n, idx)maxpartitions(protected) — delegate tomaxpartitions(obj.FileSet)
If Subsettable (R2022b+, use instead of Partitionable when fine-grained subsetting is needed):
maxpartitions(protected) — return total number of independent reads (e.g.,obj.FileSet.NumFiles)subsetByReadIndices(protected) — copy self, keep only the reads at given indices (e.g., rebuild FileSet from indexed file list)Do NOT also implement
partitionorshuffle—Subsettableprovides them automaticallyDo NOT also inherit from
PartitionableorShuffleable
If HadoopLocationBased:
initializeDatastore(Hidden) — rebuild FileSet from Hadoop info structgetLocation(Hidden) — returnobj.FileSetisfullfile(Hidden, optional) — checkobj.FileSet.FileSplitSize == 'file'
If Shuffleable (not needed if using Subsettable):
shuffle— copy self, randomize file order
4. Qualify via Tests
After implementation, write and run tests. See references/custom-datastore/testing-guidelines.md
for the required test patterns. At minimum, verify:
readallreturns all data without advancing the original positionpreviewreturns data from the beginning without affecting stateresetreturns to the start after partial readshasdatatransitions from true to false correctlypartitioncreates valid sub-datastores that sum to the whole (if Partitionable)tallconstruction works without error (if Partitionable)
Run tests via mcp__matlab__run_matlab_test_file and fix any failures before
reporting the implementation as complete.
FileSet vs BlockedFileSet
| Feature | FileSet | BlockedFileSet |
|---|---|---|
| Use when | Each file = one observation (whole-file reads) | Fixed-size records packed within files |
| File listing | fs.FileInfo.Filename | No FileInfo property — per-block filenames are in BlockInfo.Filename (one row per block) |
| Count | fs.NumFiles | fs.NumBlocks (may overcount; use iteration) |
| Iteration | hasNextFile/nextfile | hasNextBlock/nextblock |
| Block info | N/A — full file path only | BlockInfo with Filename, FileSize, Offset, BlockSize |
| Partitioning | partition(fs, n, idx) | partition(fs, n, idx) |
| Reading | Custom reader function (format-specific) | Custom reader function (typically fopen/fseek/fread internally) |
Choose FileSet when each file is one logical observation (most common).
Choose BlockedFileSet when data consists of fixed-size records packed
sequentially in files and you need block-level iteration and partitioning.
Patterns
Base Datastore with FileSet (Default Pattern)
This is the correct starting point for most custom datastores. Each file is read as one observation:
classdef MyDatastore < matlab.io.Datastore & ...
matlab.io.datastore.Partitionable
properties (Access = private)
CurrentFileIndex (1,1) double = 1
FileSet matlab.io.datastore.FileSet
end
properties (Dependent)
Files
end
methods
function ds = MyDatastore(location)
arguments
location (1, :) {mustBeText}
end
ds.FileSet = matlab.io.datastore.FileSet(location, ...
"IncludeSubfolders", true, ...
"FileExtensions", ".bin");
reset(ds);
end
function files = get.Files(ds)
files = ds.FileSet.FileInfo.Filename;
end
function tf = hasdata(ds)
tf = ds.CurrentFileIndex <= ds.FileSet.NumFiles;
end
function [data, info] = read(ds)
if ~hasdata(ds)
error("MyDatastore:NoData", ...
"No more data. Use reset to start over.");
end
filepath = ds.Files(ds.CurrentFileIndex);
data = readMyFormat(ds, filepath);
info.Filename = filepath;
info.FileSize = ds.FileSet.FileInfo.FileSize(ds.CurrentFileIndex);
ds.CurrentFileIndex = ds.CurrentFileIndex + 1;
end
function reset(ds)
ds.CurrentFileIndex = 1;
end
function subds = partition(ds, numPartitions, index)
subds = copy(ds);
subds.FileSet = partition(ds.FileSet, numPartitions, index);
subds.CurrentFileIndex = 1;
end
end
methods (Hidden)
function frac = progress(ds)
frac = (ds.CurrentFileIndex - 1) / ds.FileSet.NumFiles;
end
end
methods (Access = protected)
function n = maxpartitions(ds)
n = ds.FileSet.NumFiles;
end
function dsCopy = copyElement(ds)
dsCopy = [email protected](ds);
dsCopy.FileSet = copy(ds.FileSet);
end
end
methods (Access = private)
function data = readMyFormat(~, filepath)
% --- Format-specific reading logic here ---
end
end
endBlock-Based Datastore with BlockedFileSet
Use this pattern when data consists of fixed-size records packed sequentially
in files. Each read returns one block of data.
classdef MyBinaryDatastore < matlab.io.Datastore & ...
matlab.io.datastore.Partitionable
properties (Access = private)
BlockedFileSet matlab.io.datastore.BlockedFileSet
CurrentBlockIndex (1,1) double = 0
TotalBlocks (1,1) double = 0
end
methods
function ds = MyBinaryDatastore(location, blockSize)
arguments
location (1, :) {mustBeText}
blockSize (1,1) double = 100
end
ds.BlockedFileSet = matlab.io.datastore.BlockedFileSet(location, ...
"FileExtensions", ".bin", "BlockSize", blockSize);
ds.TotalBlocks = countBlocks(ds);
reset(ds);
end
function tf = hasdata(ds)
tf = hasNextBlock(ds.BlockedFileSet);
end
function [data, info] = read(ds)
if ~hasdata(ds)
error("MyBinaryDatastore:NoData", ...
"No more data. Use reset to start over.");
end
blkInfo = nextblock(ds.BlockedFileSet);
data = myBlockReader(ds, blkInfo);
info.Filename = blkInfo.Filename;
info.Offset = blkInfo.Offset;
info.BlockSize = blkInfo.BlockSize;
ds.CurrentBlockIndex = ds.CurrentBlockIndex + 1;
end
function reset(ds)
reset(ds.BlockedFileSet);
ds.CurrentBlockIndex = 0;
end
function subds = partition(ds, numPartitions, index)
subds = copy(ds);
subds.BlockedFileSet = partition(ds.BlockedFileSet, numPartitions, index);
subds.TotalBlocks = countBlocks(subds);
reset(subds);
end
end
methods (Hidden)
function frac = progress(ds)
if ds.TotalBlocks == 0
frac = 1;
else
frac = ds.CurrentBlockIndex / ds.TotalBlocks;
end
end
end
methods (Access = protected)
function n = maxpartitions(ds)
n = maxpartitions(ds.BlockedFileSet);
end
function dsCopy = copyElement(ds)
dsCopy = [email protected](ds);
dsCopy.BlockedFileSet = copy(ds.BlockedFileSet);
end
end
methods (Access = private)
function data = myBlockReader(~, blkInfo)
% --- Block reading logic here (e.g., fopen/fseek/fread) ---
end
function n = countBlocks(ds)
bfsCopy = copy(ds.BlockedFileSet);
reset(bfsCopy);
n = 0;
while hasNextBlock(bfsCopy)
nextblock(bfsCopy);
n = n + 1;
end
end
end
endWhy countBlocks instead of NumBlocks: BlockedFileSet.NumBlocks may
overcount (includes phantom trailing blocks). Iterating gives the true count
that matches what hasNextBlock/nextblock will produce.
Writing the Custom Read Function
The read method is the custom read function — it contains the format-specific
parsing logic. Helper methods (like readMyFormat or myBlockReader in the
patterns above) are optional but useful for isolating the parsing from the
datastore plumbing.
Key design decisions to resolve:
What is the file format? (binary with header, delimited text, structured records, etc.)
Does the format support offset-based reading (for sub-file splits)?
What MATLAB type should the output be? (table, numeric array, struct, etc.)
When blocks are NOT feasible (format has headers, variable-length records, or records don't align to byte boundaries):
Use
FileSet— each file is read whole
Common Mistakes
| Mistake | Why It's Wrong | Correct Approach |
|---|---|---|
Using DsFileSet or DsFileReader | Legacy classes; not recommended for new code | Use FileSet for whole-file reads, BlockedFileSet for fixed-size blocks |
Omitting copyElement | readall and preview share state with the original; mutations corrupt the datastore | Always implement copyElement with copy(ds.FileSet) or copy(ds.BlockedFileSet) |
Making maxpartitions public | Violates superclass contract; MATLAB may error | Must be methods (Access = protected) |
Adding matlab.mixin.Copyable to the classdef | matlab.io.Datastore already inherits from it; adding it again is redundant | Do not list it as a superclass — just implement copyElement |
Using matlab.lang.Copyable | Does not exist | Correct: [email protected](ds) |
Using BlockedFileSet.NumBlocks for progress | May overcount (phantom trailing blocks); progress never reaches 1.0 | Track blocks read with your own counter; count true blocks via iteration at construction |
Adding Shuffleable without a training use case | Unnecessary complexity | Only add when user confirms deep learning/training need |
Omitting Partitionable | Datastore won't work with tall or parallel pools | Default to including it unless user explicitly declines |
Returning inconsistent types from read | readall vertcats results; mixed types (table one call, array the next) cause errors | Always return the same type (table or array) from every read call |
Conventions
Use
FileSetfor whole-file reads,BlockedFileSetfor fixed-size block readsDo not use legacy
DsFileSetorDsFileReaderin new codeNever use manual
dir+ indexing for file managementAlways implement
copyElement—readallandpreviewcopy the datastore internallyUse
argumentsblocks for constructor validationReturn
[data, info]fromreadwhereinfois a struct with at leastFilenameTerminate all methods and the classdef with
endUse
lowerCamelCasefor method and property names
Copyright 2026 The MathWorks, Inc.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- MathWorks
- Publisher
- MathWorks (Official)
- Source
- matlab/matlab-agentic-toolkit · skills-catalog/matlab-data-import-and-analysis/matlab-choose-big-data-solution · commit 2a7f215
- Licence
- BSD 2-Clause License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-choose-big-data-solution.zip