MATLAB's treeplot: align by node height from top - matlab

Here's what I get by using the treeplot function on MATLAB (this is the example image):
Here's what I'd like to get:
As you can see, I'd like to have the position of each node according to its distance from the root. Is that possible?

I was also looking for a "root-aligned" treeplot in Matlab and found no solution. Here is what I came up with, in case someone is still in need of it (I'm using the same example as in the Matlab documentation):
nodes = [0 1 2 2 4 4 4 1 8 8 10 10];
At first we need to get the x and y coordinates of every node in the original tree plot and find all leaves in it:
[x,y] = treelayout(nodes);
leaves = find( y == min(y) );
Next, we reconstruct every chain in the tree plot and store it in a matrix (by doing so, we can later change the y position of the nodes):
num_layers = 1/min(y)-1;
chains = zeros(num_layers, length(leaves));
for l=1:length(leaves)
index = leaves(l);
chain = [];
chain(1) = index;
parent_index = nodes(index);
j = 2;
while (parent_index ~= 0)
chain(j) = parent_index;
parent_index = nodes(parent_index);
j = j+1;
end
chains(:,l) = padarray(flip(chain), [0, num_layers-length(chain)], 'post');
end
Now we compute the new y-coordinates determined by the row index in the matrix and dependent on the number of layers in the tree:
y_new = zeros(size(y));
for i=1:length(nodes)
[r,c] = find(chains==i, 1);
y_new(i) = max(y) - (r-1)*1/(num_layers+1);
end
We can now plot the re-positioned nodes and add the connecting lines:
plot(x, y_new, 'o');
hold on
for c=1:size(chains, 2)
line_x = x(chains(chains(:,c)>0, c));
line_y = y_new(chains(chains(:,c)>0, c));
line(line_x, line_y);
end
If you like, you can also add the node labels to the plot:
for t=1:length(nodes)
text(x(t)+0.025, y_new(t), num2str(t));
end
xlim([0 1]);
ylim([0 1]);
The resulting figure looks as follows:

Related

Drawing rectangles around vertical groups of pixels having the same value

Consider the following visualization of a 7x5 matrix consisting of 3 distinct regions/values:
bL = toeplitz( [zeros(1,5) -2*ones(1,2)], [0 -ones(1,4)] );
hF = figure(); hA = axes(hF);
imagesc(hA,bL); axis(hA,'image'); set(hA,'XTick',[],'YTick',[]);
N = 4; cmap = parula(N); colormap(cmap(1:end-1,:));
Now let's say I "select" 0 or more pixels in each column such that:
Selected pixels can only be chosen in the green region.
Selected pixels are always contiguous.
Selection is performed by assigning a constant new value, which is different from the 3 initial regions.
Several examples of selection (using the value 1):
%Example 1:
cSF = toeplitz([ones(1,1) zeros(1,4) -2*ones(1,2)],[1 -ones(1,4)]);
%Example 2:
oSF = toeplitz( [zeros(1,5) -2*ones(1,2)], [0 -ones(1,4)] );
oSF(end-2:end,find(any(oSF==-2,1),1,'last')+1:end) = 1;
%Example 3:
iSF = toeplitz([ones(1,3) zeros(1,2) -2*ones(1,2)],[1 -ones(1,4)]);
% Plot:
hF = figure();
hP(1) = subplot(1,3,1); imagesc(cSF);
hP(2) = subplot(1,3,2); imagesc(oSF);
hP(3) = subplot(1,3,3); imagesc(iSF);
axis(hP,'image'); set(hP,'XTick',[],'YTick',[]);
My objective is to draw a set of rectangles encompassing "selected" (yellow) pixels belonging to the same column. For the examples above, the results should look like this (respectively):
The way I see it, for the code to be general it should accept: (1) an axes handle where the imagesc should be plotted; (2) a data array; (3) a value found in the data array, representing "chosen" pixels; and optionally the color of the enclosed pixels.
I found some ways of doing this using patch and rectangle (see own answer), but I'm wondering if this can be achieved with fewer function calls or in other ways I hadn't thought of.
Loopless solution using patch:
Here's a solution that generates coordinates for patch without needing a loop:
function column_highlight(hA, data, selectionVal)
assert(nargin >= 2);
if (nargin < 3) || isempty(selectionVal)
selectionVal = 1;
end
nCol = size(data, 2);
data = diff([false(1, nCol); (data == selectionVal); false(1, nCol)]);
[r, c] = find(data);
r = reshape(r-0.5, 2, []);
c = c(1:2:end);
X = [c-0.5 c+0.5 c+0.5 c-0.5].';
Y = r([1 1 2 2], :);
patch(hA, 'XData', X, 'YData', Y, 'FaceColor', 'none');
end
Solution using regionprops:
If you have the Image Processing Toolbox, you can solve this by labeling each masked column section and getting the 'BoundingBox' shape measure using regionprops:
function column_highlight(hA, data, selectionVal)
assert(nargin >= 2);
if (nargin < 3) || isempty(selectionVal)
selectionVal = 1;
end
labelMat = bsxfun(#times, (data == selectionVal), 1:size(data, 2));
coords = regionprops(labelMat, 'BoundingBox');
coords = vertcat(coords.BoundingBox);
coords(:, 3:4) = coords(:, 1:2)+coords(:, 3:4);
X = coords(:, [1 3 3 1]).';
Y = coords(:, [4 4 2 2]).';
patch(hA, 'XData', X, 'YData', Y, 'FaceColor', 'none');
end
A solution using rectangle:
function markStreaksRect(hA, data, selectionVal)
% Check inputs:
assert(nargin >= 2); if nargin < 3 || isempty(selectionVal), selectionVal = 1; end
% Create a mask for "selected" values:
oneMask = data == selectionVal;
% Find the first encountered "selected" element from both the top and the bottom:
[~,I1] = max(oneMask,[],1); [~,I2] = max(flipud(oneMask),[],1);
% Express the "selected" extent as a 2 row vector:
firstLast = [I1; size(oneMask,1)-I2+1].*any(oneMask,1);
% For nonzero extents, plot shifted rectangles:
for ind1 = find(all(firstLast,1))
rectangle(hA,'Position',[ind1-0.5, firstLast(1,ind1)-0.5, 1, diff(firstLast(:,ind1))+1 ]);
end
A solution using patch:
function markStreaksPatch(hA, data, selectionVal)
% Check inputs:
assert(nargin >= 2); if nargin < 3 || isempty(selectionVal), selectionVal = 1; end
% Create a mask for "selected" values:
oneMask = data == selectionVal;
% Find the first encountered "selected" element from both the top and the bottom:
[~,I1] = max(oneMask,[],1); [~,I2] = max(flipud(oneMask),[],1);
% Express the "selected" extent as a 2 row vector:
firstLast = [I1; size(oneMask,1)-I2+1].*any(oneMask,1);
% For nonzero extents, plot shifted patches:
for ind1 = find(all(firstLast,1))
[XX,YY] = meshgrid(ind1-0.5 + [0 1], firstLast(1,ind1)-0.5+[0 diff(firstLast(:,ind1))+1]);
patch(hA, XX(:), [YY(1:2) YY(4:-1:3)], 'y', 'FaceAlpha', 0);
end
The above solutions can be tested using:
function q45965920
iSF = toeplitz([ones(1,3) zeros(1,2) -2*ones(1,2)],[1 -ones(1,4)]);
hF = figure(); hA = axes(hF); imagesc(hA,iSF);
axis(hA,'image'); set(hA,'XTick',[],'YTick',[]);
...then running either markStreaksRect(hA, iSF, 1); or markStreaksPatch(hA, iSF, 1); produces the desired result.

Finding the most common point of intersection among plotted triangles

I plotted a set of triangles using the code below:
A=[1, 1; 1, 5; 3, 9; 4, 2;9,9];
plot(A(:,1),A(:,2),'oc','LineWidth',2,'MarkerSize',5);
axis([0 10 0 10]);
grid on
for ii = 1:size(A, 1) - 1
for jj = ii + 1:size(A, 1)
line([A(ii, 1), A(jj, 1)], [A(ii, 2), A(jj, 2)])
end
end
The problem is, i will like the plot to indicate the region with the highest number of intersections. In this particular code, the region is the black polygon (i had to indicate this region manually).
Please can anyone help out with this problem. Thanks
Here is a variant with a more graphical approach.
Create a grid of points
Check the number of triangles that a point
is inside
Plot the points with highest number of intersecting
triangles
The code
% Create the combination of all points that make the triangles
% This could be used to plot the lines as well
N = size(A,1);
comb = [];
for i = 1:N-2
for j = i+1:N-1
comb = [comb; repmat([i j], N-j,1) (j+1:N)']; %#ok<AGROW>
end
end
nComb = size(comb,1);
% Create a mesh grid
dg = 0.1; % Resolution - tune this!
gridEdge = [min(A);max(A)];
[X, Y] = meshgrid(gridEdge(1,1):dg:gridEdge(2,1), gridEdge(1,2):dg:gridEdge(2,2));
% Check if a point is inside each triangle
[isInside, onEdge] = deal(zeros(numel(X),nComb));
for i = 1:nComb
[isInside(:,i), onEdge(:,i)] = inpolygon(X(:),Y(:),A(comb(i,:),1),A(comb(i,:),2));
end
% Remove points on edge
isInside = isInside - onEdge;
% Get index of points with most intersection
inTri = sum(isInside,2);
idx = find(inTri == max(inTri));
% Plot result
hold on
plot(X(idx),Y(idx),'.')
text(mean(X(idx)),mean(Y(:)),num2str(max(inTri)),'FontSize',20)

2D Plotting a 3D point cloud as set of lines

I have a three dimensional array of points.
I need to plot them on a 2D [x,z] grid, where every line is based on the value of Y inside a range.
es. the first line is made by points with 0 < y < 1, the second with 1 < y < 2, etc..
I can plot points just fine using this script(to have a better looking graph i'm also changing the color for every set of point by moving inside an rgb triplet).
set is the range of the line (in my case Y is time and i need to plot every 0.1 seconds). i is the index for my array of points.
w= 0;
yend = 100;
set = 0.1;
numberOfColors = 1/2*(Yinterval)*(1/set);
incrementOfColor = 1/numberOfColors;
red = 0;
green = 0;
blue = 1;
color=[red green blue];
while w < yend
while y(i)>w&&y(i)<w+set
figure(1)
hold on
plot(x(i),z(i),'.','Color',color);
hold on
i=i+1;
end
w=w+set;
if red < 1-incrementOfColor && blue > 0
red = red + incrementOfColor;
blue = blue - incrementOfColor;
end
if red > incrementOfColor && blue < incrementOfColor
red = red-incrementOfColor;
green = green + incrementOfColor;
blue = 0;
end
color = [red green blue];
end
And this is the result:
http://i.imgur.com/HTyzWai.png
When there's too much data the plot becomes cumbersome to read, and with too little data isolated points don't really tell much.
I've tried converting the points inside a "set" interval into an array but it really didn't work out:
while w < yend
while y(i)>w&&y(i)<w+set
vX(a,:)=x(i);
vZ(a,:)=z(i);
i=i+1;
a=a+1;
end
figure(2)
hold on
plot(vX,vZ,'-','Color',color);
Gave this result: imgur.com/S7OasUn.jpg
Is my conversion wrong or arrays are just not the proper way to handle this? If so, what should I use?
I'm rather new to matlab so if this is really obvious or i'm asking something in the wrong way, please excuse me.
edit:
This is the result i want to achieve:
imgur.com/jPZTO8E.png
and it was obtained with Origin's contour profile tool.
I guess you mean this with functional example:
myPoints[i]:
i=0, x = 1, y= 0.03, z = 1
i=1, x = 2, y= 0.06, z = 3
i=2, x = 2.5, y = 0.09, z = 4
i=3, x = 1.2, y = 1.01, z = 3.1
i=4, x = 1.3, y = 1.04, z = 1.1
i=5, x = 1.2, y = 1.06, z = 2.5
i=6, x = 2, y = 1.09, z = 3.1
i=7, x = 1.2, y = 2.02, z = 3.1
etc..
i want the points i 0,1,2 plotted as one line, i 3,4,5 as another line and i 7 etc.. plotted as a third line. In my data i have a lot of points.
this is one example of the data i'm using.
https://drive.google.com/file/d/0B9GHAkIYepQcRXdBdy03dFJtT1k/view?usp=sharing
where:
x = myPoints(:,1);
y = myPoints(:,2);
z = myPoints(:,3);
In this specific case, i only care about the points with y value between 85.85 and 90.85, and i want to "place an edge" (?) every 0.1
x = myPoints(:,1);
y = myPoints(:,2);
z = myPoints(:,3);
ymin = 85.85;
ymax = 90.85;
set = 0.1;
nbins = (ymax-ymin)/set;
binedge = linspace(ymin, ymax, (nbins + 1));
I think I'm understanding what you're attempting to accomplish and I believe you can accomplish your task with a vectorized use of plot rather than having to loop through each of your points.
Given your example data we can give this method a shot:
load('mypoints.mat');
x = myPoints(:,1);
y = myPoints(:,2);
z = myPoints(:,3);
% Bin your data by y values
ymin = 85.85;
ymax = 90.85;
set = 0.1;
nbins = (ymax-ymin)/set;
binedge = linspace(ymin, ymax, (nbins + 1));
[~, ~, binidx] = histcounts(y, binedge);
binloop = unique(binidx);
% Fix for error with unique. If you try to use a for loop with a column
% vector it passes the whole vector as your iterator, so you need to
% transpose it to a row vector.
temp = size(binloop);
if temp(1) ~= 1
binloop = binloop';
end
% Plot
figure
hold on
for ii = binloop
if ii == 0
% These fall outside of our bins, don't plot them
continue
else
dataidx = find(binidx == ii);
% Plot has its own color cycle, or you can add your color changing logic to this loop
plot(x(dataidx), z(dataidx), '-', 'LineWidth', 0.25);
end
end
hold off
What I've done here is to utilize the third output of histcounts to bin your y data and return a bin index for each value in your y vector.
To break the code down a bit:
Define your bin boundaries:
ymin = 85.85;
ymax = 90.85;
set = 0.1;
nbins = (ymax-ymin)/set;
binedge = linspace(ymin, ymax, (nbins + 1));
The linspace call returns the edges of each data bin. Given your example data we get binedge = [86.85, 86.95, 86.05, ..., 90.85]. We then plug in these edges and your y vector to histcounts to obtain our bin indices:
[~, ~, binidx] = histcounts(y, binedge);
Where the bins are defined per the documentation:
The value X(i) is in the kth bin if edges(k) ≤ X(i) < edges(k+1). The
last bin also includes the right bin edge, so that it contains X(i) if
edges(end-1) ≤ X(i) ≤ edges(end).
We can use unique to loop through all of the bins that are present. We can utilize find with MATLAB's logical indexing to plot all data from a single bin in each loop iteration. I ignore where ii == 0 as it indicates where data does not fall under one of the bins (and 0 index is meaningless in MATLAB).
I also played around a bit with the line sizes to try and make the plot a bit easier to read. There's a lot of data to visualize here but it helps a bit. You'll need to do a bit of pruning of your data since your x values wrap back, but that should be trivial.

Matlab Recreating freqz, normalizing x axis and getting half of plot

I have a function which is basically recreating the freqz command in matlab. I have figured out how to plot the entire transform of my frequency response, but I only need half of it, and I need to normalize it from pi to 1 (where 0:pi represents my x axis, and I want that to go to 0:1). The code is here:
function freqrespplot(b, a)
hb = fft(b,512);
ha = fft(a,512);
magh = (abs(hb) ./ abs(ha));
angh = angle(hb ./ ha);
x = linspace(0,2*pi, 512);
subplot(2,1,1);
plot(x,magh,'g');
subplot(2,1,2);
plot(x,angh,'r');
end
In the example of b = [1 2 2], a = [0 1 .8], plots like the following:
freqrespplot([1 2 2], [0 1 .8]);
Magnitude
and my corresponding phase plot is
I want the x-axis (omega) to go from 0 to 1 in both cases, and correspond to 0 to pi by taking only half of the graph, like this if possible:
You only need some small changes, marked with comments in the code below:
function freqrespplot(b, a)
hb = fft(b,512);
ha = fft(a,512);
magh = (abs(hb) ./ abs(ha));
angh = angle(hb ./ ha);
x = linspace(0,2, 513); %// 2, not 2*pi. And 513. Last value will be discarded
x = x(1:end-1); %// discard last value to avoid having 0 and 2*pi (they are the same)
subplot(2,1,1);
plot(x(1:end/2),magh(1:end/2),'g'); %// plot only half
subplot(2,1,2);
plot(x(1:end/2),angh(1:end/2),'r'); %// plot only half
end
With your example b and a this produces
You may want to include one additional sample to reach the right edge of the graph. I comment only differences with the above code:
function freqrespplot(b, a)
hb = fft(b,512);
ha = fft(a,512);
magh = (abs(hb) ./ abs(ha));
angh = angle(hb ./ ha);
x = linspace(0,2, 513);
x = x(1:end-1);
subplot(2,1,1);
plot(x(1:end/2+1),magh(1:end/2+1),'g'); %// plot only lower half plus one value
subplot(2,1,2);
plot(x(1:end/2+1),angh(1:end/2+1),'r'); %// plot only lower half plus one value
end
Compare the resulting graph (rightmost part):

Sorting two column vectors into 3D matrix based on position

Using the imfindcircles function in MATLAB to track circles in two images. I start with approximately a grid of circles which deforms. I am trying to sort the two column vector from imfindcircles into matrices so that neighbouring circles are neighbouring elements in the matrices. The first image the circles conform to a grid and the following code works:
[centXsort,IX] = sortrows(centres1,1); %sort by x
centYsort =zeros(289,2); %preallocate
for i = 1:17:289
[sortedY,IY] = sortrows(centXsort(i:i+16,:),2); %sort by y within individual column
centYsort(i:i+16,:) = sortedY;
end
cent1mat = reshape(centYsort,17,17,2); %reshape into centre matrices
This doesn't work for the second image as some of the circles overlap in the x or y direction, but neighbouring circles never overlap. This means that in the second set of matrices the neighbouring circles aren't neighbouring elements after sorting.
Is there a way to approximate a scatter of points into a matrix?
This answer doesn't work in every single case, but it seems good enough for situations where the points don't vary too wildly.
My idea is to start at the grid corners and work our way along the outside diagonals of the matrix, trying to "grab" the nearest points that seem like they fit into the grid-points based any surrounding points we've already captured.
You will need to provide:
The number of rows (rows) and columns (cols) in the grid.
Your data points P arranged in a N x 2 array, rescaled to the unit square on [0,1] x [0,1]. (I assume the you can do this through visual inspection of the corner points of your original data.)
A weight parameter edge_weight to tell the algorithm how much the border points should be attracted to the grid border. Some tests show that 3-5 or so are good values.
The code, with a test case included:
%// input parameters
rows = 11;
cols = 11;
edge_weight = 4;
%// function for getting squared errors between the points list P and a specific point pt
getErr =#(P,pt) sqrt( sum( bsxfun(#minus,P,pt(:)').^2, 2 ) ); %'
output_grid = zeros(rows,cols,2); %// output grid matrix
check_grid = zeros(rows,cols); %// matrix flagging the gridpoints we have covered
[ROW,COL] = meshgrid(... %// coordinate points of an "ideal grid"
linspace(0,1,rows),...
linspace(0,1,cols));
%// create a test case
G = [ROW(:),COL(:)]; %// the actual grid-points
noise_factor = 0.35; %// noise radius allowed
rn = noise_factor/rows;
cn = noise_factor/cols;
row_noise = -rn + 2*rn*rand(numel(ROW),1);
col_noise = -cn + 2*cn*rand(numel(ROW),1);
P = G + [row_noise,col_noise]; %// add noise to get points
%// MAIN LOOP
d = 0;
while ~isempty(P) %// while points remain...
d = d+1; %// increase diagonal depth (d=1 are the outer corners)
for ii = max(d-rows+1,1):min(d,rows)%// for every row number i...
i = ii;
j = d-i+1; %// on the dth diagonal, we have d=i+j-1
for c = 1:4 %// repeat for all 4 corners
if i<rows & j<cols & ~check_grid(i,j) %// check for out-of-bounds/repetitions
check_grid(i,j) = true; %// flag gridpoint
current_gridpoint = [ROW(i,j),COL(i,j)];
%// get error between all remaining points and the next gridpoint's neighbours
if i>1
errI = getErr(P,output_grid(i-1,j,:));
else
errI = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i+1,j)
errI = errI + edge_weight*getErr(P,current_gridpoint);
end
if j>1
errJ = getErr(P,output_grid(i,j-1,:));
else
errJ = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i,j+1)
errJ = errJ + edge_weight*getErr(P,current_gridpoint);
end
err = errI.^2 + errJ.^2;
%// find the point with minimal error, add it to the grid,
%// and delete it from the points list
[~,idx] = min(err);
output_grid(i,j,:) = permute( P(idx,:), [1 3 2] );
P(idx,:) = [];
end
%// rotate the grid 90 degrees and repeat for next corner
output_grid = cat(3, rot90(output_grid(:,:,1)), rot90(output_grid(:,:,2)));
check_grid = rot90(check_grid);
ROW = rot90(ROW);
COL = rot90(COL);
end
end
end
Code for plotting the resulting points with edges:
%// plotting code
figure(1); clf; hold on;
axis([-0.1 1.1 -0.1 1.1])
for i = 1:size(output_grid,1)
for j = 1:size(output_grid,2)
scatter(output_grid(i,j,1),output_grid(i,j,2),'b')
if i < size(output_grid,1)
plot( [output_grid(i,j,1),output_grid(i+1,j,1)],...
[output_grid(i,j,2),output_grid(i+1,j,2)],...
'r');
end
if j < size(output_grid,2)
plot( [output_grid(i,j,1),output_grid(i,j+1,1)],...
[output_grid(i,j,2),output_grid(i,j+1,2)],...
'r');
end
end
end
I've developed a solution, which works for my case but might not be as robust as required for some. It requires a known number of dots in a 'square' grid and a rough idea of the spacing between the dots. I find the 'AlphaShape' of the dots and all the points that lie along the edge. The edge vector is shifted to start at the minimum and then wrapped around a matrix with the corresponding points are discarded from the list of vertices. Probably not the best idea for large point clouds but good enough for me.
R = 50; % search radius
xy = centres2;
x = centres2(:,1);
y = centres2(:,2);
for i = 1:8
T = delaunay(xy); % delaunay
[~,r] = circumcenter(triangulation(T,x,y)); % circumcenters
T = T(r < R,:); % points within radius
B = freeBoundary(triangulation(T,x,y)); % find edge vertices
A = B(:,1);
EdgeList = [x(A) y(A) x(A)+y(A)]; % find point closest to origin and rotate vector
[~,I] = min(EdgeList);
EdgeList = circshift(EdgeList,-I(3)+1);
n = sqrt(length(xy)); % define zeros matrix
matX = zeros(n); % wrap x vector around zeros matrix
matX(1,1:n) = EdgeList(1:n,1);
matX(2:n-1,n) = EdgeList(n+1:(2*n)-2,1);
matX(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,1);
matX(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,1);
matY = zeros(n); % wrap y vector around zeros matrix
matY(1,1:n) = EdgeList(1:n,2);
matY(2:n-1,n) = EdgeList(n+1:(2*n)-2,2);
matY(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,2);
matY(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,2);
centreMatX(i:n+i-1,i:n+i-1) = matX; % paste into main matrix
centreMatY(i:n+i-1,i:n+i-1) = matY;
xy(B(:,1),:) = 0; % discard values
xy = xy(all(xy,2),:);
x = xy(:,1);
y = xy(:,2);
end
centreMatX(centreMatX == 0) = x;
centreMatY(centreMatY == 0) = y;