Package: dama
dama - Data ManipulatorThe dama python library guides you through your data and translates between different representations.
Its aim is to offer a consistant and pythonic way to handle different datasaets and translations between them.
A dataset can for instance be simple colum/row data, or it can be data on a grid.One of the key features of dama is the seamless translation from one data represenation into any other.
Conveniencepyplotplotting functions are also available, in order to produce standard plots without any hassle.Installationpip install damaGetting StartedimportnumpyasnpimportdamaasdmGrid DataGridDatais a collection of individualGridArrays. Both have a definedgrid, here we initialize the grid in the constructor through simple keyword arguments resulting in a 2d grid with axesxandyg=dm.GridData(x=np.linspace(0,3*np.pi,30),y=np.linspace(0,2*np.pi,20),)Filling one array with some sinusoidal functions, calledahereg['a']=np.sin(g['x'])*np.cos(g['y'])As a shorthand, we can also use attributes instead of items:g.a=np.sin(g.x)*np.cos(g.y)in 1-d and 2-d they render as html in jupyter notebooksIt can be plotted easily in case of 1-d and 2-d gridsg.plot(cbar=True);Let's interpolate the values to 200 points along each axis and plotg.interp(x=200,y=200).plot(cbar=True);Executions of (most) translation methods islazy. That means that the computation only happens if a specific variable is used. This can have some side effects, that when you maipulate the original data before the translation is evaluated. just something to be aware of.Masking, and item assignement also is supportedg.a[g.a>0.3]y \ x00.3250.65...8.779.19.420--0.3190.605...0.6050.319--0.331--0.3020.572...0.5720.302--0.661----0.478...0.478----........................5.62----0.478...0.478----5.95--0.3020.572...0.5720.302--6.28--0.3190.605...0.6050.319--The objects are also numpy compatible and indexable by index (integers) or value (floats). Numpy functions withaxiskeywords accept either the name(s) of the axis, e.g. herexand therefore is independent of axis ordering, or the usual integer indices.g[10::-1,:np.pi:2]y \ x3.252.922.6...0.650.32500a = -0.108a = 0.215a = 0.516...a = 0.605a = 0.319a = 00.661a = -0.0853a = 0.17a = 0.407...a = 0.478a = 0.252a = 01.32a = -0.0265a = 0.0528a = 0.127...a = 0.149a = 0.0784a = 01.98a = 0.0434a = -0.0864a = -0.207...a = -0.243a = -0.128a = -02.65a = 0.0951a = -0.189a = -0.453...a = -0.532a = -0.281a = -0np.sum(g[10::-1,:np.pi:2].T,axis='x')y00.6611.321.982.65a6.034.761.48-2.42-5.3ComparisonAs comparison to point out the convenience, an alternative way without usingdamato achieve the above would look something like the follwoing for creating and plotting the array:x = np.linspace(0,3*np.pi, 30)
y = np.linspace(0,2*np.pi, 20) 

xx, yy = np.meshgrid(x, y)
a = np.sin(xx) * np.cos(yy)

import matplotlib.pyplot as plt

x_widths = np.diff(x)
x_pixel_boundaries = np.concatenate([[x[0] - 0.5*x_widths[0]], x[:-1] + 0.5*x_widths, [x[-1] + 0.5*x_widths[-1]]])
y_widths = np.diff(y)
y_pixel_boundaries = np.concatenate([[y[0] - 0.5*y_widths[0]], y[:-1] + 0.5*y_widths, [y[-1] + 0.5*y_widths[-1]]])

pc = plt.pcolormesh(x_pixel_boundaries, y_pixel_boundaries, a)
plt.gca().set_xlabel('x')
plt.gca().set_ylabel('y')
cb = plt.colorbar(pc)
cb.set_label('a')and for doing the interpolation:from scipy.interpolate import griddata

interp_x = np.linspace(0,3*np.pi, 200)
interp_y = np.linspace(0,2*np.pi, 200) 

grid_x, grid_y = np.meshgrid(interp_x, interp_y)

points = np.vstack([xx.flatten(), yy.flatten()]).T
values = a.flatten()

interp_a = griddata(points, values, (grid_x, grid_y), method='cubic')PointDataAnother representation of data isPointData, which is not any different of a dictionary holding same-length nd-arrays or a pandasDataFrame(And can actually be instantiated with those).p=dm.PointData()p.x=np.random.randn(100_000)p.a=np.random.rand(p.size)*p.x**2px0.03410.2120.517...1.270.8271.57a0.001060.0350.18...1.590.2460.201p.plot()Maybe a correlation plot would be more insightful:p.plot('x','a','.');This can now seamlessly be translated intoGriddata, for example taking the data binwise inxin 20 bins, and in each bin summing up points:p.binwise(x=20).sum()x[-4.392 -3.962][-3.962 -3.532][-3.532 -3.102]...[2.916 3.346][3.346 3.776][3.776 4.206]a29131456...63116377.7p.binwise(x=20).sum().plot();This is equivalent of making a weighted histogram, while the latter is faster.p.histogram(x=20).ax[-4.392 -3.962][-3.962 -3.532][-3.532 -3.102]...[2.916 3.346][3.346 3.776][3.776 4.206]29131456...63116377.7np.allclose(p.histogram(x=10).a,p.binwise(x=10).sum().a)TrueThere is also KDE in n-dimensions available, for example:p.kde(x=1000).a.plot();GridArrayscan also hold multi-dimensional values, like RGB images or here 5 values from the percentile function. Let's plot those as bands:p.binwise(x=20).quantile(q=[0.1,0.3,0.5,0.7,0.9]).plot_bands()When we specifyxwith an array, we e gives a list of points to binwise. So the resulting plot will consist of points, not bins.p.binwise(x=np.linspace(-3,3,10)).quantile(q=[0.1,0.3,0.5,0.7,0.9]).plot_bands(lines=True,filled=True,linestyles=[':','--','-'],lw=1)This is not the same as using edges as in the example below, hence also the plots look different.p.binwise(x=dm.Edges(np.linspace(-3,3,10))).quantile(q=[0.1,0.3,0.5,0.7,0.9]).plot_bands(lines=True,filled=True,linestyles=[':','--','-'],lw=1)Saving and loadingDama supports the pickle protocol, and objects can be stored like:dm.save("filename.pkl",obj)And read back like:obj=dm.read("filename.pkl")Example galleryThis is just to illustrate some different, seemingly random applications, resulting in various plots. All starting from some random data pointsfrommatplotlibimportpyplotaspltp=dm.PointData()p.x=np.random.rand(10_000)p.y=np.random.randn(p.size)*np.sin(p.x*3*np.pi)*p.xp.a=p.y/p.xfig,ax=plt.subplots(4,4,figsize=(20,20))ax=ax.flatten()# First rowp.y.plot(ax=ax[0])p.plot('x','y','.',ax=ax[1])p.plot_scatter('x','y',c='a',s=1,cmap=dm.cm.spectrum,ax=ax[2])p.interp(x=100,y=100,method="nearest").a.plot(ax=ax[3])# Second rownp.log(1+p.histogram(x=100,y=100).counts).plot(ax=ax[4])p.kde(x=100,y=100,bw=(0.02,0.05)).density.plot(cmap=dm.cm.afterburner_r,ax=ax[5])p.histogram(x=10,y=10).interp(x=100,y=100).a.plot(cmap="RdBu",ax=ax[6])p.histogram(x=100,y=100).counts.median_filter(10).plot(ax=ax[7])# Third rowp.binwise(x=100).quantile(q=[0.1,0.3,0.5,0.7,0.9]).y.plot_bands(ax=ax[8])p.binwise(x=100).quantile(q=[0.1,0.3,0.5,0.7,0.9]).y.gaussian_filter((2.5,0)).interp(x=500).plot_bands(filled=False,lines=True,linestyles=[':','--','-'],ax=ax[9])p.binwise(a=100).mean().y.plot(ax=ax[10])p.binwise(a=100).std().y.plot(ax=ax[10])p.histogram(x=100,y=100).counts.std(axis='x').plot(ax=ax[11])# Fourth rownp.log(p.histogram(x=100,y=100).counts+1).gaussian_filter(0.5).plot_contour(cmap=dm.cm.passion_r,ax=ax[12])p.histogram(x=30,y=30).gaussian_filter(1).lookup(p).plot_scatter('x','y','a',1,cmap='Spectral',ax=ax[13])h=p.histogram(y=100,x=np.logspace(-1,0,100)).a.Th[h>0].plot(ax=ax[14])h[1/3:2/3].plot(ax=ax[15])<matplotlib.collections.QuadMesh at 0x7f8a0315d1f0>