Skip to content

Support scaling_factor and offset for axis#1944

Draft
loichuder wants to merge 3 commits intomainfrom
scaling-axes
Draft

Support scaling_factor and offset for axis#1944
loichuder wants to merge 3 commits intomainfrom
scaling-axes

Conversation

@loichuder
Copy link
Member

For #1943

POC for the support of scaling_factor and offset. According to the NXdata spec, these are a bit like errors: they are stored as datasets in the NXdata group with the name AXISNAME_scaling_factor and AXISNAME_offset.

So I had to work a bit around this design choice by allowing my getScalingInfo function to get data from valuesStore. Not sure if I should have made a hook out of it?

Also, scaling could have unintended side effects since the scaled value may have a different type than the value store in the dataset itself (ex: with a scaling factor of 0.1, integers get converted to floats). I also have to do a bit of dirty conversions in NxValuesFetcher to work around this.

Tell me what you think.

value.forEach((v) => {
scaledValue.push(scalingFactor * Number(v) + offset);
});
return scaledValue;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to use value.map but to no avail: I got errors saying that BigInt could not be converted to number.

I think it has to do with the savage conversions I am doing in this part but I am not versed in TS enough to do better 😓

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be best to skip bigint arrays, since it will not work anyway? I'll push something.


const offsetDataset = findOffset(group, datasetName);
const offset = offsetDataset
? Number(valuesStore.get({ dataset: offsetDataset }))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cast as Number could probably be replaced by an assert instead ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, a couple of assertions would be best I think.

@loichuder loichuder requested a review from axelboc January 15, 2026 13:13
Copy link
Contributor

@axelboc axelboc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the NXdata spec, these are a bit like errors: they are stored as datasets in the NXdata group with the name AXISNAME_scaling_factor and AXISNAME_offset.
So I had to work a bit around this design choice by allowing my getScalingInfo function to get data from valuesStore. Not sure if I should have made a hook out of it?

Argh NeXus!! 😂

The way we've dealt with this so far (e.g. with the title dataset) is to store the dataset objects in the NxData structure, and then fetch their values in NxValuesFetcher. I don't think there's a huge gain in doing so; the only ones I see are 1) separation of concerns and 2) value requests parallelisation in NxValuesFetcher (which would be more challenging to achieve in useNxData()).

I'll push a commit now for the bigint stuff, as mentioned in my first comment (since it requires moving a couple of type guards from somewhere else).

value.forEach((v) => {
scaledValue.push(scalingFactor * Number(v) + offset);
});
return scaledValue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be best to skip bigint arrays, since it will not work anyway? I'll push something.


const offsetDataset = findOffset(group, datasetName);
const offset = offsetDataset
? Number(valuesStore.get({ dataset: offsetDataset }))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, a couple of assertions would be best I think.

return dataset;
}

export function findScalingFactor(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findScalingFactorDataset and findOffsetDataset

Comment on lines +50 to +54
dataset && {
dataset,
...getDatasetInfo(dataset, attrValuesStore),
...getScalingInfo(group, dataset.name, valuesStore),
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe time to make a getAxisDef function instead of adding getScalingInfo?

};

export type AxisDef = DatasetDef<NumericType>;
export type AxisDef = DatasetDef<NumericType> & ScalingInfo;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

interface AxisDef extends DatasetDef<NumericType> {
  scalingFactor: number | undefined;
  offset: number | undefined;
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, makes sense if we replace getScalingInfo by getAxisDef that will return AxisDef

@axelboc
Copy link
Contributor

axelboc commented Jan 15, 2026

I also meant to say: good thinking putting the scaling computation in NxValuesFetcher. Definitely the simplest place for it 👍

return value;
}

return value.map((v) => scalingFactor * v + offset);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I'm just realising that the NeXus spec says this should be: 🤔

corrected values = (FIELDNAME + offset) * scaling_factor`

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oopsie-daisy ! Thanks for checking 😹

@loichuder loichuder marked this pull request as draft January 15, 2026 16:07
@NAThompson
Copy link

@loichuder : Only feedback from me is make sure to document it so claude/ChatGPT/Gemini realize this is a useful option!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants