3D histogram with gnuplot or octave

匿名 (未验证) 提交于 2019-12-03 01:59:02

问题:

I would like to draw a 3D histogram (with gnuplot or octave) in order to represent my data. lets say that I have a data file in the following form:

2 3 4     8 4 10     5 6 7 

I'd like to draw nine colored bars (the size of the matrix), in the set [1,3]x[1,3], such that the bar's color is proportional to the bar's height. How can I do this?

回答1:

Below is a function I implemented that acts as a bar3 replacement (partially).

In my version, the bars are rendered by creating a patch graphics object: we build a matrix of vertex coordinates and a list of faces connecting those vertices.

The idea is to first build a single "3d cube" as a template, then replicate it for as many bars as we have. Each bar is shifted and scaled according to its position and height.

The vertices/faces matrices are constructed in a vectorized manner (look ma, no loops!), and the result is a single patch object drawn for all bars, as opposed to multiple patches one per bar (this is more efficient in terms of graphics performance).

The function could have been implemented by specifying coordinates of connected vertices that form polygons, by using the XData, YData, ZData and CData properties instead of the Vertices and Faces properties. In fact this is what bar3 internally does. Such approach usually requires larger data to define the patches (because we cant have shared points across patch faces, although I didn't care much about that in my implementation). Here is a related post where I tried to explain the structure of the data constructed by bar3.

my_bar3.m

function pp = my_bar3(M, width)     % MY_BAR3  3D bar graph.     %     % M     - 2D matrix     % width - bar width (1 means no separation between bars)     %     % See also: bar3, hist3      %% construct patch     if nargin  0         % If running Octave, select OpenGL backend, gnuplot wont work         graphics_toolkit('fltk');         hax = gca;     else         hax = newplot();         set(ancestor(hax,'figure'), 'Renderer','opengl')     end       % draw patch specified by faces/vertices     % (we use a solid color for all faces)     p = patch('Faces',f, 'Vertices',v, ...         'FaceColor',[0.75 0.85 0.95], 'EdgeColor','k', 'Parent',hax);     view(hax,3); grid(hax,'on');     set(hax, 'XTick',1:nx, 'YTick',1:ny, 'Box','off', 'YDir','reverse', ...         'PlotBoxAspectRatio',[1 1 (sqrt(5)-1)/2]) % 1/GR (GR: golden ratio)      % return handle to patch object if requested     if nargout > 0         pp = p;     end end 

Here is an example to compare it against the builtin bar3 function in MATLAB:

subplot(121), bar3(magic(7)), axis tight subplot(122), my_bar3(magic(7)), axis tight 

Note that I chose to color all the bars in a single solid color (similar to the output of the hist3 function), while MATLAB emphasizes the columns of the matrix with matching colors.

It is easy to customize the patch though; Here is an example to match bar3 coloring mode by using indexed color mapping (scaled):

M = membrane(1); M = M(1:3:end,1:3:end); h = my_bar3(M, 1.0);  % 6 faces per bar fvcd = kron((1:numel(M))', ones(6,1)); set(h, 'FaceVertexCData',fvcd, 'FaceColor','flat', 'CDataMapping','scaled')  colormap hsv; axis tight; view(50,25) set(h, 'FaceAlpha',0.85)   % semi-transparent bars 

Or say you wanted to color the bars using gradient according to their heights:

M = 9^2 - spiral(9); h = my_bar3(M, 0.8);  % use Z-coordinates as vertex colors (indexed color mapping) v = get(h, 'Vertices'); fvcd = v(:,3); set(h, 'FaceVertexCData',fvcd, 'FaceColor','interp')  axis tight vis3d; daspect([1 1 10]); view(-40,20) set(h, 'EdgeColor','k', 'EdgeAlpha',0.1) 

Note that in the last example, the "Renderer" property of the figure will affect the appearance of the gradients. In MATLAB, the 'OpenGL' renderer will interpolate colors along the RGB colorspace, whereas the other two renderers ('Painters' and 'ZBuffer') will interpolate across the colors of the current colormap used (so the histogram bars would look like mini colorbars going through the jet palette, as opposed to a gradient from blue at the base to whatever the color is at the defined height as shown above). See this post for more details.


I've tested the function in Octave 3.6.4 and 3.8.1 both running on Windows, and it worked fine. If you run the examples I showed above, you'll find that some of the advanced 3D features are not yet implemented correctly in Octave (this includes transparency, lighting, and such..). Also I've used functions not available in Octave like membrane and spiral to build sample matrices, but those are not essential to the code, just replace them with your own data :)



回答2:

I don't have access to Octave, butI believe this should do the trick:

Z = [2 3 4      8 4 10      5 6 7];  [H W] = size(Z);  h = zeros( 1, numel(Z) );  ih = 1; for ix = 1:W     fx = ix-.45;     tx = ix+.45;     for iy = 1:W         fy = iy-.45;         ty = iy+.45;                vert = [ fx fy 0;...             fx ty 0;...             tx fy 0;...             tx ty 0;...             fx fy Z(iy,ix);...             fx ty Z(iy,ix);...             tx fy Z(iy,ix);...             tx ty Z(iy,ix)];         faces = [ 1 3 5;...             5 3 7;...             7 3 4;...             7 8 4;...             5 6 7;...             6 7 8;...             1 2 5;...             5 6 2;...             2 4 8;...             2 6 8];          h(ih) = patch( 'faces', faces, 'vertices', vert, 'FaceVertexCData', Z(iy,ix),...             'FaceColor', 'flat', 'EdgeColor','none' );         ih = ih+1;     end end view( 60, 45 ); colorbar; 


回答3:

I think the following should do the trick. I didn't use anything more sophisticated than colormap, surf and patch, which to my knowledge should all work as-is in Octave.

The code:

%# Your data Z = [2 3 4     8 4 10     5 6 7];   %# the "nominal" bar (adjusted from cylinder()) n = 4; r = [0.5; 0.5]; m = length(r); theta = (0:n)/n*2*pi + pi/4;  sintheta = sin(theta); sintheta(end) = sqrt(2)/2;  x0 = r * cos(theta); y0 = r * sintheta; z0 = (0:m-1)'/(m-1) * ones(1,n+1);  %# get data for current colormap map = colormap; Mz = max(Z(:)); mz = min(Z(:));  % Each "bar" is 1 surf and 1 patch for ii = 1:size(Z,1)     for jj = 1:size(Z,2)          % Get color (linear interpolation through current colormap)         cI = (Z(ii,jj)-mz)*(size(map,1)-1)/(Mz-mz) + 1;         fC = floor(cI);         cC = ceil(cI);         color = map(fC,:) + (map(cC,:) - map(fC,:)) * (cI-fC);          % Translate and rescale the nominal bar         x = x0+ii;         y = y0+jj;         z = z0*Z(ii,jj);          % Draw the bar         surf(x,y,z, 'Facecolor', color)         patch(x(end,:), y(end,:), z(end,:), color)      end end 

Result:

How I generate the "nominal bar" is based on code from MATLAB's cylinder(). One cool thing about that is you can very easily make much more funky-looking bars:

This was generated by changing

n = 4; r = [0.5; 0.5]; 

into

n = 8; r = [0.5; 0.45; 0.2; 0.1; 0.2; 0.45; 0.5]; 


回答4:

Solution using only functions available in OCTAVE, tested with octave-online

This solution generates a surface in a similar way to the internals of Matlabs hist3d function.

In brief:

  • creates a surface with 4 points with the "height" of each value, which are plotted at each bin edge.
  • Each is surrounded by zeros, which are also plotted at each bin edge.
  • The colour is set to be based on the bin values and is applied to the 4 points and the surrounding zeros. (so that the edges and tops of the 'bars' are coloured to match the "height".)

For data given as a matrix containing bin heights (bin_values in the code):

Code

bin_values=rand(5,4); %some random data  bin_edges_x=[0:size(bin_values,2)];  x=kron(bin_edges_x,ones(1,5)); x=x(4:end-2);  bin_edges_y=[0:size(bin_values,1)];  y=kron(bin_edges_y,ones(1,5)); y=y(4:end-2);  mask_z=[0,0,0,0,0;0,1,1,0,0;0,1,1,0,0;0,0,0,0,0;0,0,0,0,0]; mask_c=ones(5); z=kron(bin_values,mask_z); c=kron(bin_values,mask_c);  surf(x,y,z,c) 

Output



回答5:

Have you looked at this tutorial on bar3?

Adapting it slightly:

Z=[2 3 4    8 4 10    5 6 7]; % input data figure;  h = bar3(Z); % get handle to graphics for k=1:numel(h),      z=get(h(k),'ZData'); % old data - need for its NaN pattern     nn = isnan(z);      nz = kron( Z(:,k),ones(6,4) ); % map color to height 6 faces per data point      nz(nn) = NaN; % used saved NaN pattern for transparent faces      set(h(k),'CData', nz); % set the new colors end colorbar; 

And here's what you get at the end:



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!