Flexible Array in matlab - matlab

i have some cities, these cities have some neighbors and count of these neighbors are not similar.
i have a function:
function [cityN,neighbor,neghbor2:neghborN]] = makeneighbor(x,y)
cityN=x;
neighbor=y;
end % this function is false and i just told what is in my mind
for example:
//city1 have 2 neighbors:
[city1,neighbor1,neghbor2]
//but city2 have 4 neighbors:
[city2,neighbor1,neghbor2,neighbor3,neghbor4]
//and city3 have just a neighbor
[city3,neighbor1]
i need a flixeble array for this, many thanks...

You could use an adjacency matrix
cities = {'City1','City2', 'City3', 'City4'}
A =
0 1 0 1 (1 is neighbours with 2 and 4)
1 0 1 0 (2 is neighbours with 1 and 3)
0 1 0 0 (3 is only neighbours with 2)
1 0 0 0 (4 is only neighbours with 4)
(A should be of type logical)
Then, for any city, the list of neighbours is:
n = strfind('City1',cities);
neighbours = cities(A(n,:));
And the list with itself and neighbours would be
self_neighbours = [cities(n),cities(A(n,:))];
The count of number of neighbours is just:
num_neighbours = sum(A(n,:));
The advantages of keeping a list of who is neighbours with who in something like an adjacency matrix is that it makes it much easier to perform calculations. If you have access to the Bioinformatics Toolbox, you also then can use this to do various useful things:
b = biograph(A,cities); %makes biograph object
view(b); % shows connection between cities
[dist,path,pred] = shortestpath(b,1,3); % finds path between 1 and 3

Not sure is this is exactly what you need, but:
all_cities = {}
all_cities{end+1} = {'New York','Boston', 'Mscow'}
all_cities{end+1} = {'Moscow','Town1', 'St.Petersburg'}
All cities is a cell array that contains all cities. Every alement of this cell array is cell array too. Every nested cell array contains as a first element the main city, from the second element to the last one neighbors are stored.
Say, if we speak about New York,
new_york = all_cities{1};
new_york_neighbors = new_york{2:end};
You also should check if new_york_neighbors is empty while processing it. Use function isempty()

I am not quite sure if I understood your question. To handle different array sizes you could return a struct for your makeneighbor function with 1 field that contains the city name and a second field that contains a cell array with all neighborhoods:
function [] = main_func()
x1 = 'new york';
y1 = {'brooklyn'; 'queens'};
city1 = makeneighbor(x1, y1);
x2 = 'los angeles';
y2 = {'hollywood'; 'downtown'; 'mid-city'; 'bel air'};
city2 = makeneighbor(x2, y2);
% acces to cities
city1.name
city1.neighbor
city2.name
city2.neighbor
end
% function that returns a struct
function city = makeneighbor(x, y)
% generate struct with two fields
city.name = x;
city.neighbor = y;
end

Related

Simplify a Matlab code involving finding the position of maximum elements of array

I would like your help to make more efficient (maybe, by vectorising) the Matlab code below. The code below does essentially the following: take a row vector A; consider the maximum elements of such a row vector and let, for example, be i and j their positions; construct two columns vectors, the first with all zeros but a 1 positioned at i, the second with all zeros but a 1 positioned at j.
This is my attempt with loops, but it looks more complicated than needed.
clear
rng default
A=[3 2 3];
max_idx=ismember(A,max(A));
vertex=cell(size(A,2),1);
for j=1:size(max_idx,2)
if max_idx(j)>0
position=find(max_idx(j));
vertex_temp=zeros(size(A,2),1);
vertex_temp(position)=1;
vertex{j}=vertex_temp;
else
vertex{j}=[];
end
end
vertex=vertex(~cellfun('isempty',vertex));
Still using a for loop, but more readable:
A = [3 2 3];
% find max indices
max_idx = find(A == max(A));
vertex = cell(numel(max_idx),1);
for k = 1:numel(max_idx)
vertex{k} = zeros(size(A,2),1); % init zeros
vertex{k}(max_idx(k)) = 1; % set value in vector to 1
end
If you really wanted to avoid a for loop, you could probably also use something like this:
A=[3 2 3];
max_idx = find(A==max(A));
outMat = zeros(numel(A), numel(max_idx));
outMat((0:(numel(max_idx)-1)) * numel(A) + max_idx) = 1;
then optionally, if you want them in separate cells rather than columns of a matrix:
outCell = mat2cell(outMat, numel(A), ones(1,numel(max_idx)))';
However, I think this might be less simple and readable than the existing answers.
Is there a specific reason you want a cell array rather than a matrix?
If you can have it all in one vector:
A = [3 2 3]
B_rowvec = A == max(A)
B_colvec = B_rowvec'
If you need them separated into separate vectors:
A = [3 2 3]
Nmaxval = sum(A==max(A))
outmat = zeros(length(A),Nmaxval)
for i = 1:Nmaxval
outmat(find(A==max(A),i),i)=1;
end
outvec1 = outmat(:,1)
outvec2 = outmat(:,2)
Basically, the second input for find will specify which satisfactory instance of the first input you want.
so therefore
example = [ 1 2 3 1 2 3 1 2 3 ]
first = find(example == 1, 1) % returns 1
second = find(example == 1, 2) % returns 4
third = find(example == 1, 3) % returns 7

matlab return every second occurrence of value in vector

I have a vector with ID numbers that repeat an even number of times. I am only interested in every second time each number appears. I want to create a boolean mask that gives a true/1 for each second occurance of a number. I have already done this with a loop, but the actual vector will contain millions of elements, so the loop is too slow. I need a "vectorized" solution.
Here is an example Vector:
101
102
103
101
104
102
101
103
101
104
This should output the following mask:
0 (first occurrence of 101)
0 (first occurrence of 102)
0 (first occurrence of 103)
1 (second occurrence of 101)
0 (first occurrence of 104)
1 (second occurrence of 102)
0 (third occurrence of 101)
1 (second occurrence of 103)
1 (fourth occurrence of 101)
1 (second occurrence of 104)
You can do this very easily with a combination of unique and accumarray. First assign each value a unique ID, then bin all array locations together that are part of the same ID. You'll need to sort them as accumarray doesn't guarantee an order when you are binning things together. The output of this will be a cell array where each cell gives you the array locations that occurred for a particular index.
Once you do this, extract out every second element from each cell generated from accumarray, then use these to set all of the corresponding locations in a mask to 1. You can use a combination of cellfun, which can be used to process each cell individually and extracting every second element to create a new cell array, and vertcat which can be used to stack all of the cell arrays together into one final index array. This index array can be used to accomplish setting the locations in your mask to true:
%// Your data
V = [101,102,103,101,104,102,101,103,101,104];
%// Get list of unique IDs
[~,~,id] = unique(V,'stable');
%// Bin all of the locations in V together that belong to the
%// same bin
out = accumarray(id, (1:numel(V)).',[], #(x) {sort(x)}); %'
%// Extract out every second value that is for each bin
out2 = cellfun(#(x) x(2:2:end), out, 'uni', 0);
%// Create a mask and set the corresponding locations to true
mask = false(numel(V), 1);
mask(vertcat(out2{:})) = 1;
We get:
>> mask
mask =
0
0
0
1
0
1
0
1
1
1
Let's bsxfun it for a vectorized solution -
%// Assuming A as the input vector
M = bsxfun(#eq,A(:),unique(A(:).')) %//'
out = any(M - mod(cumsum(M,1).*M,2),2)
Here is one approach:
A = [101,102,103,101,104,102,101,103,101,104];
IDs = unique(A); % Find all the IDs present
test = arrayfun(#(x) find(A==x), IDs, 'UniformOutput', false); % Per ID, find where A == ID
repeatidx = cellfun(#(x) x(2:2:length(x)), test, 'UniformOutput', false); % Dump out the second match
repeatidx = cell2mat(repeatidx); % Flatten the cell array
B = false(size(A)); % Intialize output boolean array
B(repeatidx) = true; % Set true values based on repeatidx
Which returns:
B =
0 0 0 1 0 1 0 1 1 1

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

Creating Individual Matrix based on Label Matrix from DataMatrix

Let label be a matrix of size N x 1 (type double) and data be a matrix of size N x M (type double). The entries in the Label matrix looks like [ 1; 23; 135; ....; 6] which conveys that the
First row in the data matrix belongs to label 1
Second row in the data matrix belongs to label 2 and label 3
Third row in the data matrix belongs to label 1, label 3 and label 5 and so on
I would like to create a cell array say Individual{i} which stores all those rows from the data matrix which belongs to label i as given by the label matrix.
The resultant Individual{i} matrix will be size N_i x M.
Is there any efficient way to do the thing rather than looping row by row of data and label matrix?
I would turn your matrix label into a Boolean matrix L:
L = [ 1 0 0 0 0 0 ;
0 1 1 0 0 0 ;
1 0 1 0 1 0 ;
...
0 0 0 0 0 1 ];
for your example. You can use a sparse matrix if N or the number of labels is very large.
Then I think what you call N_i is sum(L(:, i)) and L' * data would compute the sum of all the rows in data with label L.
What do you want to do with the data once it's reached the Individual cell array? There's almost certainly a better way to do it...
Given the correct variables: N, M, data, label as you described, here's a sample code that creates the desired cell array Individual:
%# convert labels to binary-encoded format (as suggested by #Tom)
maxLabels = 9; %# maximum label number possible
L = false(N,maxLabels);
for i=1:N
%# extract digits of label
digits = sscanf(num2str(label(i)),'%1d');
%# all digits should be valid label indices
%assert( all(digits>=1) && all(digits<=maxLabels) );
%# mark this row as belong to designated labels
L(i,digits) = true;
end
%# distribute data rows according to labels
individual = cell(maxLabels,1);
for i=1:maxLabels
individual{i} = data(L(:,i),:);
end

matlab fxn: find contiguous regions and return bounds in struct array

This is half a question and half a challenge to the matlab gurus out there:
I'd like to have a function take in a logical array (false/true) and give the beginning and ending of all the contiguous regions containing trues, in a struct array.
Something like this:
b = getBounds([1 0 0 1 1 1 0 0 0 1 1 0 0])
should return
b = 3x1 struct array with fields:
beg
end
and
>> b(2)
ans =
beg: 4
end: 6
I already have an implementation, but I don't really know how to deal with struct arrays well so I wanted to ask how you would do it - I have to go through mat2cell and deal, and when I have to deal with much larger struct arrays it becomes cumbersome. Mine looks like this:
df = diff([0 foo 0]);
a = find(df==1); l = numel(a);
a = mat2cell(a',ones(1,l))
[s(1:l).beg] = deal(a{:});
b = (find(df==-1)-1);
b = mat2cell(b',ones(1,l))
[s(1:l).end] = deal(b{:});
I don't see why you are using mat2cell, etc. You are making too much of the problem.
Given a boolean row vector V, find the beginning and end points of all groups of ones in the sequence.
V = [1 0 0 1 1 1 0 0 0 1 1 0 0];
You get most of it from diff. Thus
D = diff(V);
b.beg = 1 + find(D == 1);
This locates the beginning points of all groups of ones, EXCEPT for possibly the first group. So add a simple test.
if V(1)
b.beg = [1,b.beg];
end
Likewise, every group of ones must end before another begins. So just find the end points, again worrying about the last group if it will be missed.
b.end = find(D == -1);
if V(end)
b.end(end+1) = numel(V);
end
The result is as we expect.
b
b =
beg: [1 4 10]
end: [1 6 11]
In fact though, we can do all of this even more easily. A simple solution is to always append a zero to the beginning and end of V, before we do the diff. See how this works.
D = diff([0,V,0]);
b.beg = find(D == 1);
b.end = find(D == -1) - 1;
Again, the result is as expected.
b
b =
beg: [1 4 10]
end: [1 6 11]
By the way, I might avoid the use of end here, even as a structure field name. It is a bad habit to get into, using matlab keywords as variable names, even if they are only field names.
This is what I went with:
df = diff([0 foo 0]);
s = struct('on',num2cell(find(df==1)), ...
'off',num2cell(find(df==-1)-1));
I forgot about num2cell and the nice behavior of struct with cell arrays.