简体   繁体   中英

how to do 3D number-density scatter plot using given data?

I have a cubic box with the size of, lets say 300 in each direction. I have some data (contains X,Y,Z coordinates) which represent the distribution of nearly 1 million data-points within this box. I want to specify a color to their Number density (its an intensive quantity used to describe the degree of concentration of countable objects in this case data-points). In another word, Using color to illustrate which part is more condensed in terms of data-points rather than the other parts. The index for the color-bar in the final image should represent the percentage of data-points specified with that color.

I have tried to do it by dividing the whole space in cubic box to 1 million smaller cube (each cube has a length of 3 in all direction). By counting the number of particles within those cube, I will know how they distributed in the box and the number of existed data-points within. Then I can specify a color to them which I wasn't successful in counting and specifying. Any suggestion is appreciated.

%reading the files
[FileName,PathName,FilterIndex] = uigetfile('H:\*.txt','MultiSelect','on');
numfiles = size(FileName,2);%('C:\final1.txt');
j=1;
X=linspace(0,300,100);
for ii = 1:numfiles
    FileName{ii}
    entirefile = fullfile(PathName,FileName{ii});
    a = importdata(entirefile);
    x = a(:,2);
    y = a(:,3);
    z = a(:,4);
    %% I DON'T KNOW HOW TO CREAT THIS LOOP TO COUNT FOR THE NUMBER OF PARTICLES WITHIN EACH DEFINED CUBE %%
    for jj = 2:size(X,2) 
        %for kk=1:m
        if x(:)<X(jj) & y(:)<X(jj) & z(:)<X(jj)
           x;
        end
        %end
    end
    h=figure(j);
    scatter3(x, y, z, 'filled', 'MarkerSize', 20);
    cb = colorbar();
    cb.Label.String = 'Probability density estimate';
end

I need to get a similar result like the following image. but I need the percentage of data-point specified by each color. Thanks in advance.

Here is a link to a sampled data.

Here is a way to count the 3D density of a point cloud. Although I am affraid the sample data you provided do not yield the same 3D distribution than on your example image.

To count the density, the approach is broken down in several steps:

  • Calculate a 2D density in the [X,Y] plane: This counts the number of points laying in each (x,y) bin. However, at this stage this number of point incorporates all the Z column for a given bin.
  • For each non-empty (x,y) bin, calculate the distribution along the Z column. We now have the number of point falling in each (x,y,z) bin. Counting the density/percentage is simply done by dividing each count by the total number of points.
  • Now for each non-empty (x,y,z) bin, we identify the linear indices of the points belonging to this bin. We then assign the bin value (color, percentage, or any value associated to this bin) to all the identified points.
  • display the results.

In code, it goes like this:

%% Import sample data
entirefile = '1565015520323.txt' ;
a = importdata(entirefile);
x = a(:,1);
y = a(:,2);
z = a(:,3);
npt = numel(x) ; % Total Number of Points

%% Define domain and grid parameters
nbins    = 100 ;
maxDim   = 300 ;
binEdges = linspace(0,maxDim,nbins+1) ;

%% Count density
% we start counting density along in the [X,Y] plane (Z axis aglomerated)
[Nz,binEdges,~,binX,binY] = histcounts2(y,x,binEdges,binEdges) ;

% preallocate 3D containers
N3d = zeros(nbins,nbins,nbins) ; % 3D matrix containing the counts
Npc = zeros(nbins,nbins,nbins) ; % 3D matrix containing the percentages
colorpc = zeros(npt,1) ;         % 1D vector containing the percentages

% we do not want to loop on every block of the domain because:
%   - depending on the grid size there can be many
%   - a large number of them can be empty
% So we first find the [X,Y] blocks which are not empty, we'll only loop on
% these blocks.
validbins = find(Nz) ;                              % find the indices of non-empty blocks
[xbins,ybins] = ind2sub([nbins,nbins],validbins) ;  % convert linear indices to 2d indices
nv = numel(xbins) ;                                 % number of block to process

% Now for each [X,Y] block, we get the distribution over a [Z] column and
% assign the results to the full 3D matrices
for k=1:nv
    % this block coordinates
    xbin = xbins(k) ;
    ybin = ybins(k) ;

    % find linear indices of the `x` and `y` values which are located into this block
    idx = find( binX==xbin & binY==ybin ) ;
    % make a subset with the corresponding 'z' value
    subZ = z(idx) ;
    % find the distribution and assign to 3D matrices
    [Nz,~,zbins] = histcounts( subZ , binEdges ) ;
    N3d(xbin,ybin,:) = Nz ;         % total counts for this block
    Npc(xbin,ybin,:) = Nz ./ npt ;  % density % for this block

    % Now we have to assign this value (color or percentage) to all the points
    % which were found in the blocks
    vzbins = find(Nz) ;
    for kz=1:numel(vzbins)
        thisColorpc = Nz(vzbins(kz)) ./ npt * 100 ;
        idz   = find( zbins==vzbins(kz) ) ;
        idx3d = idx(idz) ;
        colorpc(idx3d) = thisColorpc ;
    end

end
assert( sum(sum(sum(N3d))) == npt ) % double check we counted everything

%% Display final result
h=figure;
hs=scatter3(x, y, z, 3 , colorpc ,'filled' );
xlabel('X'),ylabel('Y'),zlabel('Z')
cb = colorbar ;
cb.Label.String = 'Probability density estimate';

As I said at the beginning, the result is slightly different than your example image. This sample set yields the following distribution:

3D密度

If you want a way to "double check" that the results are not garbage, you can look at the 2D density results on each axis, and check that it matches the apparent distribution of your points:

%% Verify on 3 axis:
Nz = histcounts2(y,x,binEdges,binEdges) ./ npt *100 ;
Nx = histcounts2(z,y,binEdges,binEdges) ./ npt *100 ;
Ny = histcounts2(x,z,binEdges,binEdges) ./ npt *100 ;
figure
ax1=subplot(1,3,1) ; bz = plotDensity(Nz,ax1) ; xlabel('X'),ylabel('Y') ;
ax2=subplot(1,3,2) ; bx = plotDensity(Nx,ax2) ; xlabel('Y'),ylabel('Z') ;
ax3=subplot(1,3,3) ; by = plotDensity(Ny,ax3) ; xlabel('Z'),ylabel('X') ;

Click on the image to see it larger:

在此处输入图片说明

The code for plotDensity.m :

function hp = plotDensity(Ndist,hax)
    if nargin<2 ; hax = axes ; end
    hp = bar3(Ndist,'Parent',hax) ;
    for k = 1:length(hp)
        zdata = hp(k).ZData;
        hp(k).CData = zdata;
        hp(k).FaceColor = 'interp';
    end
    shading interp

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM