145 lines
5.7 KiB
Matlab
145 lines
5.7 KiB
Matlab
function DataTable = nappCopyShift(DataTable, processSelection, refSelection, options)
|
|
% NAPPCOPYSHIFT Copy binding energy shift from reference spectra to target spectra.
|
|
%
|
|
% DataTable = NAPPCOPYSHIFT(DataTable, processSelection, refSelection)
|
|
% copies the calculated binding energy shift from a pool of reference spectra
|
|
% (where refSelection is true) to the target spectra (where processSelection
|
|
% is true) using a matching strategy.
|
|
%
|
|
% NOTE: This function requires that nappDetermineShift has already been
|
|
% run on the reference spectra to populate the 'bindingEnergyShift' column.
|
|
%
|
|
% Name-Value arguments:
|
|
% matchStrategy - 'earlier' (default), 'after', 'spectrumNumber'
|
|
% timeVariable - Target time column ('lastModifiedDatenum' by default)
|
|
% spectrumNumberPrefixLength - Number of leading characters used for name
|
|
% matching in 'spectrumNumber' strategy (default 7)
|
|
|
|
|
|
arguments
|
|
DataTable table
|
|
processSelection logical {mustBeVector}
|
|
refSelection logical {mustBeVector}
|
|
|
|
% Name-Value arguments
|
|
options.matchStrategy char {mustBeMember(options.matchStrategy, ...
|
|
{'earlier', 'after', 'spectrumNumber'})} = 'earlier'
|
|
options.timeVariable char {mustBeMember(options.timeVariable, ...
|
|
{'lastModifiedDatenum', 'creationDate'})} = 'lastModifiedDatenum'
|
|
options.spectrumNumberPrefixLength double {mustBePositive, mustBeInteger} = 7
|
|
end
|
|
|
|
numRowsTable = height(DataTable);
|
|
|
|
% Validate length of the logical arrays
|
|
if length(processSelection) ~= numRowsTable || length(refSelection) ~= numRowsTable
|
|
error('Logical selection arrays must have the exact same length as the DataTable height (%d).', numRowsTable);
|
|
end
|
|
|
|
% Ensure shift data exists
|
|
if ~ismember('bindingEnergyShift', DataTable.Properties.VariableNames)
|
|
error('The column "bindingEnergyShift" does not exist. Please run nappDetermineShift on your reference spectra first.');
|
|
end
|
|
|
|
% Initialize source logging column if it doesn't exist
|
|
if ~ismember('lognappCopyShiftSource', DataTable.Properties.VariableNames)
|
|
DataTable.lognappCopyShiftSource = cell(numRowsTable, 1);
|
|
end
|
|
|
|
%% Processing Loop
|
|
processIdx = find(processSelection);
|
|
refIdx = find(refSelection);
|
|
|
|
for i = 1:length(processIdx)
|
|
currRow = processIdx(i);
|
|
currName = DataTable.name{currRow};
|
|
|
|
% Safely extract time (handles both single numbers and 1x6 datevecs)
|
|
currTime = datenum(DataTable.(options.timeVariable)(currRow, :));
|
|
refTimes = datenum(DataTable.(options.timeVariable)(refIdx, :));
|
|
|
|
bestRefRow = NaN;
|
|
|
|
% Internal Matching Engine
|
|
switch options.matchStrategy
|
|
case 'earlier'
|
|
validRefs = refIdx(refTimes <= currTime);
|
|
if ~isempty(validRefs)
|
|
timeDiffs = currTime - refTimes(refTimes <= currTime);
|
|
[~, minIdx] = min(timeDiffs);
|
|
bestRefRow = validRefs(minIdx);
|
|
end
|
|
|
|
case 'after'
|
|
validRefs = refIdx(refTimes >= currTime);
|
|
if ~isempty(validRefs)
|
|
timeDiffs = refTimes(refTimes >= currTime) - currTime;
|
|
[~, minIdx] = min(timeDiffs);
|
|
bestRefRow = validRefs(minIdx);
|
|
end
|
|
|
|
case 'spectrumNumber'
|
|
% Use the first N characters of the name as the matching prefix
|
|
prefixLen = options.spectrumNumberPrefixLength;
|
|
if length(currName) >= prefixLen
|
|
searchPattern = currName(1:prefixLen);
|
|
validRefs = refIdx(startsWith(DataTable.name(refIdx), searchPattern));
|
|
if ~isempty(validRefs)
|
|
bestRefRow = validRefs(1);
|
|
end
|
|
else
|
|
warning('Row %d: Name "%s" is shorter than the requested prefix length (%d). Skipping.', ...
|
|
currRow, currName, prefixLen);
|
|
end
|
|
end
|
|
|
|
% Validate we found a match
|
|
if isnan(bestRefRow)
|
|
warning('Row %d (%s): No matching reference found for strategy "%s". Skipping.', ...
|
|
currRow, currName, options.matchStrategy);
|
|
continue;
|
|
end
|
|
|
|
% Extract shift from reference row
|
|
referenceShift = DataTable.bindingEnergyShift(bestRefRow);
|
|
|
|
if isnan(referenceShift)
|
|
warning('Row %d: Matched reference row %d (%s) does not have a valid calculated shift. Skipping.', ...
|
|
currRow, bestRefRow, DataTable.name{bestRefRow});
|
|
continue;
|
|
end
|
|
|
|
% Copy the shift and log the exact name of the reference used
|
|
DataTable.bindingEnergyShift(currRow) = referenceShift;
|
|
DataTable.lognappCopyShiftSource{currRow} = DataTable.name{bestRefRow};
|
|
% 1. Dynamically get this function's name
|
|
thisFuncName = mfilename;
|
|
colDate = sprintf('log%sDate', thisFuncName);
|
|
colCaller = sprintf('log%sCaller', thisFuncName);
|
|
|
|
|
|
% 2. Probe for column existence, create empty string arrays if missing
|
|
if ~ismember(colDate, DataTable.Properties.VariableNames)
|
|
DataTable.(colDate) = repmat("", numRowsTable, 1);
|
|
end
|
|
if ~ismember(colCaller, DataTable.Properties.VariableNames)
|
|
DataTable.(colCaller) = repmat("", numRowsTable, 1);
|
|
end
|
|
|
|
% 3. Get current time as a string
|
|
currentTimeStr = string(datetime('now', 'Format', 'yyyy-MM-dd HH:mm:ss'));
|
|
|
|
% 4. Determine the calling script using the call stack
|
|
stackInfo = dbstack;
|
|
if length(stackInfo) > 1
|
|
callerScript = string(stackInfo(2).file); % The script that called this function
|
|
else
|
|
callerScript = " "; % Called directly from the command window
|
|
end
|
|
|
|
% 5. Write the log data only to the rows being processed
|
|
DataTable.(colDate)(processIdx) = currentTimeStr;
|
|
DataTable.(colCaller)(processIdx) = callerScript;
|
|
% -------------------------------------------------------------------------
|
|
end
|
|
end |