How to shift non circularly in Matlab - matlab

I am trying to shift non circularly in MATLAB so even if I shift outside of the index it will add 0s to correct it. I tried following the answer in How do I shift columns (left or right) in a matrix? but had no success.
data = [1 2 3 4 5; 11 12 13 14 15; 21 22 23 24 25; 31 32 33 34 35]
d = 3; % shift; positive/negative for right/left
result = zeros(size(data), 'like', data); % preallocate with zeros
result(:,max(1,1+d):min(end,end+d)) = data(:,max(1,1-d):min(end,end-d)); % write values
In my output results is nothing but the same size but all zeroes
Desired output:
0 0 0 1 2 3 4 5
0 0 0 11 12 13 14 15
0 0 0 21 22 23 24 25
0 0 0 31 32 33 34 35

You can do it by creating a matrix result, the final size, filled with zeros, then copying the original data into the final result, making sure you place the data at the right indices.
What you have in your example code is not right for what you ask. If I run it,the final result is padded fine but truncated at the size of the original data matrix. This is how some matrix are shifted (with the shifted columns dropped altogether), but that's not what you asked.
A simple way to do it, is to create a padding matrix of the proper size, then simply concatenate it with your original data matrix. This can be done as below:
%% Initial data
data = [1 2 3 4 5; 11 12 13 14 15; 21 22 23 24 25; 31 32 33 34 35] ;
d = 3 ;
%% shift and pad with zeros
nrows = size(data,1) ; % Number of rows in [data]
pad = zeros( nrows , abs(d) ) ; % create padding matrix
if d>0
result = [pad data] ; % Concatenate the filler matrix on the left
else
result = [data pad] ; % Concatenate the filler matrix on the right
end
And just to be sure:
>> result
result =
0 0 0 1 2 3 4 5
0 0 0 11 12 13 14 15
0 0 0 21 22 23 24 25
0 0 0 31 32 33 34 35
If you want to reuse the same way than in your example code, you have to adjust it a bit to allow for the new columns:
%% create result and copy data
result = zeros( size(data,1) , size(data,2)+abs(d) ) ;
colStart = max(1,1+d) ;
result(:,colStart:colStart+size(data,2)-1) = data ;
This will create the same result matrix as above.

Related

All unique multiplication products

I'd like to obtain all unique products for a given vector.
For example, given a:
a = [4,10,12,3,6]
I want to obtain a matrix that contains the results of:
4*10
4*12
4*3
4*6
10*12
10*3
10*6
12*3
12*6
3*6
Is there a short and/or quick way of doing this in MATLAB?
EDIT: a may contain duplicate numbers, giving duplicate products - and these must be kept.
Given:
a =
4 10 12 3 6
Construct the matrix of all pairwise products:
>> all_products = a .* a.'
all_products =
16 40 48 12 24
40 100 120 30 60
48 120 144 36 72
12 30 36 9 18
24 60 72 18 36
Now, construct a mask to keep only those values below the main diagonal:
>> mask = tril(true(size(all_products)), -1)
mask =
0 0 0 0 0
1 0 0 0 0
1 1 0 0 0
1 1 1 0 0
1 1 1 1 0
and apply the mask to the product matrix:
>> unique_products = all_products(mask)
unique_products =
40
48
12
24
120
30
60
36
72
18
If you have the Statistics Toolbox, you can abuse pdist, which considers only one of the two possible orders for each pair:
result = pdist(a(:), #times);
One option involves nchoosek, which returns all combinations of k elements out of a vector, each row is one combination. prod computes the product of rows or columns:
a = [4,10,12,3,6];
b = nchoosek(a,2);
b = prod(b,2); % 2 indicates rows
Try starting with this. Have the unique function filter out the result of multiplying a by itself.
b = unique(a*a')

Setting a specified number of rows in each column and block of elements to zero

Is there any easy way to set a specified number of rows k in nth column to zero ? Its a bit tricky question to explain so I guess its best to look at the example.
Lets say I have:
A =
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18
I wish to set rows, like this: [row1 col1] = 0; [row2 col1] = 0, and then [row3 col2]= 0; row4 col2]= 0 and so on, so my output is:
k = 2
B =
0 2 3
0 5 6
7 0 9
10 0 12
13 14 0
16 17 0
Do you have any suggestions/solutions how this could be solved with a for loop, or maybe there is another way?
and how this solution could be extend further to something like this:
A =
1 2 3 1 2 3
4 5 6 4 5 6
7 8 9 7 8 9
10 11 12 10 11 12
13 14 15 13 14 15
16 17 18 16 17 18
B =
0 0 3 1 2 3
0 0 6 4 5 6
7 8 0 0 8 9
10 11 0 0 3 1
13 14 15 13 0 0
16 17 18 16 0 0
One approach -
k = 2;
row1 = 1:size(A,1)
col1 = ceil([1:size(A,1)]./k)
A(sub2ind(size(A),row1,col1))=0
For the edited question, use kron like this -
k = 2;
a1 = eye(size(A)./k);
b1 = ones(k,k);
A(logical(kron(a1,b1)))=0
That isn't a problem. We can figure out exactly which rows and columns you want to set to 0 based on this value k, then use sub2ind to get a single index to access into your matrix. This will be in column-major format. Then you can use this and set all of your values to zero. Here is an example. We need to know the width and height of your matrix first before we do this:
rows = [row1 row2 row3];
cols = [col1 col2 col3];
%// Get column major indices
ind = sub2ind([height width], rows, cols);
%// Set the values in this matrix to 0.
B(ind) = 0;
Now with your example, we need to access all of the rows. However, for the columns, we need to access k elements in each column and ensuring they don't overlap. As such, we can do it like so:
k = 2;
B = reshape(1:18, 6, 3).';
rows = 1 : 6;
cols = ceil(rows / k);
ind = sub2ind([rows cols], rows, cols);
B(ind) = 0;
You would thus get:
B =
0 2 3
0 5 6
7 0 9
10 0 12
13 14 0
16 17 0
The following code is executable in matlab, and does what you want:
% Create A-matrix
A = reshape(1:18,6,3)
% Set specified datapoints to zero
A([1,2],[1,1]) = 0
Alternatively, you can set each element separately
A(1,1) = 0
A(1,2) = 0
And the most general way, with k and n:
A([1:k],n) = 0
For first question:
A(mod(0:numel(A)-1, size(A,1)+k) < k) = 0;
For second question, including first question as a particular case:
c = repmat({ones(k,size(B,2)/size(B,1)*k)}, size(B,1)/k, 1);
B = B.*~blkdiag(c{:})

"Find and replace" from a matrix MATLAB

I have a matrix that is like the following:
a = [10 0; 12 5; 10 0; 12 0; 15 0; 15 2];
a =
10 0
12 5
10 0
12 0
15 0
15 2
I am looking to create a new matrix which find and replaces the zeros with a value that is dependent on the first column's value. The key is this matrix:
Key =
10 100
12 200
15 300
If the value is already greater than zero in the first column I would like to leave it. The output would look like this:
Output =
10 100
12 5
10 100
12 200
15 300
15 2
You can do it in one line using logical indexing smartly:
a(~a(:,2),2)=arrayfun(#(x)Key(Key(:,1)==x,2),a(~a(:,2),1))
%a =
% 10 100
% 12 5
% 10 100
% 12 200
% 15 300
% 15 2

Comparing two matrix and took the value if match the criteria

I have some question. I have 2 matrix, it's have same size.
For example, first matrix :
1
1
0
0
1
0
Second matrix
34
56
12
12
33
14
Then, I want to compare this two matrix and groups it by the criteria on first matrix
so I will have this two groups matrix :
Matrix when the first matrix is have value 1
34
56
33
and
Matrix when the first matrix is have value 0
12
12
14
You could try this:
a = [1 1 0 0 1 0]';
b = [34 56 12 12 33 14]';
b(a==1)
b(a==0)

How can I perform this cumulative sum in MATLAB?

I want to calculate a cumulative sum of the values in column 2 of dat.txt below for each string of ones in column 1. The desired output is shown as dat2.txt:
dat.txt dat2.txt
1 20 1 20 20 % 20 + 0
1 22 1 22 42 % 20 + 22
1 20 1 20 62 % 42 + 20
0 11 0 11 11
0 12 0 12 12
1 99 1 99 99 % 99 + 0
1 20 1 20 119 % 20 + 99
1 50 1 50 169 % 50 + 119
Here's my initial attempt:
fid=fopen('dat.txt');
A =textscan(fid,'%f%f');
in =cell2mat(A);
fclose(fid);
i = find(in(2:end,1) == 1 & in(1:end-1,1)==1)+1;
out = in;
cumulative =in;
cumulative(i,2)=cumulative (i-1,2)+ cumulative(i,2);
fid = fopen('dat2.txt','wt');
format short g;
fprintf(fid,'%g\t%g\t%g\n',[out cumulative(:)]');
fclose(fid);
Here's a completely vectorized (albeit somewhat confusing-looking) solution that uses the functions CUMSUM and DIFF along with logical indexing to produce the results you want:
>> data = [1 20;... %# Initial data
1 22;...
1 20;...
0 11;...
0 12;...
1 99;...
1 20;...
1 50];
>> data(:,3) = cumsum(data(:,2)); %# Add a third column containing the
%# cumulative sum of column 2
>> index = (diff([0; data(:,1)]) > 0); %# Find a logical index showing where
%# continuous groups of ones start
>> offset = cumsum(index.*(data(:,3)-data(:,2))); %# An adjustment required to
%# zero the cumulative sum
%# at the start of a group
%# of ones
>> data(:,3) = data(:,3)-offset; %# Apply the offset adjustment
>> index = (data(:,1) == 0); %# Find a logical index showing where
%# the first column is zero
>> data(index,3) = data(index,2) %# For each zero in column 1 set the
%# value in column 3 to be equal to
data = %# the value in column 2
1 20 20
1 22 42
1 20 62
0 11 11
0 12 12
1 99 99
1 20 119
1 50 169
Not completely vectorized solution (it loops through the segments of sequential 1s), but should be faster. It's doing only 2 loops for your data. Uses MATLAB's CUMSUM function.
istart = find(diff([0; d(:,1)])==1); %# start indices of sequential 1s
iend = find(diff([d(:,1); 0])==-1); %# end indices of sequential 1s
dcum = d(:,2);
for ind = 1:numel(istart)
dcum(istart(ind):iend(ind)) = cumsum(dcum(istart(ind):iend(ind)));
end
dlmwrite('dat2.txt',[d dcum],'\t') %# write the tab-delimited file
d=[
1 20
1 22
1 20
0 11
0 12
1 99
1 20
1 50
];
disp(d)
out=d;
%add a column
out(:,3)=0;
csum=0;
for(ind=1:length(d(:,2)))
if(d(ind,1)==0)
csum=0;
out(ind,3)=d(ind,2);
else
csum=csum+d(ind,2);
out(ind,3)=csum;
end
end
disp(out)