//  [python]
def moment(self, n, f_min=0., f_max=np.inf):
        '''Compute nth order moment of wave spectrum

        Parameters
        ----------
        n : int
            Order of moment
        f_min : float
            Minimum frequency to include in moment
        f_max : float
            Maximum frequency to include in moment

        Returns
        -------
        m : xarray.DataArray
            nth order moment of the wave spectrum at each point in
            time and location in the dataset

        '''
        
        if self.has_dimension('frequency', raise_error=True):

            coords = OrderedDict(self.coords)
            k = self._key_lookup('_energy')
            E = self.variables[k]
            dims = list(E.dims)
        
            # integrate frequencies
            k = self._key_lookup('_frequency')
            f = coords.pop(k).values
            ix_frequency = dims.index(k)
            f_mtx = expand_and_repeat(
                f,
                shape=E.values.shape,
                exist_dims=ix_frequency
            )
            dims.remove(k)
            
            if f_min == 0. and f_max == np.inf:
                
                m = np.trapz(E.values * f_mtx**n, f_mtx, axis=ix_frequency)
                
            else:

                if n != 0:
                    logger.warn('Computing %d-order moment using a frequency range; '
                                'Are you sure what you are doing?', n)
                    
                # integrate range of frequencies
                f_min = np.maximum(f_min, np.min(f))
                f_max = np.minimum(f_max, np.max(f))
                
                m = scipy.integrate.cumulative_trapezoid(E.values * f_mtx**n,
                                             f_mtx, axis=ix_frequency, initial=0)

                vals = []
                if self.has_dimension('time'):
                    k = self._key_lookup('_time')
                    vals.append(coords[k].values.flatten().astype(np.float))
                if self.has_dimension('location'):
                    k = self._key_lookup('_location')
                    vals.append(coords[k].values.flatten().astype(np.float))
                    
                vals.append(f.flatten())
                    
                if self.has_dimension('direction'):
                    k = self._key_lookup('_direction')
                    vals.append(coords[k].values.flatten().astype(np.float))
                
                points = tuple(vals)
                points_min = vals.copy()
                points_min[ix_frequency] = [f_min]
                points_max = vals.copy()
                points_max[ix_frequency] = [f_max]
                
                xi_min = list(zip(*[x.flatten() for x in np.meshgrid(*points_min)]))
                xi_max = list(zip(*[x.flatten() for x in np.meshgrid(*points_max)]))

                m_min = scipy.interpolate.interpn(points, m, xi_min)
                m_max = scipy.interpolate.interpn(points, m, xi_max)
                
                m = (m_max - m_min).reshape([
                    len(x)
                    for i, x in enumerate(vals)
                    if i is not ix_freqency
                ])

            # determine units
            k = self._key_lookup('_energy')
            E_units = self.variables[k].attrs['units']
            k = self._key_lookup('_frequency')
            f_units = self.variables[k].attrs['units']
            units = E_units + ('*((%s)^%d)' % (f_units, n+1))
            units = simplify(units)

            return xr.DataArray(m, dims=dims, coords=coords,
                                attrs=dict(units=units))