Data Preparation
Contents
Data Preparation#
This page explains how to format your time-series data for use with NeurEco Dynamic models, whether for training or evaluation.
On this page
Data Structure Overview#
Dynamic models work with two arrays per time series, and each array begins with a time column (column 0):
Excitation (input) array: column 0 is time, the remaining columns are the input signals driving the system
Output array: column 0 is time, the remaining columns are the target system responses
Both arrays of a sequence must carry the same time column. Multiple sequences (experiments, simulations, or recordings) can be combined for training.
Note
Deprecated separate-time form. Passing the time as a separate array
(train_time_list and its valid_/test_ counterparts, with the
excitation/output arrays then holding pure excitations/outputs) is still
supported for backward compatibility but emits a DeprecatedParameterWarning.
Prefer placing the time in the first column of each excitation and output array.
Critical Requirements#
Warning
Violating these requirements will cause errors or incorrect results.
Requirements for All Dynamic Models#
Matching row counts: For each sequence, the excitation and output arrays must have the same number of rows and share the same time column (column 0)
Consistent feature counts: All excitation arrays must have the same number of columns; all output arrays must have the same number of columns (the leading time column included)
Corresponding indices: Lists must maintain correspondence — the i-th excitation and output arrays must belong to the same sequence
Template-Specific Requirements#
Requirement |
ODE |
Convolutional Dynamic |
|---|---|---|
Time stamp ordering |
Required — the time column (column 0) must be sorted in strictly increasing order (no repeated time stamps) |
Required — the time column (column 0) must be sorted in strictly increasing order (no repeated time stamps) |
Time step uniformity |
Not required — can handle non-uniform time steps (as long as they are strictly increasing) |
Required — must be uniform within and across sequences |
Time step consistency |
Can vary between sequences |
Must be identical across all sequences |
What Can Vary Between Sequences#
Duration (number of time steps)
Start time
Time step spacing (ODE only)
What Must Be Constant#
Number of input features
Number of output features
Time step (Convolutional Dynamic only)
Data Formats by Interface#
File Formats
The GUI accepts data in the following formats:
CSV with
;or,separatorNumPy
.npyfilesMATLAB
.matfiles
Allowed data types: int, float, double
Input (Excitation) Files
Each input file contains a table where:
First column: Time variable (uniformly spaced for Convolutional Dynamic; for ODE any spacing is allowed, provided the values are sorted in strictly increasing order)
Remaining columns: Input features (excitations)
Each row: Time point and corresponding input values
CSV files may include a header row
Example CSV structure:
time;force;temperature;pressure
0.00;1.2;25.3;101.5
0.01;1.4;25.4;101.6
0.02;1.3;25.5;101.7
...
Output Files
Each output file contains a table where:
First column: Time variable (must match the corresponding input file)
Remaining columns: Output features
Each row: Time point and corresponding output values
CSV files may include a header row
Example CSV structure:
time;position;velocity
0.00;0.0;0.0
0.01;0.012;0.24
0.02;0.048;0.47
...
Multiple Sequences
When your data comes from multiple experiments:
Provide multiple input and output file pairs
Maintain correspondence between input and output files
For Convolutional Dynamic: all files must use the same time step
For ODE: time steps can vary between sequences
Different sequences can have different durations and start times
Data Format
The API expects data as lists of NumPy arrays. Each element in a list represents a separate time series.
You need two parallel lists, and every array carries the time in its first column:
Excitation (input) list: arrays whose column 0 is time, remaining columns are input signals
Output list: arrays whose column 0 is time, remaining columns are target outputs
Supported NumPy data types: int, float, double
The Time Column
The first column (column 0) of every excitation and output array holds the time stamps. It must be:
Sorted in strictly increasing order — no repeated time stamps (e.g.
0.1, 0.2, 0.5is valid;0.1, 0.5, 0.2is not)Uniformly spaced for Convolutional Dynamic; ODE accepts non-uniform spacing as long as the time stamps are strictly increasing
Identical in the excitation array and the output array of the same sequence
The ordering must be strict because the time step between two consecutive samples is used as a denominator during integration: equal (or decreasing) time stamps would give a zero or negative \(dt\), which is not allowed.
import numpy as np
# Uniform spacing (required for Convolutional Dynamic, works for ODE)
time = np.linspace(0, 10, 1001) # 0.00, 0.01, 0.02, ..., 10.00
time = np.arange(0, 10.01, 0.01)
# Non-uniform but strictly increasing spacing (ODE only)
time = np.array([0.0, 0.1, 0.15, 0.3, 0.5, 1.0, 2.0, 5.0])
Excitation (Input) Arrays
Each excitation array is 2-dimensional:
Rows = time steps
Column 0 = time
Remaining columns = input features
# 1001 time steps, time + 3 input features
time = np.linspace(0, 10, 1001)
features = np.random.randn(1001, 3)
excitation = np.column_stack([time, features]) # shape (1001, 4)
# Row 0: time[0] and the input values at time[0]
# Row 1: time[1] and the input values at time[1]
# etc.
Output Arrays
Each output array is 2-dimensional:
Rows = time steps (must match the excitation array)
Column 0 = time (the same time column as the excitation array)
Remaining columns = output features
# 1001 time steps, time + 2 output features
targets = np.random.randn(1001, 2)
output = np.column_stack([time, targets]) # shape (1001, 3)
Multiple Sequences Example
import numpy as np
# Common time step for all sequences
dt = 0.01
# Experiment 1: 10 seconds
time_1 = np.arange(0, 10, dt)
exc_1 = np.column_stack([time_1, np.random.randn(len(time_1), 3)])
out_1 = np.column_stack([time_1, np.random.randn(len(time_1), 2)])
# Experiment 2: 5 seconds
time_2 = np.arange(0, 5, dt)
exc_2 = np.column_stack([time_2, np.random.randn(len(time_2), 3)])
out_2 = np.column_stack([time_2, np.random.randn(len(time_2), 2)])
# Experiment 3: 8 seconds, starting at t=2
time_3 = np.arange(2, 10, dt)
exc_3 = np.column_stack([time_3, np.random.randn(len(time_3), 3)])
out_3 = np.column_stack([time_3, np.random.randn(len(time_3), 2)])
# Each array: column 0 = time, remaining columns = excitations / outputs.
train_exc_list = [exc_1, exc_2, exc_3]
train_out_list = [out_1, out_2, out_3]
File Formats
The CLI accepts the same formats as the GUI:
CSV with
;or,separatorNumPy
.npyfilesMATLAB
.matfiles
Configuration File
Specify data files in the configuration JSON:
{
"neurecoODE_build": {
"exc_filenames": [
"data/experiment1_input.csv",
"data/experiment2_input.csv"
],
"output_filenames": [
"data/experiment1_output.csv",
"data/experiment2_output.csv"
]
}
}
File indices must correspond (first input with first output, etc.). A
Convolutional Dynamic configuration (neurecoConvolutiveDynamic_build) lists
the same files under data, as training.inputs and training.targets
(see CLI Reference).
Data Normalization#
NeurEco automatically normalizes your data before training. Understanding normalization options helps you get better results.
How Normalization Works#
NeurEco applies the formula:
Where shift and scale are computed based on your chosen methods.
Note
ODE and Convolutional Dynamic templates have different default normalization options optimized for their respective use cases.
Shift Methods#
Method |
Description |
Default for |
|---|---|---|
|
Shift by the mean value (centers data around 0) |
ODE |
|
Shift by the minimum value |
Convolutional Dynamic |
|
No shifting applied |
— |
Scale Methods#
Method |
Description |
Default for |
|---|---|---|
|
Scale by the L2 norm |
ODE |
|
Scale to [0, 1] range based on min/max values |
Convolutional Dynamic |
|
Scale by the maximum absolute value |
— |
|
Scale by the standard deviation |
— |
|
No scaling applied |
— |
Per-Feature Normalization#
The normalize per feature option controls whether normalization is applied independently to each feature or globally across all features.
Set to True when:
Features represent different physical quantities (e.g., temperature and pressure)
Features have vastly different scales or units
You want each feature to contribute equally
Set to False when:
Features represent the same quantity (e.g., temperatures at different locations)
You’re working with field data where relative magnitudes matter
In the Build panel, under Normalization:
Select shift and scale methods from dropdowns
Check/uncheck Normalize per feature for inputs and outputs
# Each array: column 0 = time, remaining columns = excitations / outputs.
config = Dynamic.ODEBuildConfig(
train_exc_list=train_exc_list,
train_out_list=train_out_list,
# Input normalization
inputs_shifting="mean",
inputs_scaling="l2",
inputs_normalize_per_feature=True,
# Output normalization
outputs_shifting="mean",
outputs_scaling="l2",
outputs_normalize_per_feature=True,
)
# Each array: column 0 = time, remaining columns = excitations / outputs.
config = Dynamic.ConvolutiveDynamicBuildConfig(
train_exc_list=train_exc_list,
train_out_list=train_out_list,
# Input normalization
inputs_shifting="min",
inputs_scaling="minmax",
inputs_normalize_per_feature=True,
# Output normalization
outputs_shifting="min",
outputs_scaling="minmax",
outputs_normalize_per_feature=True,
)
{
"neurecoODE_build": {
"settings": {
"input_normalization": {
"shift_type": "mean",
"scale_type": "l2",
"normalize_per_feature": true
},
"output_normalization": {
"shift_type": "mean",
"scale_type": "l2",
"normalize_per_feature": true
}
}
}
}
{
"neurecoConvolutiveDynamic_build": {
"normalizers": {
"inputs": {
"shift_type": "min",
"scale_type": "minmax",
"normalize_per_feature": true
},
"targets": {
"shift_type": "min",
"scale_type": "minmax",
"normalize_per_feature": true
}
}
}
}
Tip
If the default normalization doesn’t suit your needs, you can normalize your data yourself before passing it to NeurEco and set normalization to none.
Complete Python Example#
import numpy as np
from NeurEco import NeurEcoDynamic as Dynamic
# Define common time step
dt = 0.001 # 1 ms
# Experiment 1: 5 seconds, 3 inputs, 2 outputs (time in column 0)
t1 = np.arange(0, 5, dt)
x1 = np.column_stack([
t1, # Column 0: time
np.sin(2 * np.pi * 1.0 * t1), # 1 Hz sine wave
np.sin(2 * np.pi * 2.5 * t1), # 2.5 Hz sine wave
np.random.randn(len(t1)) * 0.1 # Noise
])
y1 = np.column_stack([
t1, # Column 0: time
np.sin(2 * np.pi * 1.0 * t1 + 0.5), # Phase-shifted response
np.cos(2 * np.pi * 2.5 * t1) # Another response
])
# Experiment 2: 3 seconds
t2 = np.arange(0, 3, dt)
x2 = np.column_stack([
t2, # Column 0: time
np.sin(2 * np.pi * 1.5 * t2),
np.sin(2 * np.pi * 3.0 * t2),
np.random.randn(len(t2)) * 0.1
])
y2 = np.column_stack([
t2, # Column 0: time
np.sin(2 * np.pi * 1.5 * t2 + 0.5),
np.cos(2 * np.pi * 3.0 * t2)
])
# Each array: column 0 = time, remaining columns = excitations / outputs.
train_exc_list = [x1, x2]
train_out_list = [y1, y2]
# Verify shapes
for i, (x, y) in enumerate(zip(train_exc_list, train_out_list)):
print(f"Sequence {i}: excitation={x.shape}, output={y.shape}")
# Output:
# Sequence 0: excitation=(5000, 4), output=(5000, 3)
# Sequence 1: excitation=(3000, 4), output=(3000, 3)
# Now ready to use with NeurEco ODE or Convolutional Dynamic
model = Dynamic.ODE()
config = Dynamic.ODEBuildConfig(
train_exc_list=train_exc_list,
train_out_list=train_out_list,
# ... other configuration options
)