Create matrix of row-wise increasing differences based on vector - matlab

I have a column vector in MATLAB and am trying to construct a matrix of differences with row-wise varying size of difference.
It is hard to explain in words, so I will illustrate with an example:
lets say my data is:
data = [ 1 2 3 4 5 6 ];
what i am trying to do, is make a matrix that takes the differences as such (each column difference size changes [increasing by one]):
diff =
[(2 - 1) ...
(3 - 2) (3 - 1) ...
(4 - 3) (4 - 2) (4 - 1) ...
(5 - 4) (5 - 3) (5 - 2) (5 - 1) ...
(6 - 5) (6 - 4) (6 - 3) (6- 2) (6 - 1)]
My best guess of doing this was to make a triangle matrix with nested loops. My MATLAB code looks like this:
differences = zeros(length(data) - 1, length(data) - 1);
step = 0;
for j = 1:1:size(data) - 1;
for i = 1:size(logquarterly) - 1 - step;
if j <= i;
differences(i,j) = data(i + 1 + step , 1) - data(i,1);
step = step + 1;
end
end
end
What I am trying to do is calculate the first column of differences with distance 1, then calculate the second column of differences with distance 2 and so on... To accommodate the necessary row values I need, I am using the "step" variable which is set to zero for calculating column one, I then want it to increase by 1 when calculating column 2 to have the correct dimensions. But I can not make it work. If I take the "step" out and use this:
differences = zeros(length(data) - 1, length(data) - 1);
for j = 1:1:size(data) - 1;
for i = 1:size(logquarterly) - 1;
if j <= i;
differences(i,j) = data(i + 1 , 1) - data(i,1);
end
end
end
everything works, but each column has the same distance of differences and it does not increase by one. Any ideas guys?

If I understand right, you want to do that:
data = [ 1 2 3 4 5 6 ];
n = numel(data);
%// calculate differences
diffs = bsxfun(#minus, data(end:-1:1), data(end:-1:1).')
%'
%// get linear indices from circulant sub-indices for rows and
%// linear indices for columns
idx = sub2ind([n n], gallery('circul',n:-1:1), ndgrid(1:n,1:n))
%// mask output and get lower triangular matrix
output = tril(diffs(idx(n-1:-1:1,n-1:-1:1)))
so the output is:
output =
1 0 0 0 0
1 2 0 0 0
1 2 3 0 0
1 2 3 4 0
1 2 3 4 5

The problem with your solution is that it will only work with column vectors, because of the loop j = 1:1:size(data)-1. The call of size will return [1,6]; then the -1 is applied yielding [0,5]. Then only the first value of this vector is taken and in turn the for loop will only run from 1 to 1-1==0, i.e. NOT.
Use numel or size(.,1)/size(.,2) instead. (Also don't use semicola ; after the loop initialization). (Try out the MATLAB debugger!)
Here is my take on how to repair your approach:
differences = zeros(length(data)-1, length(data)-1);
for j = 1:size(differences,2)
for i = j:size(differences,1)
differences(i,j) = data(i+1) - data(i-j+1);
end
end
I like the use of gallery('circul',n:-1:1), in thewaywewalk's answer, I do however find the rest a bit too complicated.
Here is my take reusing his idea:
n = numel(data);
L = ndgrid(2:n,2:n); % // Generate indices for Left side of operator
R = gallery('circul',1:n-1).'; %'// Generate indices for Right side of operator
out = tril(data(L) - data(R)) % // Do subtraction of corresponding indices

Related

MATLAB separating array [duplicate]

I'm trying to elegantly split a vector. For example,
vec = [1 2 3 4 5 6 7 8 9 10]
According to another vector of 0's and 1's of the same length where the 1's indicate where the vector should be split - or rather cut:
cut = [0 0 0 1 0 0 0 0 1 0]
Giving us a cell output similar to the following:
[1 2 3] [5 6 7 8] [10]
Solution code
You can use cumsum & accumarray for an efficient solution -
%// Create ID/labels for use with accumarray later on
id = cumsum(cut)+1
%// Mask to get valid values from cut and vec corresponding to ones in cut
mask = cut==0
%// Finally get the output with accumarray using masked IDs and vec values
out = accumarray(id(mask).',vec(mask).',[],#(x) {x})
Benchmarking
Here are some performance numbers when using a large input on the three most popular approaches listed to solve this problem -
N = 100000; %// Input Datasize
vec = randi(100,1,N); %// Random inputs
cut = randi(2,1,N)-1;
disp('-------------------- With CUMSUM + ACCUMARRAY')
tic
id = cumsum(cut)+1;
mask = cut==0;
out = accumarray(id(mask).',vec(mask).',[],#(x) {x});
toc
disp('-------------------- With FIND + ARRAYFUN')
tic
N = numel(vec);
ind = find(cut);
ind_before = [ind-1 N]; ind_before(ind_before < 1) = 1;
ind_after = [1 ind+1]; ind_after(ind_after > N) = N;
out = arrayfun(#(x,y) vec(x:y), ind_after, ind_before, 'uni', 0);
toc
disp('-------------------- With CUMSUM + ARRAYFUN')
tic
cutsum = cumsum(cut);
cutsum(cut == 1) = NaN; %Don't include the cut indices themselves
sumvals = unique(cutsum); % Find the values to use in indexing vec for the output
sumvals(isnan(sumvals)) = []; %Remove NaN values from sumvals
output = arrayfun(#(val) vec(cutsum == val), sumvals, 'UniformOutput', 0);
toc
Runtimes
-------------------- With CUMSUM + ACCUMARRAY
Elapsed time is 0.068102 seconds.
-------------------- With FIND + ARRAYFUN
Elapsed time is 0.117953 seconds.
-------------------- With CUMSUM + ARRAYFUN
Elapsed time is 12.560973 seconds.
Special case scenario: In cases where you might have runs of 1's, you need to modify few things as listed next -
%// Mask to get valid values from cut and vec corresponding to ones in cut
mask = cut==0
%// Setup IDs differently this time. The idea is to have successive IDs.
id = cumsum(cut)+1
[~,~,id] = unique(id(mask))
%// Finally get the output with accumarray using masked IDs and vec values
out = accumarray(id(:),vec(mask).',[],#(x) {x})
Sample run with such a case -
>> vec
vec =
1 2 3 4 5 6 7 8 9 10
>> cut
cut =
1 0 0 1 1 0 0 0 1 0
>> celldisp(out)
out{1} =
2
3
out{2} =
6
7
8
out{3} =
10
For this problem, a handy function is cumsum, which can create a cumulative sum of the cut array. The code that produces an output cell array is as follows:
vec = [1 2 3 4 5 6 7 8 9 10];
cut = [0 0 0 1 0 0 0 0 1 0];
cutsum = cumsum(cut);
cutsum(cut == 1) = NaN; %Don't include the cut indices themselves
sumvals = unique(cutsum); % Find the values to use in indexing vec for the output
sumvals(isnan(sumvals)) = []; %Remove NaN values from sumvals
output = {};
for i=1:numel(sumvals)
output{i} = vec(cutsum == sumvals(i)); %#ok<SAGROW>
end
As another answer shows, you can use arrayfun to create a cell array with the results. To apply that here, you'd replace the for loop (and the initialization of output) with the following line:
output = arrayfun(#(val) vec(cutsum == val), sumvals, 'UniformOutput', 0);
That's nice because it doesn't end up growing the output cell array.
The key feature of this routine is the variable cutsum, which ends up looking like this:
cutsum =
0 0 0 NaN 1 1 1 1 NaN 2
Then all we need to do is use it to create indices to pull the data out of the original vec array. We loop from zero to max and pull matching values. Notice that this routine handles some situations that may arise. For instance, it handles 1 values at the very beginning and very end of the cut array, and it gracefully handles repeated ones in the cut array without creating empty arrays in the output. This is because of the use of unique to create the set of values to search for in cutsum, and the fact that we throw out the NaN values in the sumvals array.
You could use -1 instead of NaN as the signal flag for the cut locations to not use, but I like NaN for readability. The -1 value would probably be more efficient, as all you'd have to do is truncate the first element from the sumvals array. It's just my preference to use NaN as a signal flag.
The output of this is a cell array with the results:
output{1} =
1 2 3
output{2} =
5 6 7 8
output{3} =
10
There are some odd conditions we need to handle. Consider the situation:
vec = [1 2 3 4 5 6 7 8 9 10 11 12 13 14];
cut = [1 0 0 1 1 0 0 0 0 1 0 0 0 1];
There are repeated 1's in there, as well as a 1 at the beginning and end. This routine properly handles all this without any empty sets:
output{1} =
2 3
output{2} =
6 7 8 9
output{3} =
11 12 13
You can do this with a combination of find and arrayfun:
vec = [1 2 3 4 5 6 7 8 9 10];
N = numel(vec);
cut = [0 0 0 1 0 0 0 0 1 0];
ind = find(cut);
ind_before = [ind-1 N]; ind_before(ind_before < 1) = 1;
ind_after = [1 ind+1]; ind_after(ind_after > N) = N;
out = arrayfun(#(x,y) vec(x:y), ind_after, ind_before, 'uni', 0);
We thus get:
>> celldisp(out)
out{1} =
1 2 3
out{2} =
5 6 7 8
out{3} =
10
So how does this work? Well, the first line defines your input vector, the second line finds how many elements are in this vector and the third line denotes your cut vector which defines where we need to cut in our vector. Next, we use find to determine the locations that are non-zero in cut which correspond to the split points in the vector. If you notice, the split points determine where we need to stop collecting elements and begin collecting elements.
However, we need to account for the beginning of the vector as well as the end. ind_after tells us the locations of where we need to start collecting values and ind_before tells us the locations of where we need to stop collecting values. To calculate these starting and ending positions, you simply take the result of find and add and subtract 1 respectively.
Each corresponding position in ind_after and ind_before tell us where we need to start and stop collecting values together. In order to accommodate for the beginning of the vector, ind_after needs to have the index of 1 inserted at the beginning because index 1 is where we should start collecting values at the beginning. Similarly, N needs to be inserted at the end of ind_before because this is where we need to stop collecting values at the end of the array.
Now for ind_after and ind_before, there is a degenerate case where the cut point may be at the end or beginning of the vector. If this is the case, then subtracting or adding by 1 will generate a start and stopping position that's out of bounds. We check for this in the 4th and 5th line of code and simply set these to 1 or N depending on whether we're at the beginning or end of the array.
The last line of code uses arrayfun and iterates through each pair of ind_after and ind_before to slice into our vector. Each result is placed into a cell array, and our output follows.
We can check for the degenerate case by placing a 1 at the beginning and end of cut and some values in between:
vec = [1 2 3 4 5 6 7 8 9 10];
cut = [1 0 0 1 0 0 0 1 0 1];
Using this example and the above code, we get:
>> celldisp(out)
out{1} =
1
out{2} =
2 3
out{3} =
5 6 7
out{4} =
9
out{5} =
10
Yet another way, but this time without any loops or accumulating at all...
lengths = diff(find([1 cut 1])) - 1; % assuming a row vector
lengths = lengths(lengths > 0);
data = vec(~cut);
result = mat2cell(data, 1, lengths); % also assuming a row vector
The diff(find(...)) construct gives us the distance from each marker to the next - we append boundary markers with [1 cut 1] to catch any runs of zeros which touch the ends. Each length is inclusive of its marker, though, so we subtract 1 to account for that, and remove any which just cover consecutive markers, so that we won't get any undesired empty cells in the output.
For the data, we mask out any elements corresponding to markers, so we just have the valid parts we want to partition up. Finally, with the data ready to split and the lengths into which to split it, that's precisely what mat2cell is for.
Also, using #Divakar's benchmark code;
-------------------- With CUMSUM + ACCUMARRAY
Elapsed time is 0.272810 seconds.
-------------------- With FIND + ARRAYFUN
Elapsed time is 0.436276 seconds.
-------------------- With CUMSUM + ARRAYFUN
Elapsed time is 17.112259 seconds.
-------------------- With mat2cell
Elapsed time is 0.084207 seconds.
...just sayin' ;)
Here's what you need:
function spl = Splitting(vec,cut)
n=1;
j=1;
for i=1:1:length(b)
if cut(i)==0
spl{n}(j)=vec(i);
j=j+1;
else
n=n+1;
j=1;
end
end
end
Despite how simple my method is, it's in 2nd place for performance:
-------------------- With CUMSUM + ACCUMARRAY
Elapsed time is 0.264428 seconds.
-------------------- With FIND + ARRAYFUN
Elapsed time is 0.407963 seconds.
-------------------- With CUMSUM + ARRAYFUN
Elapsed time is 18.337940 seconds.
-------------------- SIMPLE
Elapsed time is 0.271942 seconds.
Unfortunately there is no 'inverse concatenate' in MATLAB. If you wish to solve a question like this you can try the below code. It will give you what you looking for in the case where you have two split point to produce three vectors at the end. If you want more splits you will need to modify the code after the loop.
The results are in n vector form. To make them into cells, use num2cell on the results.
pos_of_one = 0;
% The loop finds the split points and puts their positions into a vector.
for kk = 1 : length(cut)
if cut(1,kk) == 1
pos_of_one = pos_of_one + 1;
A(1,one_pos) = kk;
end
end
F = vec(1 : A(1,1) - 1);
G = vec(A(1,1) + 1 : A(1,2) - 1);
H = vec(A(1,2) + 1 : end);

Shuffle a vector of repeated numbers so the numbers do not repeat in MATLAB

Okay, so I have a script that will produce my vector of repeated integers of a certain interval, but now theres a particular instance where I need to make sure that once it is shuffled, the numbers do not repeat. So for example, I produced a vector of repeating 1-5, 36 times, shuffled. How do I ensure that there are no repeated numbers after shuffling? And to make things even more complex, I need to produce two such vectors that do not ever have the same value at the same index. For example, lets say 1:5 was repeated twice for these vectors, so then this would be what I'm looking for:
v1 v2
4 2
2 4
3 2
5 3
4 5
1 4
5 1
1 5
3 1
2 3
I made that right now by taking an example of 1 vector and just shifting it off by 1 to create another vector that will satisfy the requirements, but in my situation, that wont actually work because I can't have them be systematically dependent like that.
So I tried a recursive technique to make the script start over if the vectors did not make the cut and as expected, that did not go over so well. I hit my maximum recursive iterations and I've realized this is clearly not the way to go. Is there some other alternative?
EDIT:
So I found a way to satisfy some of the conditions I needed above in the following code:
a = nchoosek(1:5,2);
b = horzcat(a(:,2),a(:,1));
c = vertcat(a,b);
cols = repmat(c,9,1);
cols = cols(randperm(180),:);
I just need to find a way to shuffle cols that will also enforce no repeating numbers in columns, such that cols(i,1) ~= cols(i+1,1) and cols(i,2) ~= cols(i+1,2)
This works, but it probably is not very efficient for a large array:
a = nchoosek(1:5, 2);
while (any(a(1: end - 1, 1) == a(2: end, 1)) ...
|| any(a(1: end - 1, 2) == a(2: end, 2)))
random_indices = randperm(size(a, 1));
a = a(random_indices, :);
end
a
If you want something faster, the trick is to logically insert each row in a place where your conditions are satisfied, rather than randomly re-shuffling. For example:
n1 = 5;
n2 = 9;
a = nchoosek(1:n1, 2);
b = horzcat(a(:,2), a(:,1));
c = vertcat(a, b);
d = repmat(c, n2, 1);
d = d(randperm(n1 * n2), :);
% Perform an "insertion shuffle"
for k = 2: n1 * n2
% Grab row k from array d. Walk down the rows until a position is
% found where row k does not repeat with its upstairs or downstairs
% neighbors.
m = 1;
while (any(d(k,:) == d(m,:)) || any(d(k,:) == d(m+1,:)))
m = m + 1;
end
% Insert row k in the proper position.
if (m < k)
ind = [ 1: m k m+1: k-1 k+1: n1 * n2 ];
else
ind = [ 1: k-1 k+1: m k m+1: n1 * n2 ];
end
d = d(ind,:);
end
d
One way to solve this problem is to think both vectors as being created as follows:
For every row of arrays v1 and v2
Shuffle the array [1 2 3 4 5]
Set the values of v1 and v2 at the current row with the first and second value of the shuffle. Both values will always be different.
Code:
s = [1 2 3 4 5];
Nrows = 36;
solution = zeros(Nrows,2);
for k=1:Nrows
% obtain indexes j for shuffling array s
[x,j] = sort(rand(1,5));
%row k takes the first two values of shuffled array s
solution(k,1:2) = s(j(1:2));
end
v1 = solution(:,1);
v2 = solution(:,2);
Main edit: random => rand,
With this method there is no time wasted in re-rolling repeated numbers because the first and second value of shuffling [1 2 3 4 5] will always be different.
Should you need more than two arrays with different numbers the changes are simple.

Matlab - insert/append rows into matrix iteratively

How in matlab I can interactively append matrix with rows?
For example lets say I have empty matrix:
m = [];
and when I run the for loop, I get rows that I need to insert into matrix.
For example:
for i=1:5
row = v - x; % for example getting 1 2 3
% m.append(row)?
end
so after inserting it should look something like:
m = [
1 2 3
3 2 1
1 2 3
4 3 2
1 1 1
]
In most programming languages you can simply append rows into array/matrix. But I find it hard to do it in matlab.
m = [m ; new_row]; in your loop. If you know the total row number already, define m=zeros(row_num,column_num);, then in your loop m(i,:) = new_row;
Just use
m = [m; row];
Take into account that extending a matrix is slow, as it involves memory reallocation. It's better to preallocate the matrix to its full size,
m = NaN(numRows,numCols);
and then fill the row values at each iteration:
m(ii,:) = row;
Also, it's better not to use i as a variable name, because by default it represents the imaginary unit (that's why I'm using ii here as iteration index).
To create and add a value into the matrix you can do this and can make a complete matrix like yours.
Here row = 5 and then column = 3 and for hence two for loop.
Put the value in M(i, j) location and it will insert the value in the matrix
for i=1:5
for j=1:3
M(i, j) = input('Enter a value = ')
end
fprintf('Row %d inserted successfully\n', i)
end
disp('Full Matrix is = ')
disp(M)
Provably if you enter the same values given, the output will be like yours,
Full Matrix is =
1 2 3
3 2 1
1 2 3
4 3 2
1 1 1

Generate random non-repeating integers from a small range

What I'm trying to accomplish is the following:
I wish to create a vector of integers, from a relatively small range, and ensure that none of the integers will be followed by the same integer.
i.e., This is a "legal" vector:
[ 1 3 4 2 5 3 2 3 5 4 ]
and this is an "illegal" vector (since 5 follows 5):
[ 1 3 4 2 5 5 2 3 5 4 ]
I've experimented with randi, and all sorts of variations with randperm, and I always get stuck when i try to generate a vector of around 100 elements, from a small range (i.e., integers between 1 and 5).
The function just runs for too long.
Here's one of the attempts that i've made:
function result = nonRepeatingRand(top, count)
result = randi(top, 1, count);
while any(diff(result) == 0)
result = randi(top, 1, count);
end
end
Any and all help will be much appreciated. Thanks !
The kind of sequence you are looking for can be defined by generating differences from 1 to top - 1 and then computing the cumulative sum modulus top, starting from a random initial value:
function result = nonRepeatingRand(top, count)
diff = randi(top - 1, 1, count);
result = rem(cumsum(diff) + randi(1, 1, count) - 1, top) + 1;
end
On my machine, this generates a non-repeating sequence of 10 million numbers out of 1:5 in 0.58 seconds.
you can use the following code for generate Non Repeating Random Numbers from 1 to M
randperm(M);
and for K Non Repeating Random Numbers from 1 to M
randperm(M, K);
enjoy
Do not regenerate the sequence every time, but fix the repetitions. E.g.:
function result = nonRepeatingRand(top, count)
result = randi(top, 1, count);
ind = (diff(result) == 0);
while any(ind)
result(ind) = [];
result(end + 1 : count) = randi(top, 1, count - numel(result));
ind = (diff(result) == 0);
end
end
On my machine, this generates a non-repeating sequence of 10 million numbers out of 1:5 in 1.6 seconds.
Taking the idea from A. Donda but fixing the implementation:
r=[randi(top,1,1),randi(top - 1, 1, count-1)];
d=rem(cumsum(r)-1,top)+1;
The first element of r is a randomly chosen element to start with. The following elements of r randomly choose the difference to the previous element, using modulo arithmetic.
How this?
top = 5;
count = 100;
n1 = nan;
out = [];
for t = 1: count
n2 = randi(top);
while n1 == n2
n2 = randi(top);
end
out = [out, n2];
n1 = n2;
end

Matlab: multiple Assignment for vectors without Loop

is there any possibility to assign multiple values for a matrix from an another vector without a loop?
For example:
I have a matrix filled with zeros:
matrix=zeros(2);
matrix =
0 0
0 0
Now i have an another vector where the first two columns are the positions and the third column are the values wich belongs to the corresponding positions.
values=[2 1 4;1 2 2]
values =
Posx PosY Value
2 1 4
1 2 2
The result should look like:
matrix =
0 2 <-- matrix(values(2,1),values(2,2))=values(2,3) ;
4 0 <-- matrix(values(1,1),values(1,2))=values(1,3);
This isn't pretty, but it is a one liner:
matrix(size(matrix,1) * (values(:,2) - 1) + values(:,1)) = values(:,3)
I can make it a bit clearer by splitting it into two lines. The idea is that you transform the first two columns of values into a one dimensional indexing vector which has as many elements as there are values to be assigned, and then assign values:
index = size(matrix,1) * (values(:,2) - 1) + values(:,1)
matrix(index) = values(:,3)
When you index into a matrix with a vector it counts down the columns first, and then across the rows. To make it even more clear, split the first statement up some more:
numRows = size(matrix,1)
rowIndex = values(:,1)
colIndex = values(:,2)
vals = values(:,3)
index = numRows * (colIndex - 1) + rowIndex
matrix(index) = vals
In fact, you don't need to go through all the trouble of building the index vector, as the function sub2ind exists to do that for you:
index = sub2ind(size(matrix), rowIndex, colIndex)
matrix(index) = vals
although I think it's good to see how to get the results with a call to sub2index, for your own education.
I made a function to do that, you can use it, if you want:
function B = ndassign( A , varargin )
%%% copy A to B, and assign values to A at specified nd indexes
%%% B=ndind(A,X,Y,Z,V)
%%% ---> B(X(i),Y(i),Z(i))=V(i)
%%% Example:
%%% ndassign(eye(3),[1 2 3],[3 2 1],[4 5 6])
%%% ans =
%%% 1 0 4
%%% 0 5 0
%%% 6 0 1
B=A;
inds=sub2ind(size(A),varargin{1:end-1});
B(inds)=varargin{end};
end