Skip to content

scale_by_exposure for signal stacks#254

Open
aidanc151 wants to merge 3 commits intoLumiSpy:mainfrom
aidanc151:scale-exposure-stack
Open

scale_by_exposure for signal stacks#254
aidanc151 wants to merge 3 commits intoLumiSpy:mainfrom
aidanc151:scale-exposure-stack

Conversation

@aidanc151
Copy link

@aidanc151 aidanc151 commented Feb 18, 2026

How stack metadata works + Current situation

Signals created with hs.stack can stack the metadata of each signal with the stack_metadata=True argument. However, when calling stack1.metadata the metadata called belongs to the first signal in the stack.

The existing scale_by_exposure function accesses stack1.metadata, therefore scaling the whole stack by the integration time of the first signal in the stack.

To access the metadata of each individual signal in the stack stack1.original_metadata can be called giving:

- original_metadata
  - stack_elements
    - element0
      - metadata
      - original_metadata
    - element1
       - metadata
       - original_metadata

The function proposed goes into the metadata of each element (as seen above) and scales for each signal's individual integration time. This is useful for handling any stack of signals where the integration time differs between the stacked signals.

Description of the change

  • Condition is added to first lines of scale_by_exposure - determining whether signal has metadata for stack elements
  • *If condition is fulfilled, the new private function _scale_by_exposure_stack is called which performs same operations as existing scale_by_exposure but loops through navigational axis of stack for each signal contained

*I didn't see a neat way for this functionality to fit into the existing scale_by_exposure without repeating chunks of code/making sub function

Progress of the PR

  • Change implemented (can be split into several points),
    • Wrote _scale_by_exposure_stack
    • Adjust _scale_by_exposure_stack for handling stacks with of signals with navigational axes (looping through stack inav fails for this case currently)
  • added tests,
  • add a changelog entry in the upcoming_changes folder (see upcoming_changes/README.rst),
  • Check formatting of the changelog entry (and eventual user guide changes) in the docs/readthedocs.org:lumispy build of this PR (link in github checks),
  • ready for review.

Minimal example of the bug fix or the new feature

import lumispy as lum
import hyperspy.api as hs

s1 = lum.data.asymmetric_peak_map().inav[1,1]
s2 = lum.data.asymmetric_peak_map().inav[15,15]

s2.metadata.Acquisition_instrument.Detector.integration_time = 3

stack1 = hs.stack([s1,s2], stack_metadata = True)

stack1_scaled = stack1.scale_by_exposure()

hs.plot.plot_spectra(stack1, legend = ['s1 (1s)','s2 (3s)'])
hs.plot.plot_spectra(stack1_scaled, legend = ['s1 (1s)','s2 (3s)'])
output1 output2

Added a private method to scale signal stacks by exposure time, with validation checks for each element. Updated metadata accordingly to reflect scaling.
@aidanc151 aidanc151 marked this pull request as ready for review February 18, 2026 18:16
@jlaehne
Copy link
Contributor

jlaehne commented Feb 18, 2026

To avoid duplicating similar code it could make sense to have a private function for scaling a single element. For a normal signal it would be called from the main function with a parameter telling it to access the metadata. For a stack it would be called for each iteration through the stack and given the element of the original_metadata to be accessed.

For a stack, it would also make sense if integration_time could optionally be a list of times.

To reduce the repetition of code, I've split the functionality into two private functions, one which handles the scaling of each signal or stack element, and one which updates this to the metadata.

The scale_by_exposure wrapper is now for primarily looping these private functions when appropriate.
@aidanc151
Copy link
Author

This commit also handles stacks of signals which have navigational axes as well, (e.g. stacks of hyperspectral maps) by locating the stack_element axis name in the axes_manager and then looping through this - rather than looping the 'X' or 'Y' navigational axis.

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.

2 participants