Dot product with two unequal array sizes - alternative? - matlab

Is there any elegant way to perform the last step in this - I am looking for something like 'result = myFun(A,B)'
N = 100
A = randn(N,3);
B = randn(1,3)
result = sum(A.*repmat(B,N,1),2);

Related

How to speed up this for-loop code (for large matrix `H_sparse`)?

H_sparse is a large matrix with size 20,000-by-5,000. The matrix-vector product dk = A * Del_H; in the code below is time consuming. How can I speed up this code?
This code is another way to get an equivalent result to the built-in function pinv(H_Sparse) in MATLAB. I think MATLAB uses mex files and bsxfun in pinv, so it's fast.
But in theory the below algorithm is faster:
function PINV_H_Spp = Recur_Pinv_Comp( H_Sparse )
L = 1;
H_candidate = H_Sparse(:,L);
A = pinv( H_candidate );
for L = 1:size( H_Sparse, 2 ) - 1
L = L + 1;
Del_H = H_Sparse(:,L);
dk = A * Del_H;
Ck = Del_H - H_candidate * dk;
Gk = pinv( Ck );
A = A - dk * Gk;
A(end+1,:) = Gk;
H_candidate(:,end+1) = Del_H;
end
PINV_H_Spp = A;
The code can be compared with pinv(H_Sparse), using H_Sparse = rand(20000, 5000) as sample data.
A few points of improvement:
You can change your loop index to 2:size(H_Sparse, 2) and remove the line L = L + 1;.
There's no need to create a separate variable H_candidate, since it's only partitions of H_Sparse. Instead, just index H_sparse accordingly and you'll save on memory.
Instead of building your matrix A row-by-row, you can preallocate it and update it using indexing. This usually provides some speed-up.
Return A as your output. No need to put it in another variable.
Here's a new version of the code incorporating the above improvements:
function A = Recur_Pinv_Comp(H_Sparse)
[nRows, nCols] = size(H_Sparse);
A = [pinv(H_Sparse(:, 1)); zeros(nRows-1, nCols)];
for L = 2:nCols
Del_H = H_Sparse(:, L);
dk = A(1:L-1, :)*Del_H;
Ck = Del_H - H_Sparse(:, 1:L-1)*dk;
Gk = pinv(Ck);
A(1:L-1, :) = A(1:L-1, :) - dk*Gk;
A(L, :) = Gk;
end
end
In addition, it looks like your calls to pinv only ever operate on column vectors, so you may be able to replace them with a simple array transpose and scaling by the sum of the squares of the vector (which might speed things up a little more):
Gk = Ck.'./(Ck.'*Ck);

Matlab: How to re-order (re-organize) a matrix

I have a random column matrix:
r = rand(1,300)';
I want to re-order it so that instead of having elements in the order of 1,2,3,...,300
I will have elements 1,11,21,31,...,291,2,12,22,32,...,292,3,13,33,...293,...,300.
In other words, I want to take every 10th value, beginning with 1 and put them in that order, then do the same for 2 with every 10th value. I know one way to do this is:
n = 10;
r = [r(1:n:numel(r)); r(2:n:numel(r)); r(3:n:numel(r));...;r(10:n:numel(r))]; % Skipped 4-9 in this example
But obviously, this is very cumbersome to do more than a couple of times. Is there something more efficient?
A loop should be easy, but I am not doing it correctly, it seems (I can see why this might not work, but I can't correct it).
(Here is what I tried:)
n = 10;
for i = 1:10
a = [r(i:n:numel(r))];
end
Any suggestions or help is greatly appreciated.
You can do it like this:
r = reshape(reshape(r, 10, 30)', 300, 1)
EDIT:
As pointed out by #LuisMendo on the comments, it's safer to use .' than ' to transpose the matrix, because if the matrix is complex, that could introduce a complex conjugation. Then, it would be safer to do it like this:
r = reshape(reshape(r, 10, 30).', 300, 1)
You could reshape it into 30x10 matrix, transpose, and take the flat index:
A = 1:300;
A = reshape(A,30,10);
A = A';
A = A(:);
Try this -
intv = 10; %%// Interval after which you intend to get the values consecutively
out = r(reshape(reshape(1:numel(r),intv,[])',1,[]))
Some of the other solutions posted are more efficient, but your idea was a good one. It requires a simple fix to work:
N = numel(r);
M = N/10;
a=[];
for ii = 1:M
a= [a r(ii:10:N)];
end
Hope this helps

Optimizing a kind-of-correlation computation

I have some code that currently reads:
data = repmat(1:10, 1, 2);
N = 6;
period = 10;
result = NaN * zeros(1, period);
for i=1:period
range_indices = i:i+N;
temp_data = data(range_indices);
result(i) = sum( temp_data .* fliplr(temp_data));
end
I'm trying to make this faster (for larger datasets, e.g. period = 2000 and N = 1600), but I'm unable to get this into a form where it's a matrix operation (e.g. by using conv or xcorr).
You should be able to completely linearise this. Firstly, consider the range_indices. These have the form:
1 -> N
2 -> N+1
...
P -> N+P
where P is the period. We can set up a matrix of these values like so:
range_indices = bsxfun(#plus,1:N,(1:period)'-1);
We can use these to grab the data directly, like so:
temp_data = data(range_indices);
It is then fairly simply to complete the function:
result = sum(temp_data.*fliplr(temp_data));
Finally, this isn't really related to the question, but just something I thought I'd point out - in future if you need to generate a matrix of NaN values, you should use nan(1,period) instead.

How to overwrite part of a vector?

I am trying to rewrite part of a vector given that:
t = -10:.1:10
x = exp((-3.*t);
The length of x will be 201, and I want to rewrite the first 100 values.
The only way I've gotten to work is by doing this:
EDIT Fixed typo.
t = 0:.1:10;
x = exp((-3.*t); % EDIT: THERE WAS A TYPO HERE
z = zeros(1,100);
for k = 1 : 100
x(k) = z(k);
end
There are three questions. First: What is a faster and more efficient way of doing this? Second: What do I do if I don't want to overwrite the first part of the code but rather the middle or the second part? Third: Is there a way of utilizing the full range of t where t = -10:.1:10 and just ignoring the first half instead of writing a whole new variable for it?
First: Nothing else I've tried has been successful.
Second: The only way I can think to do that is to append the two vectors together, but then it doesn't overwrite the data, so that is a no go.
Third:I have tried an if statement and that didn't work at all.
Your code appears to assign something to y, then changes the value of x. I assume that is a typo - and not the problem you actually want to fix.
In general, if you have
t = -10:0.1:10; % my preference: t = linspace(-10,10,201);
and
y = exp(-3 * t );
but you want to set the first 100 elements of y to zero, you can then do
y(1:100) = 0;
If you wanted never to compute y(1:100) in the first place you might do
y = zeros(size(t));
y(101:end) = exp(-3 * t(101:end));
There are many variations on this. I think the above code samples address all three of your questions.
change your
for k = 1 : 100
x(k) = z(k); % i think it should be y(k) though
end
to
x(1:100) = 0;
You could use logical indexing; that is, you can use a logical statement to select elements of a vector/matrix:
t = -10:0.1:10;
x = exp((-3.*t);
x(t < 0) = 0;
This works for the middle of a matrix too:
x(t > -5 & t < 5) = whatever;

Applying grouping to values greater than average

I have a vector where I want to group based on the rolling average of the values in my vector. If the values are greater than average than I place them in group 1, if they are less they go in group 2.
What function can be used to give a group number to each to value within my vector based on whether or not its value is greater than the current average.
I don't think there is a function to assign "labels" to array entries.
Assuming v is your input vector, an easy approach would be to simply do:
v(v>mean(v)) %Group 1
v(v<mean(v)) %Group 2
If you intend on doing more with it of course, you could do the following:
avg = mean(v);
flag = zeros(size(v));
for i=1:numel(v)
if(v(i)>avg)
flag(i) = 1;
else
flag(i) = 2;
end
end
flag would contain your requisite grouping. Now if you want the elements of v in group 1, you can simply use:
v(flag==1)
If you want a rolling average though, it depends on how you compute it, but the same basic method should suffice.
There's no simple function that will do that. You'll need something like this:
N = length(vec);
[lo_group hi_group] = deal( NaN(ceil(N/2),1) );
[sum lo_ct hi_ct] = deal(0);
for i=1:N
v = vec(i);
sum = sum + v;
avg = sum/i;
if v>avg
hi_ct = hi_ct + 1;
hi_group(hi_ct) = v;
else
lo_ct = lo_ct + 1;
lo_group(lo_ct) = v;
end
end