Skip to content

Feature request : native support of 3D voxel plots #6537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Luluser opened this issue Apr 28, 2022 · 1 comment
Open

Feature request : native support of 3D voxel plots #6537

Luluser opened this issue Apr 28, 2022 · 1 comment

Comments

@Luluser
Copy link

Luluser commented Apr 28, 2022

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

@Luluser Luluser changed the title Deature request : native support of 3D voxel plots Feature request : native support of 3D voxel plots Apr 28, 2022
@Illviljan
Copy link
Contributor

I've been working on something similar in #5622. Tests are not passing yet though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants