how do i make a plot from a data table with a random number of entries

I wrote a little script to make a video for the dynamic of 10 beads connected by springs from a data table.
The data table format:
timestep1 x1 y1 z1 x2 y2 z2 ... x10 y10 z10
timestep2 x1 y1 z1 x2 y2 z2 ... x10 y10 z10
timestep3 x1 y1 z1 x2 y2 z2 ... x10 y10 z10
...
timestep increments with a fixed factor, xi yi zi are the cartesian coordinates of the i. bead wich change with every timestep.
here is the code:
A = readtable('\\wsl$\Debian\home\cpp\traj.txt');
t = A{:,1};
x0 = A{:,2};
y0 = A{:,3};
z0 = A{:,4};
x1 = A{:,5};
y1 = A{:,6};
z1 = A{:,7};
x2 = A{:,8};
y2 = A{:,9};
z2 = A{:,10};
x3 = A{:,11};
y3 = A{:,12};
z3 = A{:,13};
x4 = A{:,14};
y4 = A{:,15};
z4 = A{:,16};
x5 = A{:,17};
y5 = A{:,18};
z5 = A{:,19};
x6 = A{:,20};
y6 = A{:,21};
z6 = A{:,22};
x7 = A{:,23};
y7 = A{:,24};
z7 = A{:,25};
x8 = A{:,26};
y8 = A{:,27};
z8 = A{:,28};
x9 = A{:,29};
y9 = A{:,30};
z9 = A{:,31};
set(gca, 'XLim', [-5 5], 'YLim', [-5 5], 'ZLim', [-5 5]);
view(43,24);
hold on;
for i=1:1000
k = scatter3(x0(i), y0(i), z0(i),'filled','MarkerFaceColor','k');
a = scatter3(x1(i), y1(i), z1(i),'filled','MarkerFaceColor','r');
lx0 = linspace(x0(i),x1(i));
ly0 = linspace(y0(i),y1(i));
lz0 = linspace(z0(i),z1(i));
l0 = plot3(lx0,ly0,lz0);
b = scatter3(x2(i), y2(i), z2(i),'filled','MarkerFaceColor','g');
lx1 = linspace(x1(i),x2(i));
ly1 = linspace(y1(i),y2(i));
lz1 = linspace(z1(i),z2(i));
l1 = plot3(lx1,ly1,lz1);
c = scatter3(x3(i), y3(i), z3(i),'filled','MarkerFaceColor','b');
lx2 = linspace(x2(i),x3(i));
ly2 = linspace(y2(i),y3(i));
lz2 = linspace(z2(i),z3(i));
l2 = plot3(lx2,ly2,lz2);
d = scatter3(x4(i), y4(i), z4(i),'filled','MarkerFaceColor','y');
lx3 = linspace(x3(i),x4(i));
ly3 = linspace(y3(i),y4(i));
lz3 = linspace(z3(i),z4(i));
l3 = plot3(lx3,ly3,lz3);
e = scatter3(x5(i), y5(i), z5(i),'filled','MarkerFaceColor','m');
lx4 = linspace(x4(i),x5(i));
ly4 = linspace(y4(i),y5(i));
lz4 = linspace(z4(i),z5(i));
l4 = plot3(lx4,ly4,lz4);
f = scatter3(x6(i), y6(i), z6(i),'filled','MarkerFaceColor','c');
lx5 = linspace(x5(i),x6(i));
ly5 = linspace(y5(i),y6(i));
lz5 = linspace(z5(i),z6(i));
l5 = plot3(lx5,ly5,lz5);
g = scatter3(x7(i), y7(i), z7(i),'filled','MarkerFaceColor','b');
lx6 = linspace(x6(i),x7(i));
ly6 = linspace(y6(i),y7(i));
lz6 = linspace(z6(i),z7(i));
l6 = plot3(lx6,ly6,lz6);
h = scatter3(x8(i), y8(i), z8(i),'filled','MarkerFaceColor','k');
lx7 = linspace(x7(i),x8(i));
ly7 = linspace(y7(i),y8(i));
lz7 = linspace(z7(i),z8(i));
l7 = plot3(lx7,ly7,lz7);
j = scatter3(x9(i), y9(i), z9(i),'filled','MarkerFaceColor','r');
lx8 = linspace(x8(i),x9(i));
ly8 = linspace(y8(i),y9(i));
lz8 = linspace(z8(i),z9(i));
l8 = plot3(lx8,ly8,lz8);
drawnow
F(i) = getframe(gcf);
pause(0.01);
delete(a);
delete(b);
delete(c);
delete(d);
delete(e);
delete(f);
delete(g);
delete(h);
delete(j);
delete(k);
delete(l0);
delete(l1);
delete(l2);
delete(l3);
delete(l4);
delete(l5);
delete(l6);
delete(l7);
delete(l8);
end
video = VideoWriter('ConnectedBeads.avi', 'Uncompressed AVI');
video.FrameRate = 60;
open(video)
writeVideo(video,F);
close(video)
I'm sure this isnt a very dapper code. How can I improve it so It works for a random number of N beads with a table that looks like:
timestep1 x1 y1 z1 x2 y2 z2 ... x10 y10 z10 ... xN yN zN
timestep2 x1 y1 z1 x2 y2 z2 ... x10 y10 z10 ... xN yN zN
timestep3 x1 y1 z1 x2 y2 z2 ... x10 y10 z10 ... xN yN zN

1 Comment

It looks like you can use gscatter. All at once can be plotted. You have made it complex by taking different number of variables.

Sign in to comment.

 Accepted Answer

Use arrays instead of numbered variables. You see how this code is much less repeating and easier to see what is happening where?
Because I don't have your data, I didn't test this code.
A = readtable('\\wsl$\Debian\home\cpp\traj.txt');
t = A{:,1};
x = A(:,2:3:end); x=cell2mat(x);
y = A(:,3:3:end); y=cell2mat(y);
z = A(:,4:3:end); z=cell2mat(z);
%create a clean figure and axis with handles
f=figure(1);clf(1)%you could also do f=figure; instead
ax=axis('Parent',f,...
'XLim', [-5 5], 'YLim', [-5 5], 'ZLim', [-5 5],...
'NextPlot','add');
view(43,24);
%how are you making sure you never run out of colors?
%consider either not explicitly choosing colors yourself, or using ColorList{mod(bead,end-1)+1}
ColorList={'k','r','g','b','y','m','c','b','k','r'};
h=[];
for bead=1:size(x,2)
%initialize all bead plots
h.scatter(bead) = scatter3(x(1,bead), y(1,bead), z(1,bead),...
'filled','MarkerFaceColor',ColorList{bead});
lx0 = linspace(x(1,bead),x(1,bead));
ly0 = linspace(y(1,bead),y(1,bead));
lz0 = linspace(z(1,bead),z(1,bead));
h.plot(bead) = plot3(lx0,ly0,lz0);
end
%initialize F properly here
for timestamp=1:numel(t)
for bead=1:size(x,2)
%update the XData,YData,ZData properties instead of deleting everything, which can now do with delete(h.plot),delete(h.scatter)
end
drawnow
F(timestamp) = getframe(f);
end

3 Comments

First of all thanks for your answer. I'm pretty new to matlab so I don't know the syntax well, but I think table2array works better than cell2mat because the scatter function needs numeric arguments. That was the first error I got with your code. I uploaded some data with this comment. Only the neigbouring beads are connected by a spring and every new line in the data is another timestep where the coordinates change.
%using arrays
x = A(:,2:3:end);
x= I t(x);
y = A(:,3:3:end);
y= table2array(y);
z = A(:,4:3:end);
z= table2array(z);
for t =1:40
for bead=1:size(x,2)-1
%inside this loops there should be a bead and the connectionline to the next bead be initialised. Something like:
...scatter3(x(t,bead), y(t,bead), z(t,bead),'filled','MarkerFaceColor','b');
lx0 = linspace(x(t,bead),x(t,bead + 1));
ly0 = linspace(y(t,bead),y(t,bead + 1));
lz0 = linspace(z(t,bead),z(t,bead + 1));
...
end
%the last bead has to be initialised here because of the indexlimit for bead:
scatter3(x(t,size(x,2)), y(t,size(x,2)), z(t,size(x,2)),'filled','MarkerFaceColor','b');
%now all N beads and the connectionlines between neighbouring should be plotted so that the next frame for the video can be captured
end
To make it more clear I added a short gif of the version fixed for 10 beads. Thanks for the support!
First a little housekeeping: This time I edited your post for you. Next time, please use the tools explained on this page to make your post more readable. If you feel my answer solved your issue, please mark it as accepted answer.
The easiest way to make sure your data is an array, is to load it as an array, e.g. with A=readmatrix('traj.txt','NumHeaderLines',0);.
To avoid indexing errors in your last iteration, you can replace bead+1 by min(bead+1,end).
"Use arrays instead of numbered variables." was definetly the right answer. Again, thank you!
Here my solution with arrays that gets the job done:
A = readtable('\\wsl$\Debian\home\cpp\traj.txt');
time = A{:,1};
%using arrays
x = A(:,2:3:end);
x= table2array(x);
y = A(:,3:3:end);
y= table2array(y);
z = A(:,4:3:end);
z= table2array(z);
set(gca, 'XLim', [-40 45], 'YLim', [-40 50], 'ZLim', [-40 45]);
view(25,-25);
hold on;
h=[];
l =[];
for t =1:length(time)
for bead=1:size(x,2)-1
h(bead) = scatter3(x(t,bead), y(t,bead), z(t,bead),'filled','MarkerFaceColor','b');
lx = linspace(x(t,bead),x(t,bead + 1));
ly = linspace(y(t,bead),y(t,bead + 1));
lz = linspace(z(t,bead),z(t,bead + 1));
l(bead) = plot3(lx,ly,lz);
end
h(size(x,2)) = scatter3(x(t,size(x,2)), y(t,size(x,2)), z(t,size(x,2)),'filled','MarkerFaceColor','b');
drawnow
F(t) = getframe(gcf);
pause(0.01);
delete(h);
delete(l);
end
video = VideoWriter('100Beads.avi', 'Uncompressed AVI');
video.FrameRate = 60;
open(video)
writeVideo(video,F);
close(video)

Sign in to comment.

More Answers (0)

Asked:

on 5 Aug 2020

Commented:

on 6 Aug 2020

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!