I use the following code with different data files, without any problem. However, there is one file that creates troubles, and when I run the code with that file, I get an error:
'Index exceeds matrix dimensions.'
I think that is because 'i' is equal to 2546, but when the code runs the line: i=i+1, instead of stopping at 2546, it keeps going and stops (giving the error) at 2547 - which of course exceeds the matrix dimensions. In fact, when the code stops working, producing the error, I can see in the Workspace that 'i' is equal to 2547, and 'j' to 2 (instead of 5, if the loop would have worked fine).
As the exact same code works perfectly fine with other files, I assume there is something to do with this specific file. Any insight on how to solve the issue?
Here is the code:
for i=1:size(colInd,1)
for j=1:size(colInd,2)
if colInd(i,j)>0 && colInd(i,j)<=13
M1(i,j)=Windowsdata(i,colInd(i,j));
elseif colInd(i,j)==0 | colInd(i,j)==14
M1(i,j)=NaN;
elseif colInd(i,j)==-1 | colInd(i,j)==15
M1(i,:)=NaN;
i=i+1;
end
end
end
Example lines from colInd, which is 2546 x 5 double
4 5 6 7 8
-1 0 1 2 3
2 3 4 5 6
11 12 13 14 15
0 1 2 3 4
5 6 7 8 9
3 4 5 6 7
5 6 7 8 9
-1 0 1 2 3
11 12 13 14 15
Example lines from Windowsdata, which is 2546 x 13 double
-4.37370443344116 -1.64714550971985 0.569347918033600 1.62668454647064 3.73541021347046 5.15196514129639 4.04361486434937 1.77491927146912 0.702701866626740 -0.354207783937454 1.18695282936096 2.82701897621155 4.01644039154053
3.72757863998413 1.44241857528687 -1.15181946754456 -2.97936320304871 -5.16328191757202 -4.25508642196655 -2.47518587112427 0.287524074316025 -1.17596077919006 -2.04023623466492 -2.78539514541626 -2.96725606918335 -5.59557294845581
-5.52127933502197 -1.69257545471191 3.61181259155273 4.46472501754761 0.345008432865143 -4.78608989715576 -7.80892658233643 -8.83082866668701 -5.61083126068115 -4.40270948410034 -3.05102157592773 -4.67261123657227 -5.50971889495850
1.24733197689056 0.692575275897980 0.549045324325562 1.33569169044495 2.26527953147888 3.19271230697632 1.92626762390137 -0.00543282041326165 -1.76812970638275 -3.55482935905457 -2.28071475028992 2.58129334449768 6.07476711273193
2.17950797080994 2.73428583145142 1.63492679595947 -0.256836771965027 -0.773400425910950 -1.04227805137634 -1.82435607910156 -2.64025163650513 -1.53338134288788 -2.29410648345947 -4.26442241668701 -4.76120758056641 -4.47712421417236
-0.246993020176888 0.157185763120651 0.250829964876175 -0.986824631690979 1.40918886661530 5.03370332717896 8.15515422821045 6.41663646697998 2.43448591232300 -2.98093175888062 -3.53510475158691 -1.89243125915527 1.47953033447266
4.36318445205688 5.06837177276611 5.78645181655884 6.97499608993530 7.49895095825195 5.27076244354248 4.75153970718384 4.35132837295532 2.37539553642273 0.0745598822832108 0.782306909561157 1.98255372047424 1.82295107841492
0.393009424209595 0.348423480987549 -0.0242169145494699 -0.451373100280762 0.792472958564758 3.95410203933716 6.95971775054932 6.07247447967529 4.61793804168701 2.25326156616211 1.17793440818787 -1.02191674709320 -1.40514099597931
2.97367334365845 2.56695508956909 -0.0324615947902203 -0.512259364128113 -0.169182881712914 1.99416732788086 2.05820631980896 1.26427924633026 -0.107465483248234 -1.26579785346985 -2.51656532287598 -2.19553661346436 -1.86673855781555
-5.92374515533447 -4.78130531311035 -5.02523994445801 -4.12971973419189 -2.56698751449585 -2.16855669021606 -2.66882371902466 -3.24165868759155 -4.10617780685425 -4.71752023696899 -4.63748264312744 -3.33325529098511 -2.00388121604919
If I understand you correctly, colInd is a NxM matrix with N=2546 and you have the expectation, that in your last elseif, by incrementing i by one you end up in the next outer for loop iteration, starting j from 1 to M in the (i+1)th iteration.
If this is the behaviour you want to achieve, you need to use the break statement to break out of the inner for loop. Else, if j<size(colInd,2), j will be incremented and the inner loop continues.
If I understood your desired behaviour correctly, it is not a problem with the file, but rather of your algorithm. But as #excaza pointed out, you should really give an example that is mcve, including the necessary variables (such as Windowsdata). Otherwise it is really difficult to make sense of what you're trying to do.
Edit: Give this a try:
for i=1:size(colInd,1)
for j=1:size(colInd,2)
if colInd(i,j)>0 && colInd(i,j)<=13
M1(i,j)=Windowsdata(i,colInd(i,j));
elseif colInd(i,j)==0 | colInd(i,j)==14
M1(i,j)=NaN;
elseif colInd(i,j)==-1 | colInd(i,j)==15
M1(i,:)=NaN;
break;
end
end
end
Edit2: To clarify: The problem you describe occurs if in the iteration where i=size(colInd,1) (i.e. i=2546) and j<size(colInd,2) (i.e. j<5, let's assume j=1 for simplicity) the last elseif holds. Thus, causing your i to be incremented to i=2546+1=2547, and the inner loop goes through the next iteration. With j=2 now, in the first if you attempt to access colInd(2547,2), which exceeds the dimensions of colInd.
Edit3: If you want a more Matlab-y implementation for this, because for loops are not very good Matlab coding style, I also append this solution that uses vectorization (albeit not that great).
colInd = [ 4 5 6 7 8;
-1 0 1 2 3;
2 3 4 5 6;
11 12 13 14 15;
0 1 2 3 4;
5 6 7 8 9;
3 4 5 6 7;
5 6 7 8 9;
-1 0 1 2 3;
11 12 13 14 15;
10 11 12 13 14];
Windowsdata = reshape([1:size(colInd,1)*13],[size(colInd,1) 13]);
M1 = zeros(size(colInd));
M2 = zeros(size(colInd));
c1 = find(colInd>0&colInd<=13);
c2 = find(colInd==0|colInd==14);
c3 = find(colInd==-1|colInd==15);
[x1,~] = ind2sub(size(colInd),c1);
[x3,~] = ind2sub(size(colInd),c3);
M2(c1) = Windowsdata(sub2ind(size(Windowsdata),x1,colInd(c1)));
M2(c2) = NaN;
M2(x3,:) = NaN;
It runs about 3-5 times as fast as your for loop implementation.
Edit: Added the missing term to the sub2ind call, and it gives the same result as the for loop:
M1 =
34 45 56 67 78
NaN NaN NaN NaN NaN
14 25 36 47 58
NaN NaN NaN NaN NaN
NaN 5 16 27 38
50 61 72 83 94
29 40 51 62 73
52 63 74 85 96
NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN
110 121 132 143 NaN
M2 =
34 45 56 67 78
NaN NaN NaN NaN NaN
14 25 36 47 58
NaN NaN NaN NaN NaN
NaN 5 16 27 38
50 61 72 83 94
29 40 51 62 73
52 63 74 85 96
NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN
110 121 132 143 NaN
Related
I have these matrices:
I1 = [60 30 15 35 20 -25 30 5 45 25 -10 40 10];
I2 = [60 30 60 35 20 60 30 60 45 25 60 40 60];
A= 0:12 ;
I want this:
Ir=[60 30 15 NaN 60 35 20 -25 NaN 60 30 5 NaN 60 45 25 -10 NaN 60 40 10 NaN 60]
Ar= [0 1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10 10 10 11 12 12 12]
How:
When I1 and I2 are same, proceed. When different, use elements of both I1 and I2 and insert an NaN between them.
and Ar is such that use element of A and proceed when elements of I1 and I2 are same. But when different, repeat the value of A 3 times. 1st for I1 and then for NaN and then for I2.
Can't figure out a way to do this. How do I do this?
A more vectorized way to do this would be to create an augmented matrix where the top row is I1, the bottom row is I2 and the middle row is full of NaN. After, create a logical matrix which is the same size as this augmented matrix you just created and you would adjust the columns of this matrix where you'd set the bottom two rows of this logical matrix to 0 for each corresponding column in this matrix where I1 == I2. Once you're done, use this logical matrix and index the augmented matrix. The advantage of this indexing is that it accesses the elements in column major format, which is exactly what you're after. We will only sample the top row of the augmented matrix unless the elements in I1 and I2 don't equal each other. If that's the case, we sample the entire column which includes both I1, nan and I2. Because we accessed by columns, your desired output is a row so you'll need to transpose the result when you're done.
To create the index vector, you would do the same thing but you would create a matrix of IDs where we have three rows where each row is the index ID array A. You'll also need to transpose this result after you index into A:
aug = [I1; nan(1,numel(I1)); I2];
V = true(size(aug));
V(2:3, I1 == I2) = false;
Ir = aug(V).';
ID = repmat(A, 3, 1);
Ar = ID(V).';
We get:
>> format compact
>> Ir
Ir =
Columns 1 through 17
60 30 15 NaN 60 35 20 -25 NaN 60 30 5 NaN 60 45 25 -10
Columns 18 through 23
NaN 60 40 10 NaN 60
>> Ar
Ar =
Columns 1 through 17
0 1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10
Columns 18 through 23
10 10 11 12 12 12
I can provide you with pseudo-code.
for each value in A
if I1 at [current A value] equals I2 at [current A value]
add I1 at [current A value] to end of Ir
add [current A value] to end of Ar
else
add I1 at [current A value] to end of Ir
add NaN to Ir
add I2 at [current A value] to end of Ir
add [current A value] to Ar 3 times
If your problem was conceptual hopefully this is enough.
Let me know if you have any further questions.
You can use two indices and solve the problem. The first index ii goes over the input vectors, and the second index k grows as the computation proceeds. A simple code using for-loop and if-statement looks like this:
I1 = [60 30 15 35 20 -25 30 5 45 25 -10 40 10];
I2 = [60 30 60 35 20 60 30 60 45 25 60 40 60];
Ir = [];
Ar = [];
k = 1;
for ii=1:length(I1)
if I1(ii)==I2(ii)
Ir(k) = I1(ii);
Ar(k) = ii-1;
k = k + 1;
else
Ir(k:k+2) = [I1(ii) NaN I2(ii)];
Ar(k:k+2) = ii-1;
k = k + 3;
end
end
Will result in:
Ir =
60 30 15 NaN 60 35 20 -25 NaN 60 30 5 NaN 60 45 25 -10 NaN 60 40 10 NaN 60
Ar =
0 1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10 10 10 11 12 12 12
all,
I have a large dataset with a lot of continuous NAs, is there any fast way to replace the NAs with the average of previous and next non-missing value by column?
Thanks a lot
Lou
Interesting question... if only you explained clearly what you want. Maybe it's this?
data = [1 3 NaN 7 6 NaN NaN 2].'; %'// example data: column vector
isn = isnan(data); %// determine which values are NaN
inum = find(~isn); %// indices of numbers
inan = find(isn); %// indices of NaNs
comp = bsxfun(#lt,inan.',inum); %'// for each (number,NaN): 1 if NaN precedes num
[~, upper] = max(comp); %// next number to each NaN (max finds *first* maximum)
data(isn) = (data(inum(upper))+data(inum(upper-1)))/2; %// fill with average
In this example: original data:
>> data.'
ans =
1 3 NaN 7 6 NaN NaN 2
Result:
>> data.'
ans =
1 3 5 7 6 4 4 2
If you have a 2D array and want to work by columns, a for loop over columns is probably the best option.
And of course, if there can be NaN's at the beginning or end of a column, the problem is undefined.
Assuming NaNs are not in the first/last row in any column, here is how I would do it:
(If there are multiple consecutive NaNs, it searches for previous ann next non-missing values and averages them).
% Creating A
A=magic(7);
newA=A; %Result will be in newA
A(3,4)=NaN;
A(2,1)=NaN;
A(5,6)=NaN;
A(6,6)=NaN;
A(4,6)=NaN;
% Finding NaN position and calculating positions where we have to average numbers
ind=find(isnan(A));
otherInd=setdiff(1:numel(A(:)),ind);
for i=1:size(ind,1)
temp=otherInd(otherInd<ind(i));
prevInd(i,1)=temp(end);
temp=otherInd(otherInd>ind(i));
nextInd(i,1)=temp(1);
end
% For faster processing purposes
allInd(1:2:2*length(prevInd))=prevInd;
allInd(2:2:2*length(prevInd))=nextInd;
fun=#(block_struct) mean(block_struct.data)
prevNextNums=A(allInd);
A
newA(ind)=blockproc(prevNextNums,[1 2],fun)
%-----------------------Answer--------------------------
A =
30 39 48 1 10 19 28
NaN 47 7 9 18 27 29
46 6 8 NaN 26 35 37
5 14 16 25 34 NaN 45
13 15 24 33 42 NaN 4
21 23 32 41 43 NaN 12
22 31 40 49 2 11 20
newA =
30 39 48 1 10 19 28
38 47 7 9 18 27 29
46 6 8 17 26 35 37
5 14 16 25 34 23 45
13 15 24 33 42 23 4
21 23 32 41 43 23 12
22 31 40 49 2 11 20
Essentially, I have a matrix of data with many "holes" represented by NaN, and I want to retrieve the indices of all NaN's that are clustered fewer than 4 times in a single column.
e.g. with the matrix:
A =
23 12 NaN 56 60 21 NaN
60 56 94 22 45 NaN NaN
23 55 19 83 NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN
84 99 43 32 89 12 NaN
76 92 73 47 22 12 10
23 55 12 93 61 94 20
NaN NaN NaN NaN NaN NaN NaN
41 16 83 39 82 37 43
14 78 92 40 81 29 60
it would return:
ans =
[4; 5; 6; 10; 16; 17; 18; 22; 25; 28; 29; 30; 34; 40; 41; 42; 46; 58; 70; 82]
So far, I have a vector with the indices of all the NaN values from
nan_list=find(isnan(A(:)))
but I don't know how to extract sequential numbers from that vector without using loops, which would be too expensive. I also tried something similar to the answer posted by b3 here, by switching all NaN's to a value that doesn't appear in the matrix, but that code was not as transferable for other data sets.
Thanks for any suggestions!
Code
N = 4; %// Fewer than clusters of N or N+ NaNs are to be detecteed
nan_pos = isnan(A) %// Find NaN positions as a binary array
conv_res = conv2(double(nan_pos),[0 ones(1,N)]')==N %//' Perform convolution
start_ind = find(conv_res(N+1:end,:)) %// Find positions where clusters of N or N+ NaNs start
nan_pos(unique(bsxfun(#plus,start_ind,[0:N-1])))=0 %// Get positions of all those clustered N or N+ NaNs and set them in NaN position array as zeros
out = find(nan_pos) %// Finally the desired output
Example
As an example, let's try this code on a slightly different input that would hopefully test out various aspects of the problem -
A = [
23 12 NaN 56 60 21 NaN
60 56 94 22 45 NaN NaN
23 55 19 83 NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN
84 99 43 32 89 12 NaN
76 92 73 47 22 12 10
23 55 12 93 61 94 20
NaN NaN NaN NaN NaN NaN NaN
41 NaN NaN 39 82 37 43
14 78 NaN 40 81 NaN 60]
Now, let's assume that we are looking to find indices of cluster fewer than 3 NaNs. Thus editing N as 3 in the code, the output is -
out =
10 22 23 25 46 58 70 72 82
This makes sense when we look into the input.
This should work:
[rows, ~] = size(A);
maxNansPerCol = 4;
% find which columns have few enough NaNs
Anans = isnan(A);
nansInCols = sum(Anans);
qualifyingCols = nansInCols <= maxNansPerCol;
% zero the other columns
mask = repmat(qualifyingCols,rows,1);
B = Anans .* mask;
% get the NaN locations
indices = find(B(:));
(Apologies if something is slightly off--I don't have MATLAB on this computer to test it)
I'm trying to implement the Baker map.
Is there a function that would allow one to divide a 8 x 8 matrix by providing, for example, a sequence of divisors 2, 4, 2 and rearranging pixels in the order as shown in the matrices below?
X = reshape(1:64,8,8);
After applying divisors 2,4,2 to the matrix X one should get a matrix like A shown below.
A=[31 23 15 7 32 24 16 8;
63 55 47 39 64 56 48 40;
11 3 12 4 13 5 14 6;
27 19 28 20 29 21 30 22;
43 35 44 36 45 37 46 38;
59 51 60 52 61 53 62 54;
25 17 9 1 26 18 10 2;
57 49 41 33 58 50 42 34]
The link to the document which I am working on is:
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.39.5132&rep=rep1&type=pdf
This is what I want to achieve:
Edit: a little more generic solution:
%function Z = bakermap(X,divisors)
function Z = bakermap()
X = reshape(1:64,8,8)'
divisors = [ 2 4 2 ];
[x,y] = size(X);
offsets = sum(divisors)-fliplr(cumsum(fliplr(divisors)));
if any(mod(y,divisors)) && ~(sum(divisors) == y)
disp('invalid divisor vector')
return
end
blocks = #(div) cell2mat( cellfun(#mtimes, repmat({ones(x/div,div)},div,1),...
num2cell(1:div)',...
'UniformOutput',false) );
%create index matrix
I = [];
for ii = 1:numel(divisors);
I = [I, blocks(divisors(ii))+offsets(ii)];
end
%create Baker map
Y = flipud(X);
Z = [];
for jj=1:I(end)
Z = [Z; Y(I==jj)'];
end
Z = flipud(Z);
end
returns:
index matrix:
I =
1 1 3 3 3 3 7 7
1 1 3 3 3 3 7 7
1 1 4 4 4 4 7 7
1 1 4 4 4 4 7 7
2 2 5 5 5 5 8 8
2 2 5 5 5 5 8 8
2 2 6 6 6 6 8 8
2 2 6 6 6 6 8 8
Baker map:
Z =
31 23 15 7 32 24 16 8
63 55 47 39 64 56 48 40
11 3 12 4 13 5 14 6
27 19 28 20 29 21 30 22
43 35 44 36 45 37 46 38
59 51 60 52 61 53 62 54
25 17 9 1 26 18 10 2
57 49 41 33 58 50 42 34
But have a look at the if-condition, it's just possible for these cases. I don't know if that's enough. I also tried something like divisors = [ 1 4 1 2 ] - and it worked. As long as the sum of all divisors is equal the row-length and the modulus as well, there shouldn't be problems.
Explanation:
% definition of anonymous function with input parameter: div: divisor vector
blocks = #(div) cell2mat( ... % converts final result into matrix
cellfun(#mtimes, ... % multiplies the next two inputs A,B
repmat(... % A...
{ones(x/div,div)},... % cell with a matrix of ones in size
of one subblock, e.g. [1,1,1,1;1,1,1,1]
div,1),... % which is replicated div-times according
to actual by cellfun processed divisor
num2cell(1:div)',... % creates a vector [1,2,3,4...] according
to the number of divisors, so so finally
every Block A gets an increasing factor
'UniformOutput',false...% necessary additional property of cellfun
));
Have also a look at this revision to have a simpler insight in what is happening. You requested a generic solution, thats the one above, the one linked was with more manual inputs.
Currently, I need to write a program using matlab to transformate a matrix using homogeneous coordinates like this
% for translation
T = [1 0 dx; 0 1 dy; 0 0 1];
For example:
A =
92 99 1 8 15 67 74 51 58 40
98 80 7 14 16 73 55 57 64 41
4 81 88 20 22 54 56 63 70 47
85 87 19 21 3 60 62 69 71 28
86 93 25 2 9 61 68 75 52 34
17 24 76 83 90 42 49 26 33 65
23 5 82 89 91 48 30 32 39 66
79 6 13 95 97 29 31 38 45 72
10 12 94 96 78 35 37 44 46 53
11 18 100 77 84 36 43 50 27 59
>> I = translate(A, 4, 4)
I =
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN 92 99 1 8 15 67
NaN NaN NaN NaN 98 80 7 14 16 73
NaN NaN NaN NaN 4 81 88 20 22 54
NaN NaN NaN NaN 85 87 19 21 3 60
NaN NaN NaN NaN 86 93 25 2 9 61
NaN NaN NaN NaN 17 24 76 83 90 42
Where NaN cells means 'empty spaces'. As you can see, A matrix was translate 4 units on x axis and 4 units on y axis, leaving NaN values. The output matrix I must be the same size like A.
However, my current program don't work fine using images (It does not put 'NaN' values on empty spaces, it puts '1'):
So, this is my program:
function t_matrix = translate(input_matrix, dx, dy)
[rows cols] = size(input_matrix);
t_matrix = input_matrix;
t_matrix(:) = NaN;
T = [1 0 dx; 0 1 dy; 0 0 1];
for n = 1:numel(input_matrix)
[x y] = ind2sub([rows cols], n);
v = [x y 1]';
v = T*v;
a = floor(v(1));
b = floor(v(2));
if a > 0 && b > 0
t_matrix(a, b) = input_matrix(x,y);
end
end
t_matrix = t_matrix(1:rows, 1:cols);
How can I implement homogeneous transformation using matlab in a easier way?
Only restriction: keep using this matrix:
% for translation
T = [1 0 dx; 0 1 dy; 0 0 1];
And keep NaN values for empty spaces.
The problem with you code might be that you operate on integers, and NaN is a double value. You can not assign input_matrix to t_matrix. You should create t_matrix using nan function:
t_matrix = nan(size(input_matrix));
The following is a direct translation of your code, I just removed the loop
function I = translate(input_matrix, dx, dy)
% get matrix dimensions
[rows cols] = size(input_matrix);
T = [1 0 dx; 0 1 dy; 0 0 1];
% create a nan's output matrix
I = nan(size(input_matrix));
% create row-column index pairs
[R C] = meshgrid(1:cols, 1:rows);
% append 1 at the end
IDX = [R(:) C(:) ones(numel(input_matrix),1)]';
% transform coordinates
V = floor(T*IDX);
% find indices that fall into [rows, cols] range
keep = find(V(1,:)>0 & V(1,:)<=rows & V(2,:)>0 & V(2,:)<=cols);
% assign output only to the correct indices
I(sub2ind([rows cols], V(1,keep), V(2,keep))) = input_matrix(sub2ind([rows cols], R(keep), C(keep)))
end
On the other hand, you can obtain the same result as in the question just by running the following function (no T matrix though..)
function I = translate(A, dx, dy)
I = nan(size(A));
I(dx+1:end, dy+1:end) = A(1:end-dx, 1:end-dy);
end
The easiest way achieving it, if you have the image processing toolbox, is to use the built-in functions maketform and imtransform:
I = imread('cameraman.tif');
dx = 40;
dy = 100;
tform = maketform('affine',[1 0 0; 0 1 0; dx dy 1]); %#Create a translation matrix
J = imtransform(I,tform,'XData',[0 size(I,2)+dx],'YData',[0 size(I,1)+dy]);
imshow(I), figure, imshow(J)
The matrix given as input to maketform is a transpose of yours matrix
It is important to set the XData and YData, otherwise you will not get the "translation effect', since imtransform finds the smallest output range.
If you want to get the same size as inital image, use the following syntax:
.
J = imtransform(I,tform,'XData',[0 size(I,2)],'YData',[0 size(I,1)]);
Image Before:
Image After:
Image After (Keeping the same size):