matlab transform logical index to range - matlab

Couldn't find a quick answer for this, it seems super simple but I don't get it. I want do the following transformation (for this example using my imaginary function transform):
a=[0 0 0 1 0 0 0 0];
b=(-1:2); %rule to transform for every true value in [a], -1:2 should be true
transform(a,b) %should output [0 0 1 1 1 1 0 0]
a=[0 0 0 1 0 0 0 0 1 1 0 0 0]; %another example
transform(a,b) %should output [0 0 1 1 1 1 0 1 1 1 1 1 0];
Is there an quick way of doing this transform, maybe using logical operators?
edit: I tried
a(find(a)'+(-1:2))=1 %requires matlab>2016 if I'm not mistaken, otherwise replace + sign with bsxfun(#plus,...)
but I'm looking for a possible function that does this without changing a and without using find (since using find kind of defeats the purpose of using logical matrices/indexing in the first place)

If you have the Image Processing Toolbox you can use imdilate:
nh(max(abs([b(1),b(end)]))+1+b) = true;
result = imdilate(a, nh);

I found an elegant oneliner that should solve your problem:
b=(-1:2)
a(find(a) + b(:)) = 1;
Hope it helps!

Related

Generating in Matlab a "modified" diagonal matrix

I want to construct a matrix A in Matlab of dimension w x (m*w) where
each row is full of zeros except m consecutive ones that shift towards the right hand side as we move down to the rows.
Few examples can clarify
w=3,m=4
A=[1 1 1 1 0 0 0 0 0 0 0 0;
0 0 0 0 1 1 1 1 0 0 0 0;
0 0 0 0 0 0 0 0 1 1 1 1]
or
w=3, m=3
A=[1 1 1 0 0 0 0 0 0;
0 0 0 1 1 1 0 0 0;
0 0 0 0 0 0 1 1 1]
or
w=2, m=3
A=[1 1 1 0 0 0;
0 0 0 1 1 1]
I can't see how to proceed and any hint would be extremely helpful.
Step 1. Simplify the problem
If you write the "modified diagonal matrix" you are asking about as a row vector it will always look like the following
% 1 ... 1 0 ... ... 0 ... ... ... ... ... ... ... ... 1 ... 1
% m ones m*w zeros w-1 times the same as before m ones
Step 2. Think how to solve the simplified problem
The fundamental unit you need is a vector of m ones followed by m*w zeros;
Once you have built such vector, you need it to be repeated w times, MATLAB already knows how to do that;
The only thing you miss are the trailing ones: append them;
Now that the vector you were looking for is completed, you need to turn it into a matrix. MATLAB already knows how to do this too.
Final code
Once you understood the above steps, the final behaviour can be achieved even with a one-liner
>> m = 4; w = 3;
>> vec2mat([repmat([ones(1, m) zeros(1, m*w)], 1, w-1) ones(1, m)], w*m)
ans =
1 1 1 1 0 0 0 0 0 0 0 0
0 0 0 0 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 1 1 1 1
About speed
It's true, for loops aren't so slow anymore. I timed my one-liner solution, the trivial for loop and Luis Mendo's solution with eye() and repelem().
Click on images to zoom
Tested on the same machine, with MATLAB R2018a.
As you can see, as long as m and w are quite small, even if you could point out some differences in speed, them won't be noticeable to humans.
Anyway if you are going to work with bigger matrices, it becomes quite obvious which solution is the best.
Here are some approaches:
Using eye and repelem:
A = repelem(eye(w), 1, m);
Using eye and indexing:
A = eye(w);
A = A(1:w, ceil(1/m:1/m:w));
Using eye and kron:
A = kron(eye(w), ones(1,m));
Using singleton expansion:
A = bsxfun(#eq, (1:m).', ceil(1/m:1/m:w)); % Or A = (1:m).'==ceil(1/m:1/m:w);

matlab - Accumarray Adjacency Matrix Confusion?

So I'm trying to create an adjacency matrix, and I'm confused on the difference between accumarray(matrix+1,1) and accumarray(matrix,1).
I did:
matrix = [ 1 3
4 2
1 3
3 1]
adMatrix1 = accumarray(matrix,1);
adMatrix1=adMatrix1~=0;
adMatrix1 = [0 0 1
0 0 0
1 0 0
0 1 0]
and then:
adMatrix2 = accumarray(matrix+1,1);
adMatrix2=adMatrix2~=0;
adMatrix2 = [0 0 0 0
0 0 0 1
0 0 0 0
0 1 0 0
0 0 1 0]
I know that with the "matrix+1", there's an extra row and column of zero's, but I don't understand why you would do it that way. When I looked it up, according to this post I should use "matrix+1", and the best explanation that I got for that was that "because indexing in matlab starts at 1".
I don't understand that at all... if I was trying to create an adjacency matrix, which way is correct? Any help would be greatly appreciated, thanks!
If your node IDs are 0 indexed you need the +1, otherwise you don't. So the question you need to ask is, are your node IDs 0 indexed or 1 indexed?
Does your matrix accept multiple links? If yes, then both results with accumarray above are not correct, since node 1 and 3 are connected 2 times.
Btw, you can consider sparse and full:
full(sparse(matrix(:,1), matrix(:,2), ones(1, size(matrix, 1))))
ans =
0 0 2
0 0 0
1 0 0
0 1 0
ones(1, size(matrix, 1)) is actually a vector of weight.

Create Vector Ranges From Variables

I have two vectors that hold the "start" and "end" locations (as logicals) that I wish to combine as to create a third vector, Final:
Starts = [0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0];
Ends = [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0];
With the Final Vector looking like this:
Final = [0 0 0 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 0];
Currently, I can accomplish this using a for loop as follows:
Start_Locations = find(Starts);
End_Locations = find(Ends);
Final = zeros(20,1);
for x=1:length(Start_Locations)
Final(Start_Locations(x):End_Locations(x),1) = 1;
end
I was wondering if there's a way to accomplish the same exact thing without a for loop. For example, I could accomplish what I outlined above with the following "hard-coded" statement:
Final([4:8,11:19],1) = 1;
Specifically, is there a way to combine the Start_Locations and End_Locations vectors in a way such that I could have a single statement like:
Final(Combined_Start_and_End_Locations,1) = 1;
to accomplish what I did with the for loop above? I'm trying to learn to avoid for loops as much as I can and would really appreciate any solution that creates the Final vector as described above without resorting to a loop.
Problems like this can often be solved using either diff or cumsum. They are essentially discrete derivative and integration functions.
For your problem I believe that
Final = cumsum([Starts 0]-[0 Ends]);
Final = Final(1:end-1);
achieves the desired result.

Matlab PRBS 4 waveform generation

I have a variable which has values of PRBS 4 sequence.
Output = [0 0 0 1 0 0 1 1 0 1 0 1 1 1 1];
I want to plot this in Matlab. I know I have to use idinput() function to generate prbs sequences. But I am using an old version of Matlab and this function is not available for me. Just by using plot(Output) will not give me the PRBS signal, since in the transition from 0 to 1 and 1 to 0, it will be like a triangle. I need to have a square waveform for PRBS.
Also, I want to make this signal 1 Gbps signal. Is this possible to do?
Best Regards,
nkp.
You can repeat each of output bit by some number and then plot.
For example: output = [0 0 1 0];
Then you repeat each bit by some number (let say 4), so the output vector is
[0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0].

Effiicient ways to count a streak of consecutive integers in MATLAB

Say I have a vector containing only logical values, such as
V = [1 0 1 0 1 1 1 1 0 0]
I would like to write a function in MATLAB which returns a 'streak' vector S for V, where S(i) represents the number of consecutive 1s in V up to but not including V(i). For the example above, the streak vector would be
S = [0 1 0 1 0 1 2 3 4 0]
Given that I have to do this for a very large matrix, I would very much appreciate any solution that is vectorized / efficient.
This should do the trick:
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
The complexity is only O(n), which I guess should be good enough.
For your sample input:
V = [1 0 1 0 1 1 1 1 0 0];
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
display(V);
display(S);
The result would be:
V =
1 0 1 0 1 1 1 1 0 0
S =
0 1 0 1 0 1 2 3 4 0
You could also do it completely vectorized with a couple intermediate steps:
V = [1 0 1 0 1 1 1 1 0 0];
Sall = cumsum(V);
stopidx = find(diff(V)==-1)+1;
V2=V;
V2(stopidx) = -Sall(stopidx)+[0 Sall(stopidx(1:end-1))];
S2 = cumsum(V2);
S = [0 S2(1:end-1)];
Afaik the only thing that can take a while is the find call; you can't use logical indexing everywhere and bypass the find call, because you need the absolute indices.
It's outside the box - but have you considered using text functions? Since strings are just vectors for Matlab it should be easy to use them.
Regexp contains some nice functions for finding repeated values.