What does MATLAB's full() function do with a row parameter? - matlab

I have a line of code as follows
nrma2 = full(abs(sum(A.*A',1)));
but sum(...,1) is a row. abs() returns only a row with absolute numbers
So, what will full() do?

full() will make a full matrix out of a sparse one. A sparse matrix can be a row vector, that's just a 1 -by- N matrix. The code you posted presumably has A as a sparse matrix, or includes the full() in case it is not a full matrix already to ensure it becomes a full vector.
A = sparse([1,0;1,0])
A =
(1,1) 1
(2,1) 1
nrma2 = full(abs(sum(A.*A',1))) % Full row-matrix
nrma2 =
1 0
abs(sum(A.*A',1)) % Sparse row-matrix
ans =
(1,1) 1
Specifically, from the documentation on full():
full
Convert sparse matrix to full storage
(...)
S — Sparse matrix to convert
Sparse matrix to convert, specified as a matrix. If S is already a
full matrix, then A is identical to S.

Related

Column-wise average over unequal number of values in matrix

I am looking for an easy way to obtain the column-wise average of a subset of values in a matrix (indexed by a logical matrix), preferably without having to use a loop. The problem that I am experiencing is that because the number of values in each column is different, matlab collapses the values-matrix and its column-wise mean becomes the total mean (of the entire matrix). Is there a specific function or easy workaround for this problem? See below for an example.
%% define value matrix and logical indexing matrix
values=[1 2 3 4; 5 6 7 8; 9 10 11 12];
indices=[1 0 0 1; 0 1 0 1; 1 1 0 0];
indices=indices==1; %convert to logical
%% calculate column-wise average
mean(values(indices),1)
accumarray-based approach
Use the column index as a grouping variable for accumarray:
[~, col] = find(indices);
result = accumarray(col(:), values(indices), [size(values,2) 1], #mean, NaN).';
Note that:
The second line uses (:) to force the first input to be a column vector. This is needed because the first line may produce col as a row or column vector, depending on the size of indices.
NaN is used as fill value. This is specified as the fifth input to accumarray.
The third input to accumarray defines output size. It is necessary to specify it explicitly (as opposed to letting accumarray figure it out) in case the last columns of indices only contain false.
Hacky approach
Multiply and divide element-wise by indices. That will turn unwanted entries into NaN, which can then be ignored by mean using the 'omitnan' option:
result = mean(values.*indices./indices, 1, 'omitnan');
Manual approach
Multiply element-wise by indices, sum each column, and divide element-wise by the sum of each column of indices:
result = sum(values.*indices, 1) ./ sum(indices, 1);

Manipulating sparse matrices in Matlab

Suppose I have a sparse matrix Sparstica that is a vertical concatenation of several other sparse matrices. When I type Sparstica(:), I get a list of the nonzero elements. In the left column, will be the index of the element, in the right column will be the nonzero element.
How can I manipulate the i-th and j-th non-zero element of every other sparse block matrix in the middle n-2 blocks (n sparse block matrices in total)?
Appended: To clarify what I mean by the i-th and j-th element of every other sparse matrix, suppose I have
Sparstica = [A_1; A_2; A_3; ... ; A_n]
This was created from vertcat. Now I need to take the i-th and j-th, say the 3rd and 5th, nonzero element of every other sparse matrix from A_2 to A_{N-1} (I know the notation for this actually isn't allowed, but just for demonstrative purposes). I'd like to accomplish this without using for-loops if possible.
You can find the non-zero elements using find:
>> A = speye(3)
A =
(1,1) 1
(2,2) 1
(3,3) 1
>> I = find(A ~= 0)
I =
1
5
9
If you need the indices in row/column format, use ind2sub:
>> [X, Y] = ind2sub(size(A), I)
X =
1
2
3
Y =
1
2
3

Initializing a matrix with a certain value in matlab

I have this matrix A of size 100x100. Now I have another vector Z=(1,24,5,80...) which has 100 elements. it is a column vector with 100 elements. Now for each row of the matrix A, I want its A(i,j) element to be 1 where i is the row from 1:100 and j is the column which is given by Z
So the elements that should be 1 should be
1,1
2,24
3,5
4,80
and so on
I know I can do it using a loop. But is there a direct simple way I mean one liner?
A matrix that has 100 non-zero elements out of 10000 (so only 1% non-zero) in total is best stored as sparse. Use the capability of matlab.
A = sparse(1:100,Z,1,100,100);
This is a nice, clean one-linear, that results in a matrix that will be stored more efficiently that a full matrix. It can still be used for matrix multiplies, and will be more efficient at that too. For example...
Z = randperm(100);
A = sparse(1:100,Z,1,100,100);
whos A
Name Size Bytes Class Attributes
A 100x100 2408 double sparse
This is a reduction in memory of almost 40 to 1. And, while the matrix is actually rather small as these things go, it is still faster to use it as sparse.
B = rand(100);
timeit(#() B*A)
ans =
4.5717e-05
Af = full(A);
timeit(#() B*Af)
ans =
7.4452e-05
Had A been 1000x1000, the savings would have been even more significant.
If your goal is a full matrix, then you can use full to convert it to a full matrix, or accumarray is an option. And if you want to insert values into an existing array, then use sub2ind.
One way to do it is to convert the values in Z to absolute indices in A using sub2ind, and then use vector indexing:
idx = sub2ind(size(A), 1:numel(Z), Z);
A(idx) = 1;
or simply in a one-liner:
A(sub2ind(size(A), 1:numel(Z), Z)) = 1;

2d matrix histogram in matlab that interprets each column as a separate element

I have a 128 x 100 matrix in matlab, where each column should be treated as a separate element. Lets call this matrix M.
I have another 128 x 2000 matrix(called V) composed of columns from matrix M.
How would I make a histogram that maps the frequency of each column being used in the second matrix?
hist(double(V),double(M)) gives the error:
Error using histc
Edge vector must be monotonically
non-decreasing.
what should I be doing?
Here is an example. We start with data that resembles what you described
%# a matrix of 100 columns
M = rand(128,100);
sz = size(M);
%# a matrix composed of randomly selected columns of M (with replacement)
V = M(:,randi([1 sz(2)],[1 2000]));
Then:
%# map the columns to indices starting at 1
[~,~,idx] = unique([M,V]', 'rows', 'stable');
idx = idx(sz(2)+1:end);
%# count how many times each column occurs
count = histc(idx, 1:sz(2));
%# plot histogram
bar(1:sz(2), count, 'histc')
xlabel('column index'), ylabel('frequency')
set(gca, 'XLim',[1 sz(2)])
[Lia,Locb] = ismember(A,B,'rows') also returns a vector, Locb,
containing the highest index in B for each row in A that is also a row
in B. The output vector, Locb, contains 0 wherever A is not a row of
B.
ismember with the rows argument can identify which row of one matrix the rows of another matrix come from. Since it works on rows, and you are looking for columns, just transpose both matrices.
[~,Locb]=ismember(V',M');
histc(Locb)

How can I assign a value to the diagonals of a 4-D matrix using linear indexing in MATLAB?

I have a 4-D matrix A of size NxNxPxQ. How can I easily change the diagonal values to 1 for each NxN 2-D submatrix in a vectorized way?
Incorporating gnovice's suggestion, an easy way to index the elements is:
[N,~,P,Q]=size(A);%# get dimensions of your matrix
diagIndex=repmat(logical(eye(N)),[1 1 P Q]);%# get logical indices of the diagonals
A(diagIndex)=1;%# now index your matrix and set the diagonals to 1.
You can actually do this very simply by directly computing the linear indices for every diagonal element, then setting them to 1:
[N,N,P,Q] = size(A);
diagIndex = cumsum([1:(N+1):N^2; N^2.*ones(P*Q-1,N)]);
A(diagIndex) = 1;
The above example finds the N diagonal indices for the first N-by-N matrix (1:(N+1):N^2). Each subsequent N-by-N matrix (P*Q-1 of them) is offset by N^2 elements from the last, so a matrix of size PQ-1-by-N containing only the value N^2 is appended to the linear indices for the diagonal of the first matrix. When a cumulative sum is performed over each column using the function CUMSUM, the resulting matrix contains the linear indices for all diagonal elements of the 4-D matrix.
You can use direct indexing, and some faffing about with repmat, to add the indexes for a single 50x50 diagonal to the offsets within the larger matrix of each 50x50 block:
Here's an example for a smaller problem:
A = NaN(10,10,5,3);
inner = repmat(sub2ind([10 10], [1:10],[1:10]), 5*3, 10); % diagonals
outer = repmat([10*10 * [0:5*3-1]]', 1, 10*10); % offsets to blocks
diags = inner + outer;
A(diags(:)) = 1;