How can I index multiple array segments at once without a loop? - matlab

I want to change the value in column 2 based on the values in column 1 in one array (main), using a start and end index from another array (conditions).
In conditions column 1 holds the start index, column 2 the end index.
main = zeros(8, 2);
main(:, 1) = 1:8;
conditions = [2, 3; 6, 8]
main =
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
conditions =
2 3
6 8
I know how to do it using a loop (shown below), but am looking for a faster method.
for ii = 1:size(conditions, 1)
main(main(:, 1) >= conditions(ii, 1) & main(:, 1) <= conditions(ii, 2), 2) = 1;
end
main =
1 0
2 1
3 1
4 0
5 0
6 1
7 1
8 1
Doing main(main(:, 1) >= conditions(:, 1) & main(:, 1) <= conditions(:, 2), 2) = 1 results in the error Matrix dimensions must agree.
Is there a non-loop method?

Your attempt is almost correct. If you transpose the conditions, then you'll be comparing a column of main with a row of conditions, leading to MATLAB doing implicit singleton expansion, giving a matrix output. This matrix can then be collapsed using any.
main = zeros(8, 2);
main(:, 1) = 1:8;
conditions = [2, 3; 6, 8];
index = (main(:,1) >= conditions(:, 1).') & (main(:, 1) <= conditions(:, 2).');
index = any(index,2);
main(index,2) = 1;
(I've separated out the code into 3 lines for clarity, but of course they can all be a single line.)
Note that for versions of MATLAB prior to R2016b, this code won't work, you'll need to use bsxfun instead:
index = bsxfun(#ge,main(:,1),conditions(:, 1).') & bsxfun(#le,main(:, 1),conditions(:, 2).');

NOTE: This is a solution for integers only as the original question only presented the integer case.
First, figure out how many elements there are included in the interval
dCon = diff(conditions,[],2)+1;
Then construct an increasing sequence of indexes to the maximum number of elements (this list would be enormous for the float case, and thus this solution does not, feasibly/efficiently, extend to floats)
idx0 = repmat(1:max(dCon),length(dCon),1);
Cap off the indexes that are too large
idx0(idx0>dCon)=1;
Now add the starting point
idx = idx0 + conditions(:,1)-1;
now idx contains all the numbers you want to change. Use ismember to find all elements in main and change them to 1.
main(ismember(main(:,1),idx(:)),2)=1;
EDIT: This is the full example with the vector from Gnovice in the comments
main = zeros(10, 2);
main(:, 1) = [1; 2; 2; 2; 3; 3; 4; 6; 6; 8];
conditions = [2, 3; 6, 8]
dCon = diff(conditions,[],2)+1;
idx0 = repmat(1:max(dCon),length(dCon),1);
idx0(idx0>dCon)=1;
idx = idx0 + conditions(:,1)-1;
main(ismember(main(:,1),idx(:)),2)=1;

Related

Multiply matrix columns with decreasing elements starting by the last column

I have a matrix with following shape:
A = [1 2 3;
4 5 6;
7 8 9]
Now I want starting with the last column to multiply the column with a number and then decrease the number and move to the next column.
So if we start with the number 1 and use for step 0.2 to modify all columns:
Anew = [1*0.6 2*0.8 3*1;
4*0.6 5*0.8 6*1;
7*0.6 8*0.8 9*1]
Or for second example we start with 0.9 with 0.1 as step and modify 3 columns:
B = [1 2 3 4;
5 6 7 8;
9 10 11 12;
13 14 15 16]
And to get:
Bnew = [1 2*0.7 3*0.8 4*0.9;
5 6*0.7 7*0.8 8*0.9;
9 10*0.7 11*0.8 12*0.9;
13 14*0.7 15*0.8 16*0.9]
The matrices might vary in their amount of columns, and I would like to set starting number, ending number, step number and the amount of columns I want to modify.
What you are describing can be achieved with broadcasted element-wise multiplication in matlab R2016b and beyond.
Let's say your inputs are the matrix A, start value start, step size step, and number n. You can start by constructing the factors you want to multiply by. I am going to assume that when n > size(A, 2), you want to just use the first n steps rather than error out:
k = size(A, 2);
n = min(n, k);
factors = ones(1, k);
factors(1 + k - n:end) = linspace(start - (n - 1) * step, start, n);
Now you can just multiply your matrix:
result = A .* factors;
This solution has the advantage of being extremely simple and fully vectorized.
If you have an older version of MATLAB, do the following instead:
result = A .* repmat(factors, size(A, 1), 1);
Or use Tony's trick:
result = A .* factors(ones(3, 1), :)
I just found the solution:
count = 0;
A = randi([-10,10],4,4);
Anew = [];
for i=0.9:-0.1:0
number_columns = 3;
if count == number_columns
rest = existing_columns - count;
for i=rest:-1:1
Anew = [(A(:,i)) Anew];
end
break
end
existing_columns = size(A,1);
Anew = [(A(:,existing_columns-count)*i) Anew];
count = count + 1;
end

Find unique rows of a cell array considering all possible permutations on each row

I have cell array A of dimension m * k.
I want to keep the rows of A unique up to an order of the k cells.
The "tricky" part is "up to an order of the k cells": consider the k cells in the ith row of A, A(i,:); there could be a row j of A, A(j,:), that is equivalent to A(i,:) up to a re-ordering of its k cells, meaning that for example if k=4it could be that:
A{i,1}=A{j,2}
A{i,2}=A{j,3}
A{i,3}=A{j,1}
A{i,4}=A{j,4}
What I am doing at the moment is:
G=[0 -1 1; 0 -1 2; 0 -1 3; 0 -1 4; 0 -1 5; 1 -1 6; 1 0 6; 1 1 6; 2 -1 6; 2 0 6; 2 1 6; 3 -1 6; 3 0 6; 3 1 6];
h=7;
M=reshape(G(nchoosek(1:size(G,1),h),:),[],h,size(G,2));
A=cell(size(M,1),2);
for p=1:size(M,1)
A{p,1}=squeeze(M(p,:,:));
left=~ismember(G, A{p,1}, 'rows');
A{p,2}=G(left,:);
end
%To find equivalent rows up to order I use a double loop (VERY slow).
indices=[];
for j=1:size(A,1)
if ismember(j,indices)==0 %if we have not already identified j as a duplicate
for i=1:size(A,1)
if i~=j
if (isequal(A{j,1},A{i,1}) || isequal(A{j,1},A{i,2}))...
&&...
(isequal(A{j,2},A{i,1}) || isequal(A{j,2},A{i,2}))...
indices=[indices;i];
end
end
end
end
end
A(indices,:)=[];
It works but it is too slow. I am hoping that there is something quicker that I can use.
I'd like to propose another idea, which has some conceptual resemblance to erfan's. My idea uses hash functions, and specifically, the GetMD5 FEX submission.
The main task is how to "reduce" each row in A to a single representative value (such as a character vector) and then find unique entries of this vector.
Judging by the benchmark vs. the other suggestions, my answer doesn't perform as well as one of the alternatives, but I think its raison d'ĂȘtre lies in the fact that it is completely data-type agnostic (within the limitations of the GetMD51), that the algorithm is very straightforward to understand, it's a drop-in replacement as it operates on A, and that the resulting array is exactly equal to the one obtained by the original method. Of course this requires a compiler to get working and has a risk of hash collisions (which might affect the result in VERY VERY rare cases).
Here are the results from a typical run on my computer, followed by the code:
Original method timing: 8.764601s
Dev-iL's method timing: 0.053672s
erfan's method timing: 0.481716s
rahnema1's method timing: 0.009771s
function q39955559
G=[0 -1 1; 0 -1 2; 0 -1 3; 0 -1 4; 0 -1 5; 1 -1 6; 1 0 6; 1 1 6; 2 -1 6; 2 0 6; 2 1 6; 3 -1 6; 3 0 6; 3 1 6];
h=7;
M=reshape(G(nchoosek(1:size(G,1),h),:),[],h,size(G,2));
A=cell(size(M,1),2);
for p=1:size(M,1)
A{p,1}=squeeze(M(p,:,:));
left=~ismember(G, A{p,1}, 'rows');
A{p,2}=G(left,:);
end
%% Benchmark:
tic
A1 = orig_sort(A);
fprintf(1,'Original method timing:\t\t%fs\n',toc);
tic
A2 = hash_sort(A);
fprintf(1,'Dev-iL''s method timing:\t\t%fs\n',toc);
tic
A3 = erfan_sort(A);
fprintf(1,'erfan''s method timing:\t\t%fs\n',toc);
tic
A4 = rahnema1_sort(G,h);
fprintf(1,'rahnema1''s method timing:\t%fs\n',toc);
assert(isequal(A1,A2))
assert(isequal(A1,A3))
assert(isequal(numel(A1),numel(A4))) % This is the best test I could come up with...
function out = hash_sort(A)
% Hash the contents:
A_hashed = cellfun(#GetMD5,A,'UniformOutput',false);
% Sort hashes of each row:
A_hashed_sorted = A_hashed;
for ind1 = 1:size(A_hashed,1)
A_hashed_sorted(ind1,:) = sort(A_hashed(ind1,:));
end
A_hashed_sorted = cellstr(cell2mat(A_hashed_sorted));
% Find unique rows:
[~,ia,~] = unique(A_hashed_sorted,'stable');
% Extract relevant rows of A:
out = A(ia,:);
function A = orig_sort(A)
%To find equivalent rows up to order I use a double loop (VERY slow).
indices=[];
for j=1:size(A,1)
if ismember(j,indices)==0 %if we have not already identified j as a duplicate
for i=1:size(A,1)
if i~=j
if (isequal(A{j,1},A{i,1}) || isequal(A{j,1},A{i,2}))...
&&...
(isequal(A{j,2},A{i,1}) || isequal(A{j,2},A{i,2}))...
indices=[indices;i];
end
end
end
end
end
A(indices,:)=[];
function C = erfan_sort(A)
STR = cellfun(#(x) num2str((x(:)).'), A, 'UniformOutput', false);
[~, ~, id] = unique(STR);
IC = sort(reshape(id, [], size(STR, 2)), 2);
[~, col] = unique(IC, 'rows');
C = A(sort(col), :); % 'sort' makes the outputs exactly the same.
function A1 = rahnema1_sort(G,h)
idx = nchoosek(1:size(G,1),h);
%concatenate complements
M = [G(idx(1:size(idx,1)/2,:),:), G(idx(end:-1:size(idx,1)/2+1,:),:)];
%convert to cell so A1 is unique rows of A
A1 = mat2cell(M,repmat(h,size(idx,1)/2,1),repmat(size(G,2),2,1));
1 - If more complicated data types need to be hashed, one can use the DataHash FEX submission instead, which is somewhat slower.
Stating the problem: The ideal choice in identifying unique rows in an array is to use C = unique(A,'rows'). But there are two major problems here, preventing us from using this function in this case. First is that you want to count in all the possible permutations of each row when comparing to other rows. If A has 5 columns, it means checking 120 different re-arrangements per row! Sounds impossible.
The second issue is related to unique itself; It does not accept cells except cell arrays of character vectors. So you cannot simply pass A to unique and get what you expect.
Why looking for an alternative? As you know, because currently it is very slow:
With nested loop method:
------------------- Create the data (first loop):
Elapsed time is 0.979059 seconds.
------------------- Make it unique (second loop):
Elapsed time is 14.218691 seconds.
My solution:
Generate another cell array containing same cells, but converted to string (STR).
Find the index of all unique elements there (id).
Generate the associated matrix with the unique indices and sort rows (IC).
Find unique rows (rows).
Collect corresponding rows of A (C).
And this is the code:
disp('------------------- Create the data:')
tic
G = [0 -1 1; 0 -1 2; 0 -1 3; 0 -1 4; 0 -1 5; 1 -1 6; 1 0 6; ...
1 1 6; 2 -1 6; 2 0 6; 2 1 6; 3 -1 6; 3 0 6; 3 1 6];
h = 7;
M = reshape(G(nchoosek(1:size(G,1),h),:),[],h,size(G,2));
A = cell(size(M,1),2);
for p = 1:size(M,1)
A{p, 1} = squeeze(M(p,:,:));
left = ~ismember(G, A{p,1}, 'rows');
A{p,2} = G(left,:);
end
STR = cellfun(#(x) num2str((x(:)).'), A, 'UniformOutput', false);
toc
disp('------------------- Make it unique (vectorized):')
tic
[~, ~, id] = unique(STR);
IC = sort(reshape(id, [], size(STR, 2)), 2);
[~, col] = unique(IC, 'rows');
C = A(sort(col), :); % 'sort' makes the outputs exactly the same.
toc
Performance check:
------------------- Create the data:
Elapsed time is 1.664119 seconds.
------------------- Make it unique (vectorized):
Elapsed time is 0.017063 seconds.
Although initialization needs a bit more time and memory, this method is extremely faster in finding unique rows with the consideration of all permutations. Execution time is almost insensitive to the number of columns in A.
It seems that G is a misleading point.
Here is result of nchoosek for a small number
idx=nchoosek(1:4,2)
ans =
1 2
1 3
1 4
2 3
2 4
3 4
first row is complement of the last row
second row is complement of one before the last row
.....
so if we extract rows {1 , 2} from G then its complement will be rows {3, 4} and so on. In the other words if we assume number of rows of G to be 4 then G(idx(1,:),:) is complement of G(idx(end,:),:).
Since rows of G are all unique then all A{m,n}s always have the same size.
A{p,1} and A{p,2} are complements of each other. and size of unique rows of A is size(idx,1)/2
So no need to any loop or further comparison:
h=7;
G = [0 -1 1; 0 -1 2; 0 -1 3; 0 -1 4; 0 -1 5; 1 -1 6; 1 0 6; ...
1 1 6; 2 -1 6; 2 0 6; 2 1 6; 3 -1 6; 3 0 6; 3 1 6];
idx = nchoosek(1:size(G,1),h);
%concatenate complements
M = [G(idx(1:size(idx,1)/2,:).',:), G(idx(end:-1:size(idx,1)/2+1,:).',:)];
%convert to cell so A1 is unique rows of A
A1 = mat2cell(M,repmat(h,size(idx,1)/2,1),repmat(size(G,2),2,1));
Update: Above method works best however if the idea is to get A1 from A other than G I suggest following method based of erfan' s. Instead of converting array to string we can directly work with the array:
STR=reshape([A.'{:}],numel(A{1,1}),numel(A)).';
[~, ~, id] = unique(STR,'rows');
IC = sort(reshape(id, size(A, 2),[]), 1).';
[~, col] = unique(IC, 'rows');
C1 = A(sort(col), :);
Since I use Octave I can not currently run mex file then I cannot test Dev-iL 's method
Result:
erfan method (string): 4.54718 seconds.
rahnema1 method (array): 0.012639 seconds.
Online Demo

Can't figure out "if else" code correctly?

if APC<-2.9079
BUYz='z'
SELLy='y'
elseif APC>0.44
BUYy='y'
SELLz='z'
else
end
x and y are both single column matrices.
I want the system to check if the APC column is above or below the values as mentioned above, if yes, pick up corresponding values from x and y.
Do you think I have entered this correctly?
When I try the code it does not create BUYy or any of the others.
Thanks for all the help.
Clarification:
I have loaded an excel file in Matlab using xlsread. It has columns such as x, y, APC, Date etc.
I am using the "if else" statement in the command window after loading the file.
First a brief introduction to if and elseif for vectors.
Suppose A = [1 2 3], then the following will not result in B = 5.
if A > 2
B = 5;
end
The reason for this is because what the if sees is (A > 2) == [0 0 1]. The first 0 will cause the statement to be false, thus it will skip the rest.
Similarly, the following will also not result in B = 5.
if A < 2
B = 5;
end
The reason for this is because the if now sees (A < 2) == [1 0 0]. The if requires all of the elements to be true for it to "jump into it". The two below are equivalent:
if A < x
and
if all(A < x)
elseif behaves the exact same way.
Suppose y = [1 2 3], doing x = 'y' will not give you x = [1 2 3] but x = y (the character "y", not the variable. If you want the x variable to be equal to the y variable you simply do x = y.
So, what can you do?
If I understand you correctly, you have a vectors similar to this (might be decimals, but that doesn't matter).
APC = [1, -3, 4, -2, 0];
x = [1 2 3 4 5];
y = [6 7 8 9 10];
You want BUYx = x(2), and SELLy = y(2) since the second element is the only one in APC that's less than -2.9079.
You also want BUYy = [y(1), y(3)] and SELLx = [x(1), x(3)], since the first and third element of APC is larger than 0.44.
What you can do is:
BUYx = x(APC < -2.9079)
SELLy = y(APC < -2.9079)
BUYy = y(APC > 0.44)
SELLx = x(APC > 0.44)
This returns:
BUYx =
2
SELLy =
7
BUYy =
6 8
SELLx =
1 3
If you only want the first elements and not all of them, you can use find like this:
BUYx = x(find(APC < -2.9079,1,'first'))
SELLy = y(find(APC < -2.9079,1,'first'))
BUYy = y(find(APC > 0.44,1,'first'))
SELLx = x(find(APC > 0.44,1,'first'))
find(x < y, 5, 'first') find the first 5 elements where x < y. find(APC < -2.9079, 1, 'first') finds only the first element where APC < -2.9079.
or just do as the first approach and then: BUYx = BUYx(1) to only get the first elements.
It might be I have misinterpreted your question, but I think this will get you well on your way nevertheless. Good luck!

Matlab: Find row indexes with common elements

I have a Matrix:
1 2 3
4 5 6
7 8 1
How may I use matlab to find this:
for 1st row: row3
for 2nd row: ---
for 3rd row: row1
I want to have row indices for each row witch have common elements.
Consider this
A = [1 2 3; %Matrix A is a bit different from yours for testing
4 5 6;
7 8 1;
1 2 7;
4 5 6];
[row col] =size(A)
answers = zeros(row,row); %matrix of answers,...
%(i,j) = 1 if row_i and row_j have an equal element
for i = 1:row
for j = i+1:row %analysis is performed accounting for
% symmetry constraint
C = bsxfun(#eq,A(i,:),A(j,:)'); %Tensor comparison
if( any(C(:)) ) %If some entry is non-zero you have equal elements
answers(i,j) = 1; %output
end
end
end
answers = answers + answers'; %symmetric
The output here is
answers =
0 0 1 1 0
0 0 0 0 1
1 0 0 1 0
1 0 1 0 0
0 1 0 0 0
of course the answers matrix is symmetric because your relation is.
The solution proposed by Acorbe can be quite slow if you have many rows and/or long rows. I have checked that in most cases the two solutions that I present below should be considerably faster. If your matrix contains just few different values (relative to the size of the matrix) then this should work pretty fast:
function rowMatches = find_row_matches(A)
% Returns a cell array with row matches for each row
c = unique(A);
matches = false(size(A,1), numel(c));
for i = 1:numel(c)
matches(:, i) = any(A == c(i), 2);
end
rowMatches = arrayfun(#(j) ...
find(any(matches(:, matches(j,:)),2)), 1:size(A,1), 'UniformOutput', false);
This other alternative might be faster when you have short rows, i.e. when size(A,2) is small:
function answers = find_answers(A)
% Returns an "answers" matrix like in Acorbe's solution
c = unique(A);
answers = false(size(A,1), size(A,1));
idx = 1:size(A,1);
for i = 1:numel(c)
I = any(A == c(i), 2);
uMatch = idx(I);
answers(uMatch, uMatch) = true;
isReady = all(A <= c(i), 2);
if any(isReady),
idx(isReady) = [];
A = A(~isReady,:);
end
end
Depending on what you are planning to do with this output it could be redundant to have a match for "3rd row: row1".
You already have this match earlier in your output in the form of "1st row: row3"

Matlab swap

I am trying to create a function that will swap a specific number in a matrix with a specific number in the same matrix. For examlpe, if I start with A = [1 2 3;1 3 2], I want to be able to create B = [2 1 3; 2 3 1], simply by telling matlab to swap the 1's with the 2's. Any advice would be appreciated. Thanks!
If you have the following matrix:
A = [1 2 3; 1 3 2];
and you want all the ones to become twos and the twos to become ones, the following would be the simplest way to do it:
B = A;
B(find(A == 1)) = 2;
B(find(A == 2)) = 1;
EDIT:
As Kenny suggested, this can even be further simplified as:
B = A;
B(A == 1) = 2;
B(A == 2) = 1;
Another way to deal with the original problem is to create a permutation vector indicating to which numbers should the original entries be mapped to. For the example, entries [1 2 3] should be mapped respectively to [2 1 3], so that we can write
A = [1 2 3; 1 3 2];
perm = [2 1 3];
B = perm(A)
(advantage here is that everything is done in one step, and that it also works for operations more complicated than swaps ; drawback is that all elements of A must be positive integers with a known maximum)
Not sure why you would to perform that particular swap (row/column interchanges are more common). Matlab often denotes ':' to represent all of something. Here's how to swap rows and columns:
To swap rows:
A = A([New order of rows,,...], :)
To Swap columns:
A = A(:, [New order of columns,,...])
To change the entire i-th column:
A(:, i) = [New; values; for; i-th; column]
For example, to swap the 2nd and 3rd columns of A = [1 2 3;1 3 2]
A = A(:, [1, 3, 2])
A = [1 2 3; 1 3 2]
alpha = 1;
beta = 2;
indAlpha = (A == alpha);
indBeta = (A == beta);
A(indAlpha) = beta;
A(indBeta ) = alpha
I like this solution, it makes it clearer what is going on. Less magic numbers, could easily be made into a function. Recycles the same matrix if that is important.
I don't have a copy of MatLab installed, but I think you can do some thing like this;
for i=1:length(A)
if (A(i)=1), B(i) = 2, B(i)=A(i)
end
Note, that's only convert 1's to 2's and it looks like you also want to convert 2's to 1's, so you'll need to do a little more work.
There also probably a much more elegant way of doing it given you can do this sort of thing in Matlab
>> A = 1:1:3
A = [1,2,3]
>> B = A * 2
B = [2,4,6]
There might be a swapif primitive you can use, but I haven't used Matlab in a long time, so I'm not sure the best way to do it.
In reference to tarn's more elegant way of swapping values you could use a permutation matrix as follows:
>> a =[1 2 3];
>> T = [1 0 0;
0 0 1;
0 1 0];
>> b = a*T
ans =
1 3 2
but this will swap column 2 and column 3 of the vector (matrix) a; whereas the question asked about swapping the 1's and 2's.
Update
To swap elements of two different values look into the find function
ind = find(a==1);
returns the indices of all the elements with value, 1. Then you can use Mitch's suggestion to change the value of the elements using index arrays. Remeber that find returns the linear index into the matrix; the first element has index 1 and the last element of an nxm matrix has linear index n*m. The linear index is counted down the columns. For example
>> b = [1 3 5;2 4 6];
>> b(3) % same as b(1,2)
ans = 3
>> b(5) % same as b(1,3)
ans = 5
>> b(6) % same as b(2,3)
ans = 6