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
44 changes: 44 additions & 0 deletions +datop/apply_func_along_dimension.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
function varargout = apply_func_along_dimension(arr, iDim, func, varargin)
dim_size = size(arr);
num_dims = ndims(arr);

% --- Input Validation ---
if iDim > num_dims || iDim <= 0 || ~isnumeric(iDim) || fix(iDim) ~= iDim
error('iDim must be a positive integer less than or equal to the number of dimensions of arr.');
end
if ~isa(func, 'function_handle')
error('func must be a function handle.');
end

% --- Determine Output Size (and preallocate) ---
% Apply the function to the *first* slice to determine the output size.
first_slice_indices = repmat({1}, 1, num_dims);
first_slice_indices{iDim} = ':';
first_slice = squeeze(arr(first_slice_indices{:}));
outputN = cell([1,nargout]);
[outputN{:}] = func(first_slice, varargin{:});
output_size_iDim = cellfun(@(x) numel(x), outputN);
% Calculate the size of the output array
op_size = repmat(dim_size,nargout,1);
op_size(:,iDim) = output_size_iDim;
% Preallocate the output array.
varargout = arrayfun(@(i) zeros(op_size(i,:), 'like', outputN{i}), 1:nargout, 'UniformOutput',false);

% --- Prepare for Looping ---
% Create a cell array to index all dimensions *except* iDim.
slice_idx = arrayfun(@(x) 1:x, dim_size, 'UniformOutput',false);
slice_idx{iDim} = ':';
slice_idx = table2cell(combinations(slice_idx{:}));

for iSlice = 1:size(slice_idx,1)

[outputN{:}] = func(squeeze(arr(slice_idx{iSlice,:})), varargin{:});

for iOp = 1:nargout

varargout{iOp}(slice_idx{iSlice,:}) = outputN{iOp};

end
end

end
6 changes: 6 additions & 0 deletions +datop/empty_like.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
function emp = empty_like(x)
% Creates an empty instance of the class of x

emp = feval(sprintf("%s.empty",class(x)));

end
25 changes: 25 additions & 0 deletions +datop/extract_numbers_from_string.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
function varargout = extract_numbers_from_string(x)
% Extracts all sequences of digits from a string.
% numericVars = extract_numbers_from_string(x) finds all sequences of one or more
% digits in the input string x and returns them as a numeric array.
%
% [numericVars, isNumeric] = extract_numbers_from_string(x) also returns a logical
% array 'isNumeric' the same size as x, where true indicates that the
% corresponding character in x is a digit.
%
% Args:
% x: The input string.
%
% Returns:
% numericVars: A numeric array containing the numbers found in the string.
% Returns an empty array if no numbers are found.
% isNumeric: (Optional) A logical array indicating the position of digits.

varargout = cell(1, nargout);

varargout{1} = str2double(regexp(x, '\d+', 'match'));

if nargout > 1
varargout{2} = isstrprop(x,'digit');
end
end
66 changes: 66 additions & 0 deletions +datop/hstack_struct.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
function s = force_mergestruct(varargin)

filler = missing;
isMerge = cellfun(@(x) isstruct(x), varargin);

idx1_opts = find(~isMerge, 1, 'first');

if ~isempty(idx1_opts)
opts = varargin(idx1_opts:end);

ii = 1;
while ii <= length(opts)

optN = opts{ii};
ii = ii + 1;
switch optN

case {'fill_with'}

filler = opts{ii};
ii = ii + 1;
end

end


s = varargin(1:idx1_opts-1);
else

s = varargin;
end


try

s = [s{:}];

catch ME

switch ME.identifier

case 'MATLAB:catenate:structFieldBad'



fld_names = cellfun(@(x) string(fieldnames(x))', s, 'UniformOutput', false);
fld_names = unique([fld_names{:}]);

for ii = 1:length(s)

nan_fields = setdiff(fld_names, fieldnames(s{ii}));
for fldN = nan_fields

s{ii}.(fldN) = filler;

end

end

s = [s{:}];
otherwise
rethrow(ME);
end


end
5 changes: 5 additions & 0 deletions +datop/make_column.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function vec = make_column(vec)

if isrow(vec), vec = vec'; end

end
5 changes: 5 additions & 0 deletions +datop/make_row.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function vec = make_row(vec)

if iscolumn(vec), vec = vec'; end

end
22 changes: 22 additions & 0 deletions +datop/struct_to_varargin.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
function op = struct_to_varargin(s)

assert(isstruct(s) && isscalar(s), "Input must be a struct array of length 1.")

op = arrayfun(@(f_name, f_val) {f_name(:), handle_empty_val(f_val{:})}, string(fieldnames(s)), struct2cell(s), 'UniformOutput', false);

op = horzcat(op{:})';
end

function op = handle_empty_val(f_val)

if isempty(f_val)

op = gen.empty_like(f_val);

else

op = f_val;

end

end
10 changes: 10 additions & 0 deletions +num/absargmin.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function i = absargmin(varargin)
if nargin == 1
% varargin = {varargin{1},1,varargin{2:end}};
% else
varargin{2} = 1;
end
% [~, i] = min(abs(varargin{1}),varargin{2:end});
[~, i] = mink(abs(varargin{1}),varargin{2:end});

end
12 changes: 12 additions & 0 deletions +num/find_closest_integer_divisor_pair.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function pairN = get_closest_integer_dividers(num)

assert(isscalar(num) & isnumeric(num) & rem(num,1)==0, "num must be an integer!");

pairs = arrayfun(@(x) [x, num/x],1:sqrt(num),'UniformOutput',false);
pairs = vertcat(pairs{:});
pairs = sort(pairs,2);
isInt = ~any(rem(pairs,1),2);
pairs = pairs(isInt,:);
pairN = pairs(gen.absargmin(diff(pairs,[],2)),:);

end
4 changes: 4 additions & 0 deletions +num/ifbetween.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function i = ifbetween(a,vec)
i = a> vec(1) & a < vec(2);
end

5 changes: 5 additions & 0 deletions +num/ifinrange.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function i = ifinrange(a, vec)

i = a>=vec(1) & a <= vec(2);

end
5 changes: 5 additions & 0 deletions +num/ifwithin.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function i = ifwithin(varargin)

i = gen.ifinrange(varargin{:});

end
15 changes: 15 additions & 0 deletions +stats/range.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function r = range(arr, dim)

if nargin == 1, dim = 'all'; end

if isnumeric(dim)
r = cat(dim,min(arr,[],dim), max(arr,[],dim));
elseif strcmp(dim,"all")

r = [min(arr,[],dim), max(arr,[],dim)];

else
error("dimension needs to be a numeric value or 'all'.");
end

end
63 changes: 63 additions & 0 deletions +stats/robust_z.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
function varargout = robust_z(arr, dim, var_method, varargin)

%{
Robust Z-score:
Calculated using the median and median absolute deviation (MAD).  
Formula: RobustZ= (x−median)/MAD
The median and MAD are less affected by outliers, making the robust Z-score more reliable when dealing with data that may contain extreme values.  

Median: The middle value in a sorted dataset.  
Median Absolute Deviation (MAD): The median of the absolute differences between each data point and the median.

%}


if nargin == 1 || isempty(dim)

dim = 'all';

end


m = median(arr, dim, 'omitnan');

if nargin < 3

var_method = 'mad';

end


switch var_method

case 'mad'

var = median(abs(arr - m), dim, 'omitnan');

case 'iqr'

var = iqr(arr, dim);

case 'std'

if isempty(varargin)
scalar = 1/1.349; % iqr calculated from normal standard deviation
else
scalar = varargin{1};
end

var = scalar * iqr(arr, dim);

otherwise
error("Unknown method to compute variance. Pick one of the following: 'mad', 'robust_std', 'iqr'")
end

varargout = cell(1, nargout);
varargout{1} = (arr - m) ./ var;

if nargout > 1, varargout{2} = m; end
if nargout > 2, varargout{3} = var; end

end