Fastest way to compute min and max values over an array subset
19 views (last 30 days)
Show older comments
I'm trying to compute the minimum and maximum values over subsets of an array. My current approach is fairly simple but is noticeably slow for the size of data that I am working with.
e.g.
data = rand(1,100);
starts = [5 10 15 20];
stops = [9 14 19 24];
mins = zeros(1,4);
maxes = zeros(1,4);
for i = 1:4
data_subset = data(starts(i):stops(i));
mins(i) = min(data_subset);
maxes(i) = max(data_subset);
end
Note that in reality data is an array that has millions of elements and I am grabbing a couple thousand subsets. The slow part seems to be creating the data_subset. I tried creating a mex function to avoid memory allocation but the whole thing was a wash, largely due to the multi-threaded nature of min and max in Matlab. I tried calling min and max from mex, but this requires creating an mxArray, which from what I can tell, requires actual data copying, not just pointer adjustment.
Any suggestions or thoughts on something I might have missed?
Thanks, Jim
4 Comments
Jan
on 1 Jan 2015
Edited: Jan
on 1 Jan 2015
I'm going to publish a hand coded MEX version in the FEX. What is the desired result, if a NaN appears in a chunk? Should it be ignored or should NaN be replied for the min and max values?
The mex is single threaded, but 10 times faster than the posted Matlab code for a chunk length of 1000, and 30 times faster for 10 elements per chunk. I assume calling it from a PARFOR loop will squeeze out even more speed.
Accepted Answer
Jan
on 2 Jan 2015
See FEX: ChunkMinMax. An automatic multi-threading is not trivial, because starting a thread for each interval might waste a lot of time, when the intervals are tiny. You can try to split the list of intervals in a PARFOR loop, but avoid splitting the job for each interval, because this will lead to collisions of the cache line. So better let the 1nd half and the 2nd of the intervals be processed in different threads.
2 Comments
Jan
on 2 Jan 2015
You are welcome! Min and Max of Matlab are multi-threaded, when the data size exceeds a certain limit. For e.g. SUM the limit is 89000 elements.
More Answers (4)
Matt J
on 1 Jan 2015
Edited: Matt J
on 1 Jan 2015
I want to give credit to Jan's comment which inspired this answer. However, it assumes that the subsets are all relatively small (i.e., they can all be held in RAM simultaneously when NaN-padded to the length of the largest subset).
function [mins,maxes]=doProcess(data,starts,stops)
starts=starts(:).'; stops=stops(:).'; %ensure row vectors
len=stops-starts;
maxlen=max(len);
data(end+maxlen)=0;
idx=bsxfun(@plus,starts,(0:maxlen).');
nanmap=bsxfun(@gt,idx,stops);
data_subsets=data(idx);
data_subsets(nanmap)=nan;
mins=min(data_subsets);
maxes=max(data_subsets);
3 Comments
Matt J
on 1 Jan 2015
Edited: Matt J
on 1 Jan 2015
This however could, depending on the array size, require a pretty significant duplication of the data.
Not from the reshaping. Reshaping doesn't duplicate any data. Also, it doesn't sound like your data is that big if it only has "millions of elements". So why care about duplication anyway?
Are your subsets always intervals connected end-to-end? And are they always the same length but for the final interval (due to divisibility issues)? If so, just pad "data" with NaNs so that all intervals are of equal length. Then, reshape and be done with it!
Matt J
on 31 Dec 2014
Edited: Matt J
on 31 Dec 2014
Are all your subsets contiguous and of the same size, like in your example? If so, and if you have the Image Processing Toolbox, the imdilate(X) command will perform a local max filter over all subsets of X a fixed size (or the local min filtering of -X). You can then just grab the results of the particular subsets you want from the filter result.
If the size of the subsets varies (but not too much), you could try grouping together all subsets of a common size, and do as above in a loop over the different subset sizes.
0 Comments
Nicolás Casaballe
on 26 May 2016
Depending on how many subsets you are going to use and the size of the data, I think it maybe worthy to spend some resources sorting the data just once, and using the sorted data to find the extrema of the subsets. The resulting code would look similar to this (not verified):
data = rand(1,100);
starts = [5 10 15 20];
stops = [9 14 19 24];
mins = zeros(1,4);
maxes = zeros(1,4);
[B,I] = sort(data); % This call would be lengthy for large datasets, but runs just once
for i = 1:4
range = starts(i):stops(i);
ind_sort = I(range); % indices of the range in the sorted data B
% Since B is already sorted, we avoid calling min and max for each subset
mins(i) = B(ind_sort(1));
maxes(i) = B(ind_sort(end));
end
I haven't tried this yet (sorry) but even if the codes needs some fixing, the key idea is to run through the data values just once, instead of calling min and max each time.
0 Comments
See Also
Categories
Find more on Creating and Concatenating Matrices in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!