The converter can apply additional calculations to the selected x-values and y-values. These calculations are stored in xOperations and yOperations and are executed when an output table is processed.
There are two layers involved:
The four operation types are:
columnvaluemetadata_valueheader_valueThe four mathematical operators are:
+ addition- subtraction* multiplication: divisionIn other words:
A profile can say things like:
For each output table, the converter uses these fields in the table configuration:
xColumnyColumnxOperationsyOperationsxOperationsDescription and yOperationsDescriptionEach entry in xOperations or yOperations contains at least:
type – the operation typeoperator – one of +, -, *, :Depending on the type, additional fields are used, such as column, value, table, regex, line, and ignore_missing_values.
process(self)Prepare the raw x/y data, apply all configured x/y operations, and build the final output table.
xColumn into x_rows and from yColumn into y_rows.column, the converter preloads the referenced column values into operation['rows'].xOperations are executed in their stored order.yOperations are executed in their stored order.The order of operations matters.
If several operations are configured, each one works on the result of the previous one. So * 2 followed by + 5 is not the same as + 5 followed by * 2.
_run_operation(self, rows, operation)Execute one configured operation on all values of one target row set, meaning either all x-values or all y-values.
For each row, the method first determines the right-hand operand based on operation.type. Then it applies the selected mathematical operator by calling apply_operation.
column – Value from another table columnoperation.column.tableIndex and operation.column.columnIndex.operation['rows'].Plain-language interpretation:
The current x- or y-value is combined with the value from another column at the same row index.
value – Fixed scalar valueoperation.value.Plain-language interpretation:
All x- or y-values are modified by the same fixed number, such as + 5 or * 1000.
metadata_value – Value from table metadataoperation.value and a reference table index in operation.table.self.input_tables[int(operation.table)]['metadata'].ignore_missing_values.Plain-language interpretation:
The current x- or y-value is combined with a numeric value taken from the metadata of a selected input table.
header_value – Value extracted from the table header via regex_search_regex searches either a specific header line or the whole header text.Plain-language interpretation:
The current x- or y-value is combined with a number that is found somewhere in the selected table header.
After the right-hand value has been determined, the converter tries to convert it to a float.
ignore_missing_values is false, a CalculationError is raised.ignore_missing_values is true, that operation is skipped.This special handling is mainly relevant for metadata_value and header_value, because those two types may depend on metadata or free text that is missing or non-numeric.
apply_operation(self, value, op_value, op_operator)Apply the mathematical operator to the current row value (value) and the resolved operation value (op_value).
if op_operator == '+':
return float_value + float(op_value)
if op_operator == '-':
return float_value - float(op_value)
if op_operator == '*':
return float_value * float(op_value)
if op_operator == ':':
return float_value / float(op_value)
+-*:The current implementation only applies the operation when op_value is truthy. This means that a right-hand value of 0 will not be applied and will leave the current row unchanged.
process(self) prepares the x/y data and executes all configured operations in order._run_operation(self, rows, operation) determines the right-hand value depending on the operation type.apply_operation(self, value, op_value, op_operator) performs the actual mathematical calculation.So the backend logic can be summarized like this:
This part of the frontend is the visual configuration for the x/y operations you saw in the backend.
It lets a user decide:
<TableColumn
table={table.table}
label="Which column should be used as x-values?"
columnKey="xColumn"
operationsKey="xOperations"
...
/>
<TableColumn
table={table.table}
label="Which column should be used as y-values?"
columnKey="yColumn"
operationsKey="yOperations"
...
/>
A selection field for the x-column and a selection field for the y-column.
These define the starting values before any operation is applied.
The selections are stored as:
table.xColumntable.yColumnThe backend reads these values first, then applies xOperations and yOperations on top of them.
Special case:
If the table header says DATA CLASS = XYDATA, the x-values are configured differently in the frontend, via metadata-related identifiers such as FIRSTX, LASTX, and DELTAX, instead of the normal x-column operation block.
<Form.Select size="sm" value={value} onChange={event => onChange(event.target.value)}>
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value=":">:</option>
</Form.Select>
Each configured operation row starts with a small dropdown containing:
+-*:The selected symbol is stored in operation.operator. In the backend, this value is passed to apply_operation, where it determines whether the calculation becomes addition, subtraction, multiplication, or division.
The frontend offers four buttons for creating operations. Each new operation is added either to xOperations or to yOperations.
<Button onClick={() => addOperation(operationsKey, 'column')}>
Add column operation
</Button>
<Button onClick={() => addOperation(operationsKey, 'value')}>
Add scalar operation
</Button>
<Button onClick={() => addOperation(operationsKey, 'metadata_value')}>
Add table metadata operation
</Button>
<Button onClick={() => addOperation(operationsKey, 'header_value')}>
Add table header operation
</Button>
These correspond directly to the four backend operation types.
columnAfter adding a column operation, the user sees:
The selected column is stored in:
operation = {
type: 'column',
operator: '+',
column: {
tableIndex: ...,
columnIndex: ...
}
}
The backend then reads the referenced column values row by row and combines them with the current x- or y-values.
valueAfter adding a scalar operation, the user sees:
The entered value is stored in:
operation = {
type: 'value',
operator: '+',
value: '...'
}
The backend uses this same scalar for every row.
metadata_valueAfter adding a metadata operation, the user sees:
ignore_missing_valuesThe selected metadata field is stored using fields such as metadata, value, and table. The backend uses value as the metadata key and table as the table reference.
If the metadata is missing or not numeric:
ignore_missing_values = true, the operation is skippedignore_missing_values = false, the calculation failsheader_valueAfter adding a header operation, the user sees:
ignore_missing_valuesThe entered fields are stored in something like:
operation = {
type: 'header_value',
operator: '+',
table: '0',
line: '...',
regex: '...',
ignore_missing_values: false
}
The backend uses _search_regex to search the selected table header. If the regex returns a numeric match, that value is used in the calculation.
The frontend also shows an operation description area when operations are configured.
The summary is stored in:
xOperationsDescriptionyOperationsDescriptionThis description is later included in the exported JCAMP comment.
From the user’s perspective:
From the backend’s perspective:
xColumn and yColumn.xOperations and yOperations.x and y.In short:
The frontend provides a user-friendly way to build a calculation chain for x- and y-values, while the backend executes that chain using one of four operation types and one of four mathematical operators.