Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/x/__tests__/xNoiseSanPlot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1055,4 +1055,13 @@ test('get noise level', () => {

//the SNR should be less because the biggest peak is not present.
expect(noiseWithoutBigPeaks.snr).toBeLessThan(noise.snr);

expect(Object.keys(noise.percentiles.positive)).toHaveLength(100);
expect(Object.keys(noise.percentiles.negative)).toHaveLength(100);
expect(noise.percentiles.positive[99]).toBeGreaterThan(
noise.percentiles.positive[98],
);
expect(noise.percentiles.negative[99]).toBeGreaterThan(
noise.percentiles.negative[98],
);
});
80 changes: 59 additions & 21 deletions src/x/xNoiseSanPlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export interface XNoiseSanPlotResult {
negative: number;
snr: number;
sanplot: Record<string, DataXY>;
percentiles: {
positive: Record<number, number>;
negative: Record<number, number>;
};
}

/**
Expand Down Expand Up @@ -154,6 +158,10 @@ export function xNoiseSanPlot(
negative: { from: firstNegativeValueIndex, to: input.length },
},
}),
percentiles: {
positive: getPercentiles(signPositive),
negative: getPercentiles(signNegative),
},
};
}

Expand Down Expand Up @@ -194,6 +202,7 @@ function calculateNoiseLevel(
const effectiveCutOffDist =
(cutOffDist * cloneSign.length + cutOffSignalsIndex) /
(cloneSign.length + cutOffSignalsIndex);

const refinedCorrectionFactor =
-1 * simpleNormInvValue(effectiveCutOffDist / 2);
noiseLevel /= refinedCorrectionFactor;
Expand All @@ -220,10 +229,6 @@ function calculateNoiseLevel(
* it will be biased and noisy. We therefore scan candidate quantile windows
* and pick the one where the sigma estimates are most self-consistent
* (lowest variance) - a proxy for "where the noise region reliably starts".
* <<<<<<< Updated upstream
*
* =======
* >>>>>>> Stashed changes
* @param signPositive - an array of positive numbers.
* @param options - optional parameters to configure the cut-off determination.
* @param options.magnitudeMode - if true, uses magnitude mode for normalization. Default is false.
Expand Down Expand Up @@ -255,10 +260,11 @@ function determineCutOff(
// For each quantile fraction, compute a local sigma estimate by inverting
// the theoretical relationship between order statistics and the assumed
// noise distribution (Gaussian or Rayleigh).
const { from, to, step } = considerList;
const sigmaEstimates: Array<[quantileFraction: number, sigma: number]> = [];
for (
let quantileFraction = 0.01;
quantileFraction <= 0.99;
let quantileFraction = from - step / 2;
quantileFraction <= to + step / 2;
quantileFraction += 0.01
) {
Comment on lines 265 to 269
const index = Math.round(indexMax * quantileFraction);
Expand All @@ -267,7 +273,6 @@ function determineCutOff(
sigmaEstimates.push([quantileFraction, Math.abs(sigma)]);
}

const { from, to, step } = considerList;
const halfWindow = step / 2;

let bestVariance = Number.MAX_SAFE_INTEGER;
Expand Down Expand Up @@ -375,19 +380,6 @@ function scale(
return { x: xAxis, y: array };
}

/**
* Prepares and processes the input data array based on the provided options.
* @param array - the input array of numbers to be processed.
* @param options - an object containing the following properties:
* - scaleFactor: A number by which to scale each element of the array.
* - mask: An optional array of the same length as the input array, where
* elements corresponding to `true` values will be excluded from processing.
* @param options.scaleFactor
* @param options.mask
* @param from
* @returns A new Float64Array containing the processed data, scaled by the
* scaleFactor and sorted in descending order.
*/
function createNegativeSign(array: Float64Array, from: number): Float64Array {
const length = array.length - from;
const result = new Float64Array(length);
Expand All @@ -397,9 +389,55 @@ function createNegativeSign(array: Float64Array, from: number): Float64Array {
return result;
}

/**
* Calculates intensity percentiles ranging from the 50th to the 99.5th percentile
* (in 0.5 increments) from a pre-sorted array.
* @param sorted - a pre-sorted array of numbers.
* @returns An object mapping percentile labels to their corresponding values.
*/
Comment on lines +392 to +397
function getPercentiles(sorted: NumberArray): Record<number, number> {
if (sorted.length === 0) return {};

const nbPercentiles = 100;
const result: Record<number, number> = {};
const maxIndex = sorted.length - 1;

for (let i = 0; i < nbPercentiles; i++) {
const pLabel = 50 + (i * 50) / nbPercentiles;
const percentile = pLabel / 100;
const index = maxIndex - Math.floor(percentile * maxIndex);

result[pLabel] = sorted[index];
}

return result;
}

interface PrepareDataOptions {
/**
* A multiplier by which to scale each element of the array.
* Note: Scaling is only applied if this value is strictly greater than 1.
*/
scaleFactor: number;

/**
* An optional array of the same length as the input array.
* Elements in the input array corresponding to truthy values in this mask
* will be excluded from the final processed output.
*/
mask?: NumberArray;
}

/**
* Prepares and processes the input data array based on the provided options.
* @param array - The input array of numbers to be processed.
* @param options - The configuration options for processing the data.
* @returns A new Float64Array containing the processed data, scaled by the
* scaleFactor and sorted in descending order.
*/
function prepareData(
array: NumberArray,
options: { scaleFactor: number; mask?: NumberArray },
options: PrepareDataOptions,
): Float64Array<ArrayBuffer> {
const { scaleFactor, mask } = options;

Expand Down
Loading