I have a 2D array (typical size about 400x100) as shown (it looks like a trapezium because elements in the lower right are nan). For each element in the array, I want to perform a numerical integral along the column for several elements (of the order of ~10 elements). In physics language think of the colour as the magnitude of the force, and I want to find the work done by calculating th integral of Fdz. I can use a double for-loop and use trap
to do the job, but are there other more efficient ways (probably mkaing use of arrays and vectorization) to do it in Matlab or python? My ultimate goal is to find the point where the evaluated integral is the largest. So from the image in which yellow represents large value, we expect the integral to be the largest somewhere on the right side above the dotted line.
Also, it is relatively easy if the number of points I want to integrate is an integer, but what if I want to integrate, say, 7.5 points? I am thinking of using fit
to interpolate the points, but I'm not sure if that's over-complicating the task.
You can use cumsum
to speedup trap
. Calculating the cummulative sum (1-dimensional integral images proposed by @Benjamin)
>>> import numpy as np
>>> csdata = np.cumsum(data, axis=1)
Integrate with a discrete length
>>> npoints = 6
>>> result = np.zeros_like(data)
>>> result[:-npoints, :] = csdata[npoints:, :] - csdata[:-npoints, :]
The result
is a vectorization of cumdata[i+npoints, j] - cumdata[i, j]
for every i, j
in the image. It will fill with zeros last npoints
rows. You can reflect
the boundary with np.pad
if you want to prevent this.
For non-discrete intervals, you can work with interpolations:
>>> from scipy.interpolate import interp2d
>>> C = 0.5 # to interpolate every npoints+C pixels
>>> y, x = np.mgrid[:data.shape[0], :data.shape[1]]
>>> ynew, xnew = np.mgrid[C:data.shape[0]+C, :data.shape[1]]
>>> f = interp2d(x, y, csdata)
>>> csnew = f(xnew, ynew)
The above shifts a regular grid C
pixels in y
direction, and interpolates the cummulative data csdata
at those points (in practice, it vectorices interpolation for every pixel).
Then the integral of npoints+C
length can be obtained as
>>> npoints = 6
>>> result = np.zeros_like(data)
>>> result[:-npoints, :] = csnew[npoints:, :] - csdata[:-npoints, :]
Note that the upper bound is now csnew
(a shift of 6 actually gets the 6.5 element), making it integrate every 6.5 points in practice.
You can then find the maximum point as
>>> idx = np.argmax(result.ravel()) # ravel to get the 1D maximum point
>>> maxy, maxx = np.unravel_index(idx, data.shape) # get 2D coordinates of idx