I have a 5x5 matrix, V1, with values and a classification in both the first row and first column. If you wanted to, you could think of sectors in an economy that have a classification. The first two sectors have a 3-digit classification, the last two have a 4-digit classification.
V1 =
0 101 111 1234 1111
101 4 0 7 0
111 5 8 0 0
1234 6 0 6 2
1111 0 0 4 9
Now, I want to rediagonalize all columns that have a 4-digit code using MATLAB. That means in four-digit columns the values should be summed up over the whole column and shifted to the diagonal. In particular, the code should perform the following steps:
If the classification code has four digits AND the classification code is equal in both the first row AND the first column, then sum up the whole column (excl. the first value of the column, which is the classification code itself)
Elseif the classification code has three digits in the column, then leave the value as is
Else assign a zero.
The resulting matrix should look like this:
V1 =
0 101 111 1234 1111
101 4 0 0 0
111 5 8 0 0
1234 6 0 17 0
1111 0 0 0 11
I have tried the following code, but it didn't work:
[vrow vcol] = size(V1)
for c = 2:vcol;
for r = 2:vrow;
if all([ V1(1,c) == V1(r,1), numel(num2str(V1(1,c))) > 3, numel(num2str(V1(r,1))) > 3 ]) ;
V1(r,c) = sum(V1(2:end,c)) ;
elseif numel(num2str(V1(1,c))) == 3;
V1(r,c) = V1(r,c);
else
V1(r,c) = 0;
end
end
end
With the above code, I got the following result, which is sort of close to the desired result, only the column summations do not work yet:
V1 =
0 101 111 1234 1111
101 4 0 0 0
111 5 8 0 0
1234 6 0 10 0
1111 0 0 0 9
Thank you for any hints!
Since you're changing elements of V1 as the code executes, but also relying on values in V1 to get your answer (when you sum columns), you'll have errors if you set a value to 0 before it's needed in a sum. Below, I've fixed that issue by initializing a second matrix V2 as your output. I've also gone ahead and removed a few unneeded lines, as explained in the comments
[vrow vcol] = size(V1);
V2 = zeros(vrow, vcol);
for c = 2:vcol;
for r = 2:vrow;
if all([ V1(1,c) == V1(r,1), numel(num2str(V1(1,c))) > 3]) ; %Third statement was redundant
V2(r,c) = sum(V1(2:end,c)) ;
elseif numel(num2str(V1(1,c))) == 3;
V2(r,c) = V1(r,c);
end %We intialized whole thing to 0, so we don't need to set elements to 0
end
end
Related
I have the following row vector:
A = zeros(1,200);
I'd like to insert a '1' every 2-3 columns until I have exactly 80 ones in total that are approximately evenly spaced - as opposed to having fixed spacing - with the first 2 columns being zeros.
e.g.
0 0 1 0 1 0 0 1 0 0 1 0 1 ...
It would be nice if the combination could be permuted as well so that more than one row vector satisfies the criteria.
Thanks!
You could use repelem (run-length encoding) to do this. The way that repelem works is that we have two inputs: the values and the number of times each value is repeated.
For example
values = [0 1];
repeats = [1 2];
repelem(values, repeats)
% 0 1 1
We can also have duplicate values in the values array
values = [0 1 0 1];
repeats = [2 1 1 1];
repelem(values, repeats)
% 0 0 1 0 1
We can utilize this to solve your problem.
First we construct the values matrices to simply alternate between 0 and 1 meaning that we want the expanded matrix to contain some 0's followed by a 1, some 0's followed by a 1, etc.
values = ~mod(1:80, 2);
% 0 1 0 1 0 1 0 1 ....
In your case, the number of times each 0 is going to be repeated is going to be either 1 or 2. Each 1 however, is only going to be repeated once. To make this happen we use rand to pick randomly between 1 and 2 repeats. Then we assign all the repeats for 1 values to be a single repeat.
repeats = randi([1 2], size(values));
% Make sure that the 1's are always only repeated once
repeats(values) = 1;
We use 80 entries in the repeats and values arrays just to make sure that we end up with at least 80 values in the final (expanded) array.
Now apply the repelem and keep only the first 80 values
result = repelem(values, repeats);
result = result(1:80);
% 0 1 0 0 1 0 0 1 0 1 0 0 1
You can do this with a few standard functions and array indexing. Something like this ...
A = zeros(1,200);
ixs = round(cumsum(2 + rand(200,1)));
A(ixs(ixs<200))=1;
%Sample result here, first 20 entries: 0 0 1 0 1 0 0 1 0 0 1 0 1 0 0 1 0 1 0 1
What we're doing here is:
Setting up the A array (this is clear)
Defining an oversized array of index values to set to one (more on that below)
Then using that index to set values to one, trimming the oversize.
In terms of creating the index ixs, in innermost portion (2 + rand(200,1)) creates a 200x1 array of values between 2 and 3. Using cumsum generates the cumulative sum of this array, and then round rounds the values to an integer, which can be used for indexing. For example, the first 10 values is ixs look like this, for a particular run:
>> ixs(1:10)'
ans =
3 5 8 11 13 16 18 20 22 24
Since the number of 1 values will vary each time, I set this up to be oversized. That is, the last few values are [487 489 491 497 500], larger than the actual size required. This is why the values need to be trimmed with applying the index.
A = zeros(1,200);
idx = cumsum(1 + randi(2,80,1)); % This is the main trick
A(idx) = 1;
cumsum(1 + randi(2,80,1)) gets you the indexes for exactly 80 elements in A which need to be switched to 1 spaced by 2 or 3 (randomly)
I have a image (orig) and a corresponding binary mask (maskD) composed of vertical streaks/columns of values zero or one. I am trying to make a third matrix (streakTemp) composed of only those columns in the image which have a 1 value in the mask. I'm using the code below and for some reason its giving me as an output both the columns I'm looking for and then zero values where the mask value is 0...so my output has the same x length as my input image...it should be shorter with the mask values of zero excluded. Not sure what I'm doing wrong..any thoughts? Thanks!
streakTemp=[];
for i=1:x
if maskD(1,i)==1
streakTemp(:,i)=orig(:,i);
end
end
imtool(streakTemp);
This variant of your code should work:
streakTemp=[];
j=1;
for i=1:x
if maskD(1,i)==1
streakTemp(:,j)=orig(:,i);
j=j+1;
end
end
The problem you have is that the i index always corresponds to the original matrix column, thus it won's skip the column even if the mask condition is not met.
I think this is what you need:
orig = reshape(1 : 20, 4, 5)
maskD = [1 0 0 1 1; 1 0 0 1 1; 1 0 0 1 1; 1 0 0 1 1]
mask1D = maskD(1, :)
x = 5;
streakTemp=[];
for i=1:x
if maskD(1,i)==1
streakTemp(:,i)=orig(:,i);
end
end
streakTemp
streakTemp2 = orig(:, logical(mask1D))
It outputs:
orig =
1 5 9 13 17
2 6 10 14 18
3 7 11 15 19
4 8 12 16 20
maskD =
1 0 0 1 1
1 0 0 1 1
1 0 0 1 1
1 0 0 1 1
mask1D =
1 0 0 1 1
streakTemp =
1 0 0 13 17
2 0 0 14 18
3 0 0 15 19
4 0 0 16 20
streakTemp2 =
1 13 17
2 14 18
3 15 19
4 16 20
Here is more on logical indexing.
Note that your maskD does not have to be a matrix at all; you only need to store the first line, which is why I use mask1D.
You shouldn't be using loops here. Straight up logical indexing is totally fine for your purposes:
streakTemp = orig(:, maskD(1,:) == 1);
Remember that maskD is a mask that's the same size as your original image, so we only need to access the first row to do the check. Simply put, this takes a look at all columns where maskD is equal to 1 then uses the corresponding locations to subsample from your orig matrix to create a new matrix that removes all columns that are not desired.
N.B: This question is more complex than my previous question: Matlab: How I can make this transformation on the matrix A?
I have a matrix A 4x10000, I want to use it to find another matrix C, based on a predefined vector U.
I'll simplify my problem with a simple example:
from a matrix A
20 4 4 74 20 20 4
36 1 1 11 36 36 1
77 1 1 15 77 77 1
3 4 2 6 7 8 15
and
U=[2 3 4 6 7 8 2&4&15 7&8 4|6].
& : AND
| : OR
I want, first, to find an intermediate entity B:
2 3 4 6 7 8 2&4&15 7&8 4|6
[20 36 77] 0 1 0 0 1 1 0 1 0 4
[4 1 1] 1 0 1 0 0 0 1 0 1 4
[74 11 15] 0 0 0 1 0 0 0 0 1 2
we put 1 if the corresponding value of the first line and the vector on the left, made a column in the matrix A.
the last column of the entity B is the sum of 1 of each line.
at the end I want a matrix C, consisting of vectors which are left in the entity B, but only if the sum of 1 is greater than or equal to 3.
for my example:
20 4
C = 36 1
77 1
This was a complex one indeed and because of the many restrictions and labeling processes involved, it won't be as efficient as the solution to the previous problem. Here's the code to solve the posted problem -
find_labels1 = 2:8; %// Labels to be detected - main block
find_labels2 = {[2 4 15],[7 8],[4 6]}; %// ... side block
A1 = A(1:end-1,:); %// all of A except the last row
A2 = A(end,:); %// last row of A
%// Find unique columns and their labels for all of A execpt the last row
[unqmat_notinorder,row_ind,inv_labels] = unique(A1.','rows'); %//'
[tmp_sortedval,ordered_ind] = sort(row_ind);
unqcols = unqmat_notinorder(ordered_ind,:);
[tmp_matches,labels] = ismember(inv_labels,ordered_ind);
%// Assign labels to each group
ctl = numel(unique(labels));
labelgrp = arrayfun(#(x) find(labels==x),1:ctl,'un',0);
%// Work for the main comparisons
matches = bsxfun(#eq,A2,find_labels1'); %//'
maincols = zeros(ctl,numel(find_labels1));
for k = 1:ctl
maincols(k,:) = any(matches(:,labelgrp{k}),2);
end
%// Work for the extra comparisons added that made this problem extra-complex
lens = cellfun('length',find_labels2);
lens(end) = 1;
extcols = nan(ctl,numel(find_labels2));
for k = 1:numel(find_labels2)
idx = find(ismember(A2,find_labels2{k}));
extcols(:,k)=arrayfun(#(n) sum(ismember(labelgrp{n},idx))>=lens(k),1:ctl).'; %//'
end
C = unqcols(sum([maincols extcols],2)>=3,:).' %//'# Finally the output
I will give you a partial answer. I think you can take from here. Idea is to concatenate first 3 rows of A with each element of U replicated as last column. After you get the 3D matrix, replicate your original A and then just compare the rows. The rows which are equal, that is equivalent to putting one in your table.
B=(A(1:3,:).';
B1=repmat(B,[1 1 length(U)]);
C=permute(U,[3 1 2]);
D=repmat(C,[size(B1,1),1,1]);
E=[B1 D];
F=repmat(A',[1 1 size(E,3)]);
Now compare F and E, row-wise. If the rows are equal, then you put 1 in your table. For replicating & and |, you can form some kind of indicator vector.
Say,
indU=[1 2 3 4 5 6 7 7 7 8 8 -9 -9];
Same positive value indicates &, same negative value indicates |. Different value indicate different entries.
I hope you can take from here.
I want to create a variable that finds a pattern (let's say [1 1]) in different rows of a matrix (A). Of course there aren't an equal number of occurrences of this string in each row.
A = [ 0 0 0 1 1
1 1 1 0 0
0 1 0 1 1
1 1 1 0 0
0 1 0 0 1
1 0 1 1 1
0 1 0 1 0
1 1 1 0 1];
I could do:
for i = 1:n
var(i,:) = strfind(A(i,:),[1 1]);
end
but then both sides of the equation won't be equal.
ERROR: ??? Subscripted assignment dimension mismatch.
I try to preallocate. I create a matrix with what I think would be the maximum number of occurrences of this string in each row of matrix A (let's say 50).
for i = 1:n
var(i, :) = NaN(1,50)
end
That's followed by the previous bit of code and it's no good either.
I've also tried:
for i = 1:n
var(i,1:numel(strfind(A(i,:),[1 1])) = strfind(A(i,:),[1 1])
end
Error: The expression to the left of the equals sign is not a valid
target for an assignment.
How should I go about doing this?
The output I expect is a matrix var(i,:) that gives me the position in the matrix where each of these patterns occur. It works fine for just one row.
For example:
var(1,:) = [1 2 5 8 10 22 48]
var(2,:) = [2 3 4 7 34 45 NaN]
var(3,:) = [4 5 21 32 33 NaN]
Thanks!
In your first try: you tried to build a matrix with different length of rows.
In your second try: you pre-allocated, but then run it over by re-definning var(i,:), while you tried to put there your desired result.
In your third try: unfortunately you just missed one brackets- ) at the end of left expression.
This code suppose to work (what you did at 2nd and 3rd attempts, with pre-allocate and fixed brackets):
var=NaN(1,50);
for i = 1:n
var(i,1:numel(strfind(A(i,:),[1 1]))) = strfind(A(i,:),[1 1])
end
This is a follow up question from a previous SO question. Now I have a bit which I have spread it into 8 bits. I have use Amro's solution to spread the bit to 8 bits. Now I want an inverse way to convert the 8bits back to the single bit.
I have only managed to implement the inverse using for loop which take alot of time in the application.
Is there a faster way of doing it?
Since you are using the solution I suggested last time, lets say you have a matrix N-by-8 of these 'bits' where each row represent one 8-bit binary number. To convert to decimal in a vectorized way, its as simple as:
» M = randi([0 1], [5 8]) %# 5 random 8-bit numbers
M =
1 0 1 0 1 0 1 1
0 1 1 0 1 1 1 0
1 1 0 1 1 0 1 1
1 0 0 0 0 1 1 0
1 0 0 1 0 1 1 0
» d = bin2dec( num2str(M) )
d =
171
110
219
134
150
An alternative solution:
d = sum( bsxfun(#times, M, power(2,7:-1:0)), 2)