pydata / pydata/xarray

Feature request : native support of 3D voxel plots

Open
#6,537 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement topic-plotting
Dominant language
Python
Stars
4.2k
Forks
1.4k
Avg merge
2d 15h
Merged PRs (30d)
14

Description

Is your feature request related to a problem?

There is a @_plot2d decorator vith miscellaneous plots methods and a signature if one wants to build its own custom 2D plot method but no method for 3D plot while I believe we can already have 3D scatter and line plots.

Describe the solution you'd like

It would be great to have a voxel method wrapped on the matplotlib.pyplot in a proper _3Dplot method. It should deal with the label names, facetgrids or severals axes, the colorbar and the dimension.variables of the dataset we want to plot. Coding it for a 3D datarray would already be a very good start !

It is particularly useful for 3D Histograms the same way as pcolormesh is useful for 2D ones and is already implemented

I've written my own basic approach, I here are the plots to give you an idea :

simple_ax_example

multiple_axes_example

As you can see the multiple axis dealing part can be seriously improved ahah

Describe alternatives you've considered

This is my code for the moment, I used some functions that you define the documentation to get the labels for example.
I got rid of most of the args checking bc my code is not intended to be used for others and I dropped the facegrid option bc I create my custom figs by hand

[Edit : colormap and levels working now, so it can be used by others if the voxels method is still not available]

`

def hist3Dplot(darray,bins,levels,ax=None,cmap="viridis", add_labels=True,add_colorbar=True,**kwargs):#colors=None,,cbar_ax=None
    #bins could in theory be obtained by darray but [darray[dim].values for dim in darray.dims] give centered values and not interval borns

    def bin2coord(bins):
    #X,Y,Z are the coords of the bins hence the "+1"
        return np.meshgrid(*[np.linspace(bin[0][0],bin[-1][1],len(bin)+1) for bin in bins])


    NPArr=darray.to_numpy()
    BoolArr=NPArr!=0 #won't draw non occuring voxels from the histogram
    

    palette=mpl.cm.get_cmap("viridis",lut=levels).copy()
    palette.set_under((0,0,0,0)) #for the cmap so that vmin is taken into account


    
    CArr = palette(NPArr/np.max(NPArr))
    CArr[:,:,:,3]=0.9


    X, Y, Z = bin2coord(bins)

    if ax==None:
        fig=plt.figure()
        ax = fig.add_subplot(projection='3d')
    else:
        fig=plt.gcf()
    
    primitive = ax.voxels(X, Y, Z, BoolArr, facecolors=CArr)#,edgecolor=(236/250, 236/255, 236/255, 1))

    dim_names=darray.dims
    if add_labels:
        #So that the long labels don't stack on the axis ticks
        axis_labels=np.array([xr.plot.utils.label_from_attrs(darray[dim_names[i]]) for i in range(3)])
        labelpad=np.where(np.char.str_len(axis_labels)>50,20,5)

        ax.set_xlabel(axis_labels[0],fontsize=15,labelpad=labelpad[0])
        ax.set_ylabel(axis_labels[1],fontsize=15,labelpad=labelpad[1])
        ax.set_zlabel(axis_labels[2],fontsize=15,labelpad=labelpad[2])

        ax.set_title(darray._title_for_slice())


    if add_colorbar:
        from matplotlib import cm
        vmax=np.max(NPArr)
        norm = mpl.colors.Normalize(0,vmax)
        m = cm.ScalarMappable(cmap=palette,norm=norm)
        m.set_array([])
        ticks=np.linspace(1,vmax,levels+1)
        fig.colorbar(m,ax=ax,extend="min",ticks=ticks)
    
    
        

    return primitive

`

In this example I have a custom cmap to filter the 0 values as transparent (it's easier to read for a histogram) that's why I add a level in the colorbar and I created the axis before calling the function

I believe someone with a better understanding of the API could create a wrapper around matplotlib.pyplot.voxels easily. The _3dplot decorator would make some sense if you keep adding 3d plot methods on requests !

Additional context

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the existing @_plot2d decorator and current 3D scatter and line plot entry points, then compare their interfaces with matplotlib.pyplot.voxels. A complete result should support a 3D DataArray with dimension labels, colorbar handling, and multiple axes or facet grids as described in the request.

Written by the indexing model from the issue text.

Assessment

Tech stack
numpy, python
Domain
data-visualization
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.