getting the index of min values with Numpy Python - arrays

The function below separates each value into chunks separated by indexes index with the values in L_list. So it outputs the minimum value between indexes 3-5 which is -5 and the index of the value. Both the numpy_argmin_reduceat(a, b) and the Drawdown function do as planned however the index output of the numpy_argmin_reduceat(a, b) is faulty it The minimum values of Drawdown do not match with the indexes of the outputs of numpy_argmin_reduceat(a, b).How would I be able to solve this?
Arrays:
import numpy as np
# indexes 0, 1, 2,3,4, 5, 6,7, 8, 9,10, 11, 12
L_list = np.array([10,20,30,0,0,-5,11,2,33, 4, 5, 68, 7])
index = np.array([3,5,7,11])
Functions:
#getting the minimum values
Drawdown = np.minimum.reduceat(L_list,index+1)
#Getting the min Index
def numpy_argmin_reduceat(a, b):
n = a.max() + 1 # limit-offset
id_arr = np.zeros(a.size,dtype=int)
id_arr[b] = 1
shift = n*id_arr.cumsum()
sortidx = (a+shift).argsort()
grp_shifted_argmin = b
idx =sortidx[grp_shifted_argmin] - b
min_idx = idx +index
return min_idx
min_idx =numpy_argmin_reduceat(L_list,index+1)
#printing function
DR_val_index = np.array([np.around(Drawdown,1), min_idx])
DR_result = np.apply_along_axis(lambda x: print(f'Min Values: {x[0]} at index: {x[1]}'), 0, DR_val_index)
Output
Min Values: -5 at index: 4
Min Values: 2 at index: 6
Min Values: 4 at index: 8
Min Values: 7 at index: 11
Expected Output:
Min Values: -5 at index: 5
Min Values: 2 at index: 7
Min Values: 4 at index: 9
Min Values: 7 at index: 12

If you change the line
id_arr[b[1:]] = 1
to
id_arr[b] = 1
I think the function will behave as you hope.

Related

Extract indices of sets of values greater than zero in an array

I have an array of length n. The array has braking energy values, and the index number represents time in seconds.
The structure of array is as follows:
Index 1 to 140, array has zero values. (Vehicle not braking)
Index 141 to 200, array has random energy values. (Vehicle was braking and regenerating energy)
Index 201 to 325, array has zero values. (Vehicle not braking)
Index 326 to 405, array has random energy values. (Vehicle was braking and regenerating energy)
...and so on for an array of length n.
What I want to do is to get starting and ending index number of each set of energy values.
For example the above sequence gives this result:
141 - 200
326 - 405
...
Can someone please suggest what method or technique can I use to get this result?
Using diff is a quick way to do this.
Here is a demo (see the comments for details):
% Junk data for demo. Indices shown above for reference
% 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
x = [0, 0, 0, 2, 3, 4, 0, 0, 1, 1, 7, 9, 3, 4, 0, 0, 0];
% Logical converts all non-zero values to 1
% diff is x(2:end)-x(1:end-1), so picks up on changes to/from zeros
% Instead of 'logical', you could have a condition here,
% e.g. bChange = diff( x > 0.5 );
bChange = diff( logical( x ) );
% bChange is one of the following for each consecutive pair:
% 1 for [0 1] pairs
% 0 for [0 0] or [1 1] pairs
% -1 for [1 0] pairs
% We inflate startIdx by 1 to index the non-zero value
startIdx = find( bChange > 0 ) + 1; % Indices of [0 1] pairs
endIdx = find( bChange < 0 ); % Indices of [1 0] pairs
I'll leave it as an exercise to capture the edge cases where you add a start or end index if the array starts or ends with a non-zero value. Hint: you could handle each case separately or pad the initial x with additional end values.
Output of the above:
startIdx
>> [4, 9]
endIdx
>> [6, 14]
So you can format this however you like to get the spans 4-6, 9-14.
This task is performed by two methods Both works perfectly.
Wolfie Method:
bChange = diff( EnergyB > 0 );
startIdx = find( bChange > 0 ) + 1; % Indices of [0 1] pairs
endIdx = find( bChange < 0 ); % Indices of [1 0] pairs
Result:
startIdx =
141
370
608
843
endIdx =
212
426
642
912
Second Method:
startends = find(diff([0; EnergyB > 0; 0]));
startends = reshape(startends, 2, [])';
startends(:, 2) = startends(:, 2) - 1
Result:
startends =
141 212
370 426
608 642
843 912

MATLAB store indices maximum in logical matrix

Lets say I have a 4 dimensional matrix, from which I would like to retrieve the maximum values over the 2nd and 3rd dimension.
A = rand(4, 4, 4, 4);
[max_2, in_2] = max(A, [], 2);
[max_3, in_3] = max(max_2, [], 3);
How could I use ind_2 and ind_3 to obtain a logical 4 dimensional matrix, where a 1 entry means this entry is maximum in the 2nd and 3rd dimension?
I would use this approach:
A = rand(4, 4, 4, 4); % example data
B = permute(A, [1 4 2 3]); % permute dims 2 and 3 to the end
B = reshape(B, size(A,1), size(A,4), []); % collapse last two dims
C = bsxfun(#eq, B, max(B, [], 3)); % maximize over collapsed last dim
C = reshape(C, size(A,1), size(A,4), size(A,2), size(A,3)); % expand dims back
C = permute(C, [1 3 4 2]); % permute dims back. This is the final result
Here's an approach working with linear indices and uses argmax indices from max function, so it would only consider the first argmax in case of ties for the max value -
% Get size parameters
[m,n,p,q] = size(A);
% Reshape to merge second and third dims
[~, in_23] = max(reshape(A,m,[],q), [], 2);
% Get linear indices equivalent that could be mapped onto output array
idx1 = reshape(in_23,m,q);
idx2 = bsxfun(#plus,(1:m)', m*n*p*(0:q-1)) + (idx1-1)*m;
% Initialize output array an assign 1s at linear indices from idx2
out = false(m,n,p,q);
out(idx2) = 1;
Explanation with a sample
1) Input array :
>> A
A(:,:,1,1) =
9 8
9 1
A(:,:,2,1) =
2 9
8 1
A(:,:,1,2) =
1 7
8 1
A(:,:,2,2) =
8 5
9 7
2) Reshape array for a better visualization :
>> reshape(A,m,[],q)
ans(:,:,1) =
9 8 2 9
9 1 8 1
ans(:,:,2) =
1 7 8 5
8 1 9 7
3) The question is to take max value from each of the rows. For that, we had idx2 as the linear indices :
>> idx2
idx2 =
1 13
2 14
Looking back at the reshape version, thus we chose (bracketed ones) -
>> reshape(A,m,[],q)
ans(:,:,1) =
[9] 8 2 9
[9] 1 8 1
ans(:,:,2) =
1 7 [8] 5
8 1 [9] 7
So, looking closely, we see that for the first row, we had two 9s, but we are choosing the first one only.
4) Finally, we are assigning these into the output array initialized as logical zeros :
>> out
out(:,:,1,1) =
1 0
1 0
out(:,:,2,1) =
0 0
0 0
out(:,:,1,2) =
0 0
0 0
out(:,:,2,2) =
1 0
1 0

Find the sum of the max values from all subarrays

The question is - to find the sum of all maximum values from all subarrays. For instance, I have the array {2, 8, 4, 3, 5}, the solution will be 92. Where all of my subarrays are:
{2},{8},{4},{3},{5},
{2,8},{8,4},{4,3},{3,5},
{2,8,4},{8,4,3},{4,3,5},
{2,8,4,3},{8,4,3,5},
{2,8,4,3,5}
And all maximum values from all subarrays are:
2 - 8 - 4 - 3 - 5 -
8 - 8 - 4 - 5 -
8 - 8 - 5 -
8 - 8 -
8
Do you know the way to solve this problem in linear time complexity?
It should be very straightforward. See the following algorithm
sum = 0
max = 0
for every array 'arr' in the array of arrays
do
for every element 'arri' in the array 'arr'
do
if arri >= max, max = arri
end for
max = 0
sum = sum + max
end for

Find unique elements of multiple arrays

Let say I have 3 MATs
X = [ 1 3 9 10 ];
Y = [ 1 9 11 20];
Z = [ 1 3 9 11 ];
Now I would like to find the values that appear only once, and to what array they belong to
I generalized EBH's answer to cover flexible number of arrays, arrays with different sizes and multidimensional arrays. This method also can only deal with integer-valued arrays:
function [uniq, id] = uniQ(varargin)
combo = [];
idx = [];
for ii = 1:nargin
combo = [combo; varargin{ii}(:)]; % merge the arrays
idx = [idx; ii*ones(numel(varargin{ii}), 1)];
end
counts = histcounts(combo, min(combo):max(combo)+1);
ids = find(counts == 1); % finding index of unique elements in combo
uniq = min(combo) - 1 + ids(:); % constructing array of unique elements in 'counts'
id = zeros(size(uniq));
for ii = 1:numel(uniq)
ids = find(combo == uniq(ii), 1); % finding index of unique elements in 'combo'
id(ii) = idx(ids); % assigning the corresponding index
end
And this is how it works:
[uniq, id] = uniQ([9, 4], 15, randi(12,3,3), magic(3))
uniq =
1
7
11
12
15
id =
4
4
3
3
2
If you are only dealing with integers and your vectors are equally sized (all with the same number of elements), you can use histcounts for a quick search for unique elements:
X = [1 -3 9 10];
Y = [1 9 11 20];
Z = [1 3 9 11];
XYZ = [X(:) Y(:) Z(:)]; % one matrix with all vectors as columns
counts = histcounts(XYZ,min(XYZ(:)):max(XYZ(:))+1);
R = min(XYZ(:)):max(XYZ(:)); % range of the data
unkelem = R(counts==1);
and then locate them using a loop with find:
pos = zeros(size(unkelem));
counter = 1;
for k = unkelem
[~,pos(counter)] = find(XYZ==k);
counter = counter+1;
end
result = [unkelem;pos]
and you get:
result =
-3 3 10 20
1 3 1 2
so -3 3 10 20 are unique, and they appear at the 1 3 1 2 vectors, respectively.

Generate an array with specific duplicate elements in MATLAB

I have one array, for example B = [2,5,7], and also have a number C = 10, where C is always larger than or equal to the largest number in B.
and I want to generate an array A according to B and C. In this specific example, I have
A = [1, 2, 2, 2, 3, 4, 5, 5, 5, 6, 7, 7, 7, 8, 9, 10]
that is, I generate an array [1:C], but with each element in B are duplicated 3 times. Is there any good way that does not use for loop to generate array A?
Thank you!
You can use repelem (introduced in Matlab R2015a):
B = [2 5 7]
C = 10;
n = 3;
r = ones(1,C);
r(B) = n;
A = repelem(1:C, r)
How about...
B = [2,5,7];
C = 10;
A = sort([1:C,B,B])
I think the answer of #RPM could be faster. But because you specifically asked for a solution without sort:
B = [2,5,7];
C = 10;
D = setdiff(1:C,B)-1;
A = reshape(repmat(1:C,3,1),1,3*C);
A([3*D+1,3*D+2]) = [];
which will also return the correct result. I'm not to sure about the order setdiff() has. It might be worse than sort() in all cases. Especially with
A = sort([1:C,B,B]) as the input is already almost in order.
Following the same philosophy as this solution to Repeat copies of array elements: Run-length decoding in MATLAB, you can do something similar here, like this -
%// Get increment array (increments used after each index being repeated in B)
inc = zeros(1,C);
inc(B+1) = N-1
%// Calculate ID array (places in output array where shifts occur)
id = zeros(1,C+(N-1)*numel(B));
id(cumsum(inc) + (1:C)) = 1
%// Finally get cumulative summation for final output
A = cumsum(id)
Sample run -
B =
2 5 7
C =
10
N =
3
inc =
0 0 2 0 0 2 0 2 0 0
id =
1 1 0 0 1 1 1 0 0 1 1 0 0 1 1 1
A =
1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10

Resources