Fastest way to copy values between two matrix - matlab

I am looking for a fastest way to copy some special value of a matrix to other matrix. Assume that I have matrix A such as
A =[4 1 5 4 4
-2 -1 1 2 2
3 -1 1 7 3
5 3 -1 1 -2
6 4 4 -1 1]
My aim is that copy element that have value 1 and -1 to matrix B. The expected matrix B such as
B =[ 0 1 0 0 0
0 -1 1 0 0
0 -1 1 0 0
0 0 -1 1 0
0 0 0 -1 1]
I performed two way to create matrix B. However, I think that my way is still not fastest way if size of matrix A becomes larger. I know that the forum has many expert matlab guy. Could you suggest to me another way?
This is my code
%%First way:
tic;B=((A==1)|(A==-1)).*A;toc
Elapsed time is 0.000026 seconds.
%%Second way:
tic;idx1=find(A==1);idx2=find(A==-1);B=zeros(size(A));B(idx1)=1; B(idx2)=-1;toc;B
Elapsed time is 0.000034 seconds.

here's somthing on par with #thewaywewalk
B=A.*reshape(abs(A(:))==1,size(A));
This is how I test these:
A=randi(10,1000,1000)-7;
B1=#() ((A==1)|(A==-1)).*A;
B2=#() (abs(A) == 1).*A;
B3=#() A.*reshape(abs(A(:))==1,size(A));
timeit(B1)
ans =
0.0136
timeit(B2)
ans =
0.0080
timeit(B3)
ans =
0.0079
These will change from run to run, but the methods are on par...
here's the same test on a range of matrix sizes:

The only thing which comes to my mind, which could be faster:
B = (abs(A) == 1).*A;

Related

Up-/Downsampling of a logical vector (not with zeros)

I hope you can help with a little problem I am having.
I want to upsample and downsample a vector with zeros and ones. We have the functions upsample and downsample for that, however, the upsample function in Matlab only adds zeros to the vector. I would like to repeat the value, instead of just putting in zeros.
Unfortunately the upsample function does not do that. Thus, I tried to use repmat (in the third dimension) and then reshape to get back to the old format. I know it must be possible with these functions, but if I simply use them, the vector just gets duplicated and added to the end.
An example:
The input vector is: [1 0 0 1 0 1 0 1 1 1 0 0] (these should be random).
Now I want to upsample (say) by a factor of 2. Then I want to get:
[1 1 0 0 0 0 1 1 0 0 1 1 0 0 1 1 1 1 1 1 0 0 0 0].
Thanks in advance for any help!
You can use repelem:
>> repelem([1 0 1],2)
ans =
1 1 0 0 1 1
Or using repmat and reshape when input is a column vector:
>> input = [1 0 1];
>> reshape(repmat(input, 2, 1), 1, [])
ans =
1 1 0 0 1 1

Is there a fast way to count occurrences of items in a matrix and save them in another matrix without using loops?

I have a time-series matrix X whose first column contains user ID and second column contains the item ID they used at different times:
X=[1 4
2 1
4 2
2 3
3 4
1 1
4 2
5 3
2 1
4 2
5 4];
I want to find out which user used which item how many times, and save it in a matrix Y. The rows of Y represent users in ascending order of ID, and the columns represent items in ascending order of ID:
Y=[1 0 0 1
2 0 1 0
0 0 0 1
0 3 0 0
0 0 1 1]
The code I use to find matrix Y uses 2 for loops which is unwieldy for my large data:
no_of_users = size(unique(X(:,1)),1);
no_of_items = size(unique(X(:,2)),1);
users=unique(X(:,1));
Y=zeros(no_of_users,no_of_items);
for a=1:size(A,1)
for b=1:no_of_users
if X(a,1)==users(b,1)
Y(b,X(a,2)) = Y(b,X(a,2)) + 1;
end
end
end
Is there a more time efficient way to do it?
sparse creates a sparse matrix from row/column indices, conveniently accumulating the number of occurrences if you give a scalar value of 1. Just convert to a full matrix.
Y = full(sparse(X(:,1), X(:,2), 1))
Y =
1 0 0 1
2 0 1 0
0 0 0 1
0 3 0 0
0 0 1 1
But it's probably quicker to just use accumarray as suggested in the comments:
>> Y2 = accumarray(X, 1)
Y2 =
1 0 0 1
2 0 1 0
0 0 0 1
0 3 0 0
0 0 1 1
(In Octave, sparse seems to take about 50% longer than accumarray.)

Calculate mean of all values below the diagnonal line in an adjacency matrix

I have an adjacency matrix M, something like this:
[1 2 0 2 4
2 1 2 0 -1
0 3 1 2 3
2 0 2 1 0
4 -1 3 0 1]
I want to calculate the mean of all values below (but not including) the diagonal. The final output should be 1.5.
To get those values, I thought I'd use N = tril(M,-1). The issue is that I now have zeros in upper and lower part of the matrix N and therefore mean(sum(N)./sum(N~=0)) wouldn't work. Since I also have negative values, I can't just do the mean of values >=0 either. How can I do this?
In one line using logical indexing to extract just the values below the diagonal:
M = [ 1 2 0 2 4;
2 1 2 0 -1;
0 3 1 2 3;
2 0 2 1 0;
4 -1 3 0 1];
mean(M(tril(true(size(M)),-1)))
This returns 1.5 as #excaza indicated.

Count the number of the first zero elements

I would line to find the number of the first consecutive zero elements. For example in [0 0 1 -5 3 0] we have two zero consecutive elements that appear first in the vector.
could you please suggest a way without using for loops?
V=[0 0 1 -5 3 0] ;
k=find(V);
Number_of_first_zeros=k(1)-1;
Or,
Number_of_first_zeros=find(V,1,'first')-1;
To solve #The minion comment (if that was the purpose):
Number_of_first_zeros=find(V(find(~V,1,'first'):end),1,'first')-find(~V,1,'first');
Use a logical array to find the zeros and then look at where the zeros and ones are alternating.
V=[1 2 0 0 0 3 5123];
diff(V==0)
ans =
0 1 0 0 -1 0
Create sample data
V=[1 2 0 0 0 3 5123];
Find the zeros. The result will be a logical array where 1 represents the location of the zeros
D=V==0
D =
0 0 1 1 1 0 0
Take the difference of that array. 1 would then represent the start and -1 would represent the end.
T= diff(D)
ans =
0 1 0 0 -1 0
find(T==1) would give you the start and find(T==-1) would give you the end. The first index+1 of T==1 would be the start of the first set of zeros and the first index of T==-1 would be the end of the first set of zeros.
You could find position the first nonzero element using find.
I=find(A, 1);
The number of leading zeros is then I-1.
My solution is quite complex yet it doesn't use the loops and it does the trick. I am pretty sure, that there is a more direct approach.
Just in case no one else posts a working solution here my idea.
x=[1 2 4 0 20 0 10 1 23 45];
x1=find(x==0);
if numel(x1)>1
x2=[x1(2:end), 0];
x3=x2-x1;
y=find(x3~=1);
y(1)
elseif numel(x1)==1
display(1)
else
display('No zero found')
end
x is the dataset. x1 contains the index of all zero elements. x2 contains all those indices except the first one (because matrix dimensions must agree, one zero is added. x3 is the difference between the index and the previous index of zeros in your dataset. Now I find all those differences which are not 1 (do not correspond to sequences of zeros) and the first index (of this data is the required result. The if case is needed in case you have only one or no zero at all.
I'm assuming your question is the following: for the following vector [0 0 1 -5 3 0], I would like to find the index of the first element of a pair of 0 values. Is this correct? Therefore, the desired output for your vector would be '1'?
To extend the other answers to find any such pairs, not just 0 0 (eg. 0 1, 0 2, 3 4 etc), then this might help.
% define the pattern
ptrn = [ 0 0 ];
difference = ptrn(2) - ptrn(1)
V = [0 0 1 -5 3 0 0 2 3 4 0 0 1 0 0 0]
x = diff(V) == difference
indices = find(x)
indices =
1 6 11 14 15

Inverse lookup table

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)