I have a NxNx5 array T that I would like to convert into a Rx5 array TT such that the following condition is satisfied (where R is the number of non-zero entries of the array T(:,:,1)):
If T(i,j,1) == 0 then we ignore. If T(i,j,1) != 0 then I would like a row of TT whose entry is
[T(i,j,1) T(i,j,2) T(i,j,3) T(i,j,4) T(i,j,5)]
Note that T(i,j,k) (k = 2,3,4,5) could be zero. For example,
If
T(3,2,1) = 3
then I would like a row of TT to be
[3 0 2 1 5].
Some notes:
The entries of TT are all integers.
The entries accent in order column wise. i.e the first column of TT(:,:,1) maybe
[1 2 0 0 3 4 0 0 0 5 6]'
then the next column
[7 8 0 0 0 0 0 9 10 11 12]'
I think this does what you want:
ind = find(T(:,:,1));
ind = bsxfun(#plus, ind(:), (0:size(T,3)-1)*size(T,1)*size(T,2));
result = T(ind);
This will do it:
clear
rng(343)
N=7;
K=5;
T=randi([0,4],[N,N,K])
TT=reshape(T,[N*N,K])
TT(T(:,1)==0,:)=[] %delete rows with first col equal to 0
Related
I have two matrices. One is of size 1,000,000 x 9 and the other is 500,000 x 9.
The columns have the same meaning and the first 7 columns have the function of a key. Correspondingly, the last two columns have data character. There are many overlapping key values in both of the matrices and I would like to have a big matrix to compare the values. This big matrix should be of dimension 1,000,000 x 11.
For example:
A = [0 0 0 0 0 0 0 10 20; 0 0 0 0 0 0 1 30 40];
B = [0 0 0 0 0 0 0 50 60];
A merged matrix would look like this:
C = [0 0 0 0 0 0 0 10 20 50 60; 0 0 0 0 0 0 1 30 40 0 0];
As you can see, the first row of C has columns 8, 9 from matrix A and columns 10,11 from matrix B. The second row uses the columns 8, 9 from matrix A and 0,0 for the last to columns because there is no corresponding entry in matrix B.
I have accomplished this task theoretically, but it is very, very slow. I use loops a lot. In any other programming language, I would sort both tables, would iterate both of the tables in one big loop keeping two pointers.
Is there a more efficient algorithm available in Matlab using vectorization or at least a sufficiently efficient one that is idiomatic/short?
(Additional note: My largest issue seems to be the search function: Given my matrix, I would like to throw in one column vector 7x1, let's name it key to find the corresponding row. Right now, I use bsxfun for that:
targetRow = data( min(bsxfun(#eq, data(:, 1:7), key), [], 2) == 1, :);
I use min because the result of bsxfun is a vector with 7 match flags and I obviously want all of them to be true. It seems to me that this could be bottleneck of a Matlab algorithm)
Maybe with ismember and some indexing:
% locates in B the last ocurrence of each key in A. idxA has logicals of
% those keys found, and idxB tells us where in B.
[idxA, idxB] = ismember(A(:,1:7), B(:,1:7),'rows');
C = [ A zeros(size(A, 1), 2) ];
C(idxA, 10:11) = B(idxB(idxA), 8:9); % idxB(idxA) are the idxB != 0
I think this does what you want, only tested with your simple example.
% Initial matrices
A = [0 0 0 0 0 0 0 10 20;
0 0 0 0 0 0 1 30 40];
B = [0 0 0 0 0 0 0 50 60];
% Stack matrices with common key columns, 8&9 or 10&11 for data columns
C = [[A, zeros(size(A,1),2)]; [B(:,1:7), zeros(size(B,1),2), B(:,8:9)]];
% Sort C so that matching key rows will be consecutive
C = sortrows(C,1:7);
% Loop through rows
curRow = 1;
lastRow = size(C,1) - 1;
while curRow < lastRow
if all(C(curRow,1:7) == C(curRow+1,1:7))
% If first 7 cols of 2 rows match, take max values (override 0s)
% It may be safer to initialise the 0 columns to NaNs, as max will
% choose a numeric value over NaN, and it allows your data to be
% negative values.
C(curRow,8:11) = max(C(curRow:curRow+1, 8:11));
% Remove merged row
C(curRow+1,:) = [];
% Decrease size counter for matrix
lastRow = lastRow - 1;
else
% Increase row counter
curRow = curRow + 1;
end
end
Answer:
C = [0 0 0 0 0 0 0 10 20 50 60
0 0 0 0 0 0 1 30 40 0 0]
I have 2 matrices. Matrix A is already defined.
Matrix A = [10 7 8 4 1 6;
2 6 4 3 5 1;
7 3 2 2 8 7;
6 2 3 10 11 4;
1 5 1 2 4 5]
Matrix B = [1 1 1 0 0 0;
0 0 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 0 0 0 0]
Here each row has to have at least one '1'. This is our main target, so if there is a row with no 1's in it, we will have to move a '1' from another row.
How will we move the 1?
1) Check which rows that are all zeros. In this example, row 2 and 5.
2) We will subtract these 2 rows with all other rows that contain 1's in Matrix A. Which means that row 2 and row 5 in matrix A will be subtracted by all the other rows.
3) After the subtraction, we will check the change between each 2 subtracted rows, in the places of the 1's.
For example:
subtracting row 2 (all zeros) from row 1 will give us this [8 1 4] and
subtracting row 5 (all zeros) from row 1 will give us this [9 2 7].
subtracting row 2 (all zeros) from row 3 will give us this [6] and
subtracting row 5 (all zeros) from row 3 will give us this [2]
subtracting row 2 (all zeros) from row 4 will give us this [7 6] and
subtracting row 5 (all zeros) from row 4 will give us this [8 7]..
4) In the places of the 1's we will check the change between the rows and see the minimum change. The column that satisfied the minimum change, we will put the 1 in its place and remove it from the old place.
For example here:
For row 2, we will see where was the minimum change. Here the minimum change for row 2 was 1, which is in row 1. So we will remove the 1 of this column, and move it to the same column in row 2.
[1 0 1 0 0 0;
0 1 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 0 0 0 0]
subtracting row 5 (all zeros) from row 1 will give us this [8 4].
subtracting row 5 (all zeros) from row 3 will give us this [2]
subtracting row 5 (all zeros) from row 4 will give us this [8 7]..
The same for row 5, check the minimum change. Here it is 2 but it is the only one in the array which is in row 3. So for this case we want to add a condition, that we don't do the subtraction method unless there is two 1's or more in the row. so we will move to another minimum change which is here the 4 which is the change from row 1 so will remove the 1 in row 1 and put it in row 5
so here the output will be =
[1 0 0 0 0 0;
0 1 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 1 0 0 0]
and now the condition is satisfied, each row has at least one 1.
This is what i wrote in the code
%search for zero-rows in matrix B
minim = max(A) % Set the minimum value as an initial solution
zeroRows = find(sum(B,2)==0);
nonZeroRows = find(sum(B,2)~=0);
x = [];
y = [];
for zi = zeroRows'
for nZi = nonZeroRows'
%gives row vector of A with elements, where corresponding B elements are 1
%this row nZi in B is not zero
nonZeroRow = A(nZi,B(nZi,:) ==1);
nonZeroFull = A(nZi,:)
%gives row vector of A with elements, where corresponding B elements are 1
%this row zi in B is zero
zeroRow =A(zi, B(nZi,:) == 1);
zeroFull = A(zi,:)
%calculate the distance
disp(strcat('row ',num2str(nZi), ' - row ', num2str(zi)))
change = abs(nonZeroRow - zeroRow)
changeFull = nonZeroFull - zeroFull
x = [x change]
y = [y;changeFull]
Minimumchange = min(x)
if(Minimumchange < minim)
minim = Minimumchange
intersection = intersect(y,minim)
for i = 1 : length(intersection)
[w Index_intersection] = find(y == intersection(i))
B(zi,Index_intersection) = 0
B(nZi,Index_intersection) = 1
end
end
end
end
This is the code so far but its not giving the right output
Ask me if the question is still not clear.!
Since I can't comment due to the lack of my reputation I'll try it this way...
this would be your steps 1,2 and 3:
A = [10 7 8 4 1 6;
2 6 4 3 5 1;
7 3 2 2 8 7;
6 2 3 10 11 4;
1 5 1 2 4 5];
B = [1 1 1 0 0 0;
0 0 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 0 0 0 0];
%search for zero-rows in matrix B
zeroRows = find(sum(B,2)==0);
nonZeroRows = find(sum(B,2)~=0);
for nZi = nonZeroRows'
for zi = zeroRows'
%gives row vector of A with elements, where corresponding B elements are 1
%this row nZi in B is not zero
nonZeroRow = A(nZi,B(nZi,:) ==1);
%gives row vector of A with elements, where corresponding B elements are 1
%this row zi in B is zero
zeroRow =A(zi, B(nZi,:) == 1);
%calculate the distance
disp(strcat('row ',num2str(nZi), ' - row ', num2str(zi)))
abs(nonZeroRow - zeroRow)
end
end
However I don't understand what you mean in step 4. Could you formulated your step 4 shorter and more precisely? Or give some examples with intermediate steps?
How to efficiently combined cell array v row and Column with different size into a matrix, filling the vectors with 0?
for For example, if I have
A= {[1;2;3] [1 2 ; 1 3; 2 3] [1 2 3]};
I'd like to get either:
A=[1 0 0
2 0 0
3 0 0
1 2 0
1 3 0
2 3 0
1 2 3]
You can use simply padarray to pad your arrays with zeros before vertcat them:
B = padarray(A{1},[0 3-size(A{1},2)],'post')
C = padarray(A{2},[0 3-size(A{2},2)],'post')
D = padarray(A{3},[0 3-size(A{3},2)],'post')
%//Note the 3-size(A{1},2)... The 3 comes from the number of columns you want your final matrix to be, and it cannot be smaller than the maximum value of size(A{N},2) in your case it is 3, since A{3} is 3 columns wide.
result = vertcat (B,C,D)
result =
1 0 0
2 0 0
3 0 0
1 2 0
1 3 0
2 3 0
1 2 3
you can write a loop to iterate through your cell or use a cellfun to parallelize.
In a simple loop, it looks like:
result = [];
for t = 1:size(A,2)
B = padarray(A{t},[0 3-size(A{t},2)],'post');
result = vertcat(result,B);
end
This question already has answers here:
How can I find indices of each row of a matrix which has a duplicate in matlab?
(3 answers)
Closed 8 years ago.
I have two matrices and I want to find the indices of rows in Matrix B which have the same row values in Matrix A. Let me give a simple example:
A=[1,2,3; 2,3,4; 3,5,7; 1,2,3; 1,2,3; 5,8,6];
B=[1,2,3; 29,3,4; 3,59,7; 1,29,3; 1,2,3; 5,8,6;1,2,3];
For example, for first row in matrix A, The row1, row5, and row7 in Matrix B are correspondences.
I have written below code but it doesn't return back all indices which have the same row value in matrix A and only one of them (row7) is backed !!
A_sorted = sort(A,2,'descend'); % sorting angles
B_sorted = sort(B,2,'descend'); % sorting angles
[~,indx]=ismember(A_sorted,B_sorted,'rows')
the result is
indx_2 =
7
0
0
7
7
6
It means for the first row in matrix A , only one row ( row 7) in Matrix B is available !! But as you can see for first row in matrix A there is three correspondent rows in matrix B (Row 1, row 5 and row 7)
I think the best strategy is to apply ismember to unique rows
%make matrix unique
[B_unique,B2,B3]=unique(B_sorted,'rows')
[~,indx]=ismember(A_sorted,B_unique,'rows')
%For each row in B_unique, get the corresponding indices in B_sorted
indx2=arrayfun(#(x)find(B3==x),indx,'uni',0)
If you want to compare all pairs of rows between A and B, use
E = squeeze(all(bsxfun(#eq, A, permute(B, [3 2 1])), 2));
or equivalently
E = pdist2(A,B)==0;
In your example, this gives
E =
1 0 0 0 1 0 1
0 0 0 0 0 0 0
0 0 0 0 0 0 0
1 0 0 0 1 0 1
1 0 0 0 1 0 1
0 0 0 0 0 1 0
The value E(ia,ib) tells you if the ia-th row of A equals the ib-th row of B.
I have a matrix A with size 10x100 as shown below. What I want to do is:
I'll work row by row in which for each row I'll check the data of
each coloumn in this row
Let's say I'm now in the first col cell in the first row. I'll check if the value is zero I'll move to the next col, and so on till I found a col having a non-zero value and save its col number e.g. col 3 "this means that col 1&2 were zeros"
Now I'm in the first non zero col in row1, I'll move to the next col till I find a col with zero value. I'll fetch the col just before this zero one which must be a non-zero one and save it. e.g col 7 "this means that col4&5&6 are non-zeros and col8 is zero"
Now I want to save the median middle col between this two columns e.g col3 and col7 then the middle col is col5 so I'll save the index row1_col5. if there are two middle values then any of them is fine.
I'll then move to the next col till I find a non-zero col "do the
same steps from 2-->5" till the first row is finished.
Move to the next row and start over again from step 2-->5.
There are two rules: -The first one is that I'll get the middle index of non-zero consecutive values only if there is a minimum of 3 non-zero consecutive values, if there are two non-zero consecutive value then the middle will not be calculated -The second one is that if the number of zero consecutive values are less than 3 then they will be ignored and will be considered as non-zero values. e.g in the below example the first row middle values are col5 and col11. In row2 col5 is counted, while no cols in row3 satisfy this conditions , and in row4 col6 or col7 will be counted.
After finishing all the rows want to have a vector or array holding the positions of all the middle indexes e.g row1_col5 row1_col17 row2_col_10 and so on.
example:
A = [ 0 0 0 2 4 1 0 0 0 1 3 2;
0 0 0 5 1 1 1 1 0 0 0 1;
0 3 4 1 0 3 1 2 0 0 1 3;
0 0 0 0 1 3 4 5 0 0 0 0];
for the first row the middle value will be 5 and 11 and so on
So if anyone could please advise how can I do this with least processing as this can be done using loops but if there is more efficient way of doing it? Please let me know if any clarification is needed.
Now you have clarified your question (again...) here is a solution (still using a for loop...). It includes "rule 7" - excluding runs of fewer than three elements; it also includes the second part of that rule - runs of fewer than three zeros don't count as zero. The new code looks like this:
A = [ 0 0 0 2 4 1 0 0 0 1 3 2;
0 0 0 5 1 1 1 1 0 0 0 1;
0 3 4 1 0 3 1 2 0 0 1 3;
0 0 0 0 1 3 4 5 0 0 0 0];
retVal = cell(1, size(A, 1));
for ri = 1:size(A,1)
temp = [1 0 0 0 A(ri,:) 0 0 0 1]; % pad ends with 3 zeros + 1
% so that is always a "good run"
isz = (temp == 0); % find zeros - pad "short runs of 0" with ones
diffIsZ = diff(isz);
f = find(diffIsZ == 1);
l = find(diffIsZ == -1);
shortRun = find((l-f)<3); % these are the zeros that need eliminating
for ii = 1:numel(shortRun)
temp(f(shortRun(ii))+1:l(shortRun(ii))) = 1;
end
% now take the modified row:
nz = (temp(4:end-3)~=0);
dnz = diff(nz); % find first and last nonzero elements
f = find(dnz==1);
l = find(dnz==-1);
middleValue = floor((f + l)/2);
rule7 = find((l - f) > 2);
retVal{ri} = middleValue(rule7);
end
You have to use a cell array for the return value since you don't know how many elements will be returned per row (per your updated requirement).
The code above returns the following cell array:
{[5 11], [6], [7], [7]}
I appear still not to understand your "rule 7", because you say that "no columns in row 3 satisfy this condition". But it seems to me that once we eliminate the short runs of zeros, it does. Unless I misinterpret how you want to treat a run of non-zero numbers that goes right to the edge (I assume that's OK - which is why you return 11 as a valid column in row 1; so why wouldn't you return 7 for row 3??)
Try this:
sizeA = size(A);
N = sizeA(1);
D = diff([zeros(1, N); (A.' ~= 0); zeros(1,N)]) ~= 0;
[a b] = find(D ~= 0);
c = reshape(a, 2, []);
midRow = floor(sum(c)/2);
midCol = b(1:2:length(b))
After this, midRow and midCol contain the indices of your centroids (e.g. midRow(1) = 1, midCol(1) = 4 for the example matrix you gave above.
If you don't mind using a for loop:
A = [ 0 0 1 1 1 0 1;
0 0 0 0 0 0 0;
0 1 1 1 1 0 0;
0 1 1 1 0 1 1;
0 0 0 0 1 0 0]; % data
sol = repmat(NaN,size(A,1),1);
for row = 1:size(A,1)
[aux_row aux_col aux_val] = find(A(row,:));
if ~isempty(aux_col)
sol(row) = aux_col(1) + floor((find(diff([aux_col 0])~=1,1)-1)/2);
% the final 0 is necessary in case the row of A ends with ones
% you can use either "floor" or "ceil"
end
end
disp(sol)
Try it and see if it does what you want. I hope the code is clear; if not, tell me