I am trying to figure out how NumPy's mgrid function works and I read that the syntax creating a simple array works like this:
Arguments are separated by :. First number gives the start, second number the stop.
If you give a real number e.g. 1 as third argument you fix the step-size (stop excluded)
np.mgrid[-5:5:1]
[-5 -4 -3 -2 -1 0 1 2 3 4]If you give an imaginary third argument you give the number of elements as the coefficient. This time stop included. E.g. 5j gives
np.mgrid[-5:5:5j]
[-5. -2.5 0. 2.5 5. ]Straightforward enough. I come from a mathematics background and got to know complex numbers with a real and an imaginary part.
z in C --> z = x + iy
with real valued x and y. So I wondered what would happen if one used a complex number that has both a real and an imaginary part and I got this result
np.mgrid[-5:5:1+5j]
[-5. -2.56039219 -0.12078439 2.31882342 4.75843122]Can someone explain what just happened internally?
22 Answers
First, it's easy to understand that
np.mgrid[-5:5:5j]
[-5. -2.56039219 -0.12078439 2.31882342 4.75843122]means that 5 numbers between -5 and 5 with an equivalent step size.
5j can be understood as 0 + 5j. The total numbers between -5 and 5 are determined by the length of the complex number instead of the imaginary part.
In np.mgrid, we can see the internal code that it uses:
step= abs(Re+Imj)
length = int(step_size)
if step != 1: step = (Interval)/float(step-1)Apparently, the real part of the complex number increases the number and changes the step size.
For example:
[-5:5:4+5j]
6 = int(abs(4+5j))
array([-5. , -3.14921894, -1.29843788, 0.55234318, 2.40312424, 4.2539053 ])The number would be 6. And we can compute the step_size by
step = (key.stop-start)/float(step-1)where key.stop is -5, start equals 5. The final step equals to
[in]: 10/(abs(4+5j) -1)
[out]: 1.8507810593582121It also shows that np.mgrid[-5:5:4+5j] equals np.mgrid[-5:5:5+4j]
However, the question is how to use it? I don't know, but I think it should be understood from
np.mgrid[Re1+Xj : Re2 +Yj: Re3+ Zj] mgrid uses np.lib.index_tricks.ndgrid class. The use of the complex step is complicated. I remember at one time it use np.linspace, taking the integer part of the 5j as the number of samples. Now I don't see use of linspace. It's either arange or some home-grown equivalent, which is presumably faster when making a multidimensional grid.
But I've been able to replicate the numbers with:
In [247]: step = 5j
In [248]: np.mgrid[0:5:5j]
Out[248]: array([0. , 1.25, 2.5 , 3.75, 5. ])
In [249]: x = 5/(float(np.abs(step))-1)
In [250]: np.arange(0,5+x,x)
Out[250]: array([0. , 1.25, 2.5 , 3.75, 5. ])and for:
In [251]: step = 1+5j
In [252]: x = 5/(float(np.abs(step))-1)
In [253]: x
Out[253]: 1.219803902718557
In [254]: np.mgrid[0:5:step]
Out[254]: array([0. , 1.2198039 , 2.43960781, 3.65941171, 4.87921561])
In [255]: np.arange(0,5+x,x)
Out[255]:
array([0. , 1.2198039 , 2.43960781, 3.65941171, 4.87921561, 6.09901951])
In [256]: np.arange(0,5,x)
Out[256]: array([0. , 1.2198039 , 2.43960781, 3.65941171, 4.87921561])This isn't an exact duplicate, but the numbers match well enough to identify the step size that it's using.
In any case using a full complex number is not documented. It works, but the result is not easily understood.