add gradient function in numeric package

This commit is contained in:
wyq 2023-08-22 09:25:11 +08:00
parent 779acb01b5
commit ed90ae3529
9 changed files with 254 additions and 30 deletions

View File

@ -1,14 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<MeteoInfo File="milconfig.xml" Type="configurefile">
<Path OpenPath="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\volume">
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite\FY"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\burf"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\awx"/>
<Path OpenPath="D:\Working\MIScript\Jython\mis\array">
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\LaSW"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\LaSW\ZhengZhou"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\savefig"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\no_opengl"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d"/>
@ -16,19 +12,23 @@
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\surf"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\volume"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\array\complex"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\array"/>
</Path>
<File>
<OpenedFiles>
<OpenedFile File="D:\MyProgram\java\MeteoInfoDev\toolbox\meteoview3d\_reload.py"/>
<OpenedFile File="D:\MyProgram\java\MeteoInfoDev\toolbox\meteoview3d\mainGUI.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\radar\radar_bz2_geo.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\volume\volumeplot_sagittal_specular.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\array\gradient_1.py"/>
</OpenedFiles>
<RecentFiles>
<RecentFile File="D:\MyProgram\java\MeteoInfoDev\toolbox\meteoview3d\_reload.py"/>
<RecentFile File="D:\MyProgram\java\MeteoInfoDev\toolbox\meteoview3d\mainGUI.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\io\radar\radar_bz2_geo.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\volume\volumeplot_sagittal_specular.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\array\gradient_1.py"/>
</RecentFiles>
</File>
<Font>

View File

@ -20,8 +20,8 @@ class NDArray(object):
if not isinstance(array, Array):
array = ArrayUtil.array(array, None)
if array.getRank() == 0 and array.getDataType() != DataType.STRUCTURE:
array = ArrayUtil.array([array.getIndexIterator().getObjectNext()])
# if array.getRank() == 0 and array.getDataType() != DataType.STRUCTURE:
# array = ArrayUtil.array([array.getIndexIterator().getObjectNext()])
self._array = array
self.ndim = array.getRank()
@ -153,7 +153,7 @@ class NDArray(object):
indices = nindices
if len(indices) != self.ndim:
print 'indices must be ' + str(self.ndim) + ' dimensions!'
print('indices must be ' + str(self.ndim) + ' dimensions!')
raise IndexError()
ranges = []
@ -582,6 +582,24 @@ class NDArray(object):
r = NDArray(ArrayMath.inValues(self._array, other))
return r
def all(self, axis=None):
"""
Test whether all array element along a given axis evaluates to True.
:param x: (*array_like or list*) Input values.
:param axis: (*int*) Axis along which a logical OR reduction is performed.
The default (axis = None) is to perform a logical OR over all the
dimensions of the input array.
:returns: (*array_like*) All result
"""
if axis is None:
return ArrayMath.all(self._array)
else:
if axis < 0:
axis += self.ndim
return NDArray(ArrayMath.all(self._array, axis))
def any(self, axis=None):
"""
Test whether any array element along a given axis evaluates to True.
@ -942,7 +960,7 @@ class NDArray(object):
:param axis: (*int*) Axis along which the standard deviation is computed.
The default is to compute the standard deviation of the flattened array.
:param ddof: (*int*) Delta Degrees of Freedom: the divisor used in the calculation is
N - ddof, where N represents the number of elements. By default ddof is zero.
N - ddof, where N represents the number of elements. By default, ddof is zero.
returns: (*array_like*) Standart deviation result.
"""
@ -1222,7 +1240,7 @@ class NDArray(object):
:param indices: (*array_like*) The indices of the values to extract.
:param axis: (*int*) The axis over which to select values.
:returns: (*array*) The returned array has the same type as a.
:returns: (*array*) The returned array has the same type as this array.
"""
if isinstance(indices, (list, tuple)):
indices = NDArray(indices)

View File

@ -1,6 +1,6 @@
# coding=utf-8
from .numeric import asarray, array
from .numeric import asarray, array, isscalar
from ._ndarray import NDArray
from .dimarray import DimArray
from org.meteoinfo.ndarray.math import ArrayUtil, ArrayMath
@ -39,6 +39,9 @@ def ndim(a):
>>> np.ndim(1)
0
"""
if isscalar(a):
return 0
try:
return a.ndim
except AttributeError:

View File

@ -110,14 +110,15 @@ def array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0):
elif miutil.iscomplex(object):
a = NDArray(JythonUtil.toComplexArray(object))
if a is None:
if isinstance(dtype, basestring):
dtype = _dtype.DataType(dtype)
if not dtype is None:
dtype = dtype._dtype
if a is None:
a = NDArray(ArrayUtil.array(object, dtype))
else:
a = NDArray(object)
if a.ndim < ndmin:
shape = []
@ -1233,12 +1234,7 @@ def all(x, axis=None):
if isinstance(x, list):
x = array(x)
if axis is None:
return ArrayMath.all(x._array)
else:
if axis < 0:
axis += x.ndim
return NDArray(ArrayMath.all(x._array, axis))
return x.all(axis)
def isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False):
"""

View File

@ -1,10 +1,11 @@
from mipylib import numeric as np
from ..core import numeric as _nx
from ..core import dtype
from ..core._ndarray import NDArray
from ..core.fromnumeric import (ravel, nonzero)
from org.meteoinfo.ndarray.math import ArrayMath
__all__ = ['angle','extract', 'place', 'grid_edge']
__all__ = ['angle','extract', 'place', 'grid_edge', 'gradient']
def extract(condition, arr):
@ -161,3 +162,207 @@ def angle(z, deg=False):
if deg:
a *= 180 / _nx.pi
return a
def gradient(f, *varargs, **kwargs):
"""
Return the gradient of an N-dimensional array.
The gradient is computed using second order accurate central differences
in the interior points and either first or second order accurate one-sides
(forward or backwards) differences at the boundaries.
The returned gradient hence has the same shape as the input array.
Parameters
----------
f : array_like
An N-dimensional array containing samples of a scalar function.
varargs : list of scalar or array, optional
Spacing between f values. Default unitary spacing for all dimensions.
Spacing can be specified using:
1. single scalar to specify a sample distance for all dimensions.
2. N scalars to specify a constant sample distance for each dimension.
i.e. `dx`, `dy`, `dz`, ...
3. N arrays to specify the coordinates of the values along each
dimension of F. The length of the array must match the size of
the corresponding dimension
4. Any combination of N scalars/arrays with the meaning of 2. and 3.
If `axis` is given, the number of varargs must equal the number of axes.
Default: 1.
edge_order : {1, 2}, optional
Gradient is calculated using N-th order accurate differences
at the boundaries. Default: 1.
axis : None or int or tuple of ints, optional
Gradient is calculated only along the given axis or axes
The default (axis = None) is to calculate the gradient for all the axes
of the input array. axis may be negative, in which case it counts from
the last to the first axis.
Returns
-------
gradient : ndarray or list of ndarray
A list of ndarrays (or a single ndarray if there is only one dimension)
corresponding to the derivatives of f with respect to each dimension.
Each derivative has the same shape as f.
"""
f = _nx.asanyarray(f)
N = f.ndim # number of dimensions
axis = kwargs.pop('axis', None)
edge_order = kwargs.pop('edge_order', 1)
if axis is None:
axes = tuple(range(N))
else:
axes = _nx.normalize_axis_tuple(axis, N)
len_axes = len(axes)
n = len(varargs)
if n == 0:
# no spacing argument - use 1 in all axes
dx = [1.0] * len_axes
elif n == 1 and np.ndim(varargs[0]) == 0:
# single scalar for all axes
dx = varargs * len_axes
elif n == len_axes:
# scalar or 1d array for each axis
dx = list(varargs)
for i, distances in enumerate(dx):
distances = _nx.asanyarray(distances)
if distances.ndim == 0:
continue
elif distances.ndim != 1:
raise ValueError("distances must be either scalars or 1d")
if len(distances) != f.shape[axes[i]]:
raise ValueError("when 1d, distances must match "
"the length of the corresponding dimension")
if distances.dtype == dtype.int:
# Convert numpy integer types to float to avoid modular
# arithmetic in np.diff(distances).
distances = distances.astype(dtype.float)
diffx = _nx.diff(distances)
# if distances are constant reduce to the scalar case
# since it brings a consistent speedup
if (diffx == diffx[0]).all():
diffx = diffx[0]
dx[i] = diffx
else:
raise TypeError("invalid number of arguments")
if edge_order > 2:
raise ValueError("'edge_order' greater than 2 not supported")
# use central differences on interior and one-sided differences on the
# endpoints. This preserves second order-accuracy over the full domain.
outvals = []
# create slice objects --- initially all are [:, :, ..., :]
slice1 = [slice(None)]*N
slice2 = [slice(None)]*N
slice3 = [slice(None)]*N
slice4 = [slice(None)]*N
otype = f.dtype
if otype == dtype.int:
otype = dtype.float
for axis, ax_dx in zip(axes, dx):
if f.shape[axis] < edge_order + 1:
raise ValueError(
"Shape of array too small to calculate a numerical gradient, "
"at least (edge_order + 1) elements are required.")
# result allocation
out = np.empty_like(f, dtype=otype)
# spacing for the current axis
uniform_spacing = np.ndim(ax_dx) == 0
# Numerical differentiation: 2nd order interior
slice1[axis] = slice(1, -1)
slice2[axis] = slice(None, -2)
slice3[axis] = slice(1, -1)
slice4[axis] = slice(2, None)
if uniform_spacing:
out[tuple(slice1)] = (f[tuple(slice4)] - f[tuple(slice2)]) / (2. * ax_dx)
else:
dx1 = ax_dx[0:-1]
dx2 = ax_dx[1:]
a = -(dx2)/(dx1 * (dx1 + dx2))
b = (dx2 - dx1) / (dx1 * dx2)
c = dx1 / (dx2 * (dx1 + dx2))
# fix the shape for broadcasting
shape = np.ones(N, dtype=dtype.int)
shape[axis] = -1
a.shape = b.shape = c.shape = shape
# 1D equivalent -- out[1:-1] = a * f[:-2] + b * f[1:-1] + c * f[2:]
out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)]
# Numerical differentiation: 1st order edges
if edge_order == 1:
slice1[axis] = 0
slice2[axis] = 1
slice3[axis] = 0
dx_0 = ax_dx if uniform_spacing else ax_dx[0]
# 1D equivalent -- out[0] = (f[1] - f[0]) / (x[1] - x[0])
out[tuple(slice1)] = (f[tuple(slice2)] - f[tuple(slice3)]) / dx_0
slice1[axis] = -1
slice2[axis] = -1
slice3[axis] = -2
dx_n = ax_dx if uniform_spacing else ax_dx[-1]
# 1D equivalent -- out[-1] = (f[-1] - f[-2]) / (x[-1] - x[-2])
out[tuple(slice1)] = (f[tuple(slice2)] - f[tuple(slice3)]) / dx_n
# Numerical differentiation: 2nd order edges
else:
slice1[axis] = 0
slice2[axis] = 0
slice3[axis] = 1
slice4[axis] = 2
if uniform_spacing:
a = -1.5 / ax_dx
b = 2. / ax_dx
c = -0.5 / ax_dx
else:
dx1 = ax_dx[0]
dx2 = ax_dx[1]
a = -(2. * dx1 + dx2)/(dx1 * (dx1 + dx2))
b = (dx1 + dx2) / (dx1 * dx2)
c = - dx1 / (dx2 * (dx1 + dx2))
# 1D equivalent -- out[0] = a * f[0] + b * f[1] + c * f[2]
out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)]
slice1[axis] = -1
slice2[axis] = -3
slice3[axis] = -2
slice4[axis] = -1
if uniform_spacing:
a = 0.5 / ax_dx
b = -2. / ax_dx
c = 1.5 / ax_dx
else:
dx1 = ax_dx[-2]
dx2 = ax_dx[-1]
a = (dx2) / (dx1 * (dx1 + dx2))
b = - (dx2 + dx1) / (dx1 * dx2)
c = (2. * dx2 + dx1) / (dx2 * (dx1 + dx2))
# 1D equivalent -- out[-1] = a * f[-3] + b * f[-2] + c * f[-1]
out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)]
outvals.append(out)
# reset the slice object in this dimension to ":"
slice1[axis] = slice(None)
slice2[axis] = slice(None)
slice3[axis] = slice(None)
slice4[axis] = slice(None)
if len_axes == 1:
return outvals[0]
else:
return outvals

View File

@ -6022,9 +6022,11 @@ public class ArrayMath {
public static Array setSection(Array a, List<Range> ranges, Array v) throws InvalidRangeException {
Array r = a.section(ranges);
IndexIterator iter = r.getIndexIterator();
if (r.getSize() != v.getSize()) {
if (r.getShape() != v.getShape()) {
v = ArrayUtil.broadcast(v, r.getShape());
}
}
Index index = v.getIndex();
while (iter.hasNext()) {
iter.setObjectNext(v.getObject(index));

View File

@ -652,7 +652,7 @@ public class ArrayUtil {
} else {
if (dt == null)
dt = ArrayMath.getDataType(data);
Array a = Array.factory(dt, new int[]{1});
Array a = Array.factory(dt, new int[0]);
if (data instanceof String) {
a.setString(0, (String)data);
} else {