Just a note that get_neighbours could be optimized in a few places. Currently we seem to generate a number at a time and then perform valid checks and then transform, causing a lot of non-vectorized if checks.
This could be vectorized by generating n numbers, validating them together and then performing the transformation on all elements.
Just rough pseudo code
neighoubrs = np.array([])
while len(neighbours) < n:
samples = rs.normal(value, std, size=n - len(neighbours))
neighbours = np.append(neighbours, samples[<condition>])
if transform:
neighbours = self._transform_vector(neighbours)
return neighbours