Skip to content
This repository

Meshgrid enhancements (>2-D, sparse grids, matrix indexing) #192

Merged
merged 5 commits into from almost 2 years ago

5 participants

Ralf Gommers Travis E. Oliphant Luke Campbell Charles Harris Per A. Brodtkorb
numpy/lib/function_base.py
((15 lines not shown))
3207 3211
 
3208 3212
     Parameters
3209 3213
     ----------
3210  
-    x, y : ndarray
3211  
-        Two 1-D arrays representing the x and y coordinates of a grid.
  3214
+    x1, x2,..., xn : array_like
  3215
+        1-D arrays representing the coordinates of a grid.
  3216
+    indexing : {'xy', 'ij'}, optional
  3217
+        Cartesian ('xy', default) or matrix ('ij') indexing of output.
  3218
+        See Notes for more details.
  3219
+    sparse : bool, optional
  3220
+         If True a sparse grid is returned in order to conserve memory.
  3221
+         Default is False.
  3222
+    copy : bool, optional
  3223
+        If False, a view into the original arrays are returned in
  3224
+        order to conserve memory.  Default is True.  Please note that
  3225
+        ``sparse=False, copy=False`` will likely return non-contiguous arrays.
  3226
+        Furthermore, more than one element of a broadcasted array may refer to
2
Charles Harris Owner
charris added a note January 31, 2012

broadcast, not broadcasted. Although languages do evolve ;)

Ralf Gommers Owner

fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Charles Harris charris commented on the diff January 31, 2012
numpy/lib/function_base.py
((40 lines not shown))
3217  
-        return `X`, `Y` where `X` and `Y` are ``(Ny, Nx)`` shaped arrays
3218  
-        with the elements of `x` and y repeated to fill the matrix along
3219  
-        the first dimension for `x`, the second for `y`.
  3232
+    X1, X2,..., XN : ndarray
  3233
+        For vectors `x1`, `x2`,..., 'xn' with lengths ``Ni=len(xi)`` ,
  3234
+        return ``(N1, N2, N3,...Nn)`` shaped arrays if indexing='ij'
  3235
+        or ``(N2, N1, N3,...Nn)`` shaped arrays if indexing='xy'
  3236
+        with the elements of `xi` repeated to fill the matrix along
  3237
+        the first dimension for `x1`, the second for `x2` and so on.
  3238
+
  3239
+    Notes
  3240
+    -----
  3241
+    This function supports both indexing conventions through the indexing keyword
  3242
+    argument.  Giving the string 'ij' returns a meshgrid with matrix indexing,
  3243
+    while 'xy' returns a meshgrid with Cartesian indexing.  In the 2-D case
  3244
+    with inputs of length M and N, the outputs are of shape (N, M) for 'xy'
7
Charles Harris Owner
charris added a note January 31, 2012

Maybe explain this with a connection to image plots, where the 'x' typically runs left to right and is the 'fast' index. Or is that what this is about. Where would one use 'ij' ?

Travis E. Oliphant Owner

I agree. I find the "matrix indexing" versus "Cartesian indexing" description hard to understand. I think you are talking about "image" conventions versus "plotting" conventions. Is that correct?

Ralf Gommers Owner

Correct, image convention vs. just standard array/matrix indexing. Since this is the most confusing part apparently (see also discussion on this point at http://projects.scipy.org/numpy/ticket/966), I'm tempted to just leave out the indexing keyword after all. This seems to be confusing for Matlab users too, see for example the comments at http://blogs.mathworks.com/loren/2007/06/21/indexing-terminology/.

There really isn't an ideal solution here.

Note that mgrid and ogrid use 'ij', while currently meshgrid uses 'xy':

In [35]: mgrid[0:2, 3:5]
Out[35]: 
array([[[0, 0],
        [1, 1]],

       [[3, 4],
        [3, 4]]])

In [36]: meshgrid([0, 1], [3, 4])
Out[36]: 
[array([[0, 1],
       [0, 1]]), array([[3, 3],
       [4, 4]])]
Charles Harris Owner
charris added a note February 05, 2012

Maybe call ir 'image' indexing? I find in practice it is easier to settle on a single convention ('ij'), always put x in the first index, and just transpose for showing images. Mixing the conventions leads to all sorts of confusion.

Ralf Gommers Owner

Only using 'ij' would be best probably, but since currently meshgrid uses 'xy' we can't do that.

Charles Harris Owner
charris added a note February 05, 2012

Oops, see, I already got confused ;) I think a different choice of names would clarify things, 'xy' seems ok, 'standard' would be another option, and then maybe 'image' for the second choice, with a short explanation (images are displayed left-right, top-bottom) and a translation for Matlab users.

Ralf Gommers Owner
rgommers added a note May 28, 2012

ij --> image sounds OK to me. Although I forgot the details by now. With added explanation it should work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Charles Harris charris commented on the diff January 31, 2012
numpy/lib/function_base.py
((134 lines not shown))
  3303
+    sparse = kwargs.get('sparse', False)
  3304
+    indexing = kwargs.get('indexing', 'xy')
  3305
+    if not indexing in ['xy', 'ij']:
  3306
+        raise ValueError("Valid values for `indexing` are 'xy' and 'ij'.")
  3307
+
  3308
+    s0 = (1,) * ndim
  3309
+    output = [x.reshape(s0[:i] + (-1,) + s0[i + 1::]) for i, x in enumerate(args)]
  3310
+
  3311
+    shape = [x.size for x in output]
  3312
+
  3313
+    if indexing == 'xy':
  3314
+        # switch first and second axis
  3315
+        output[0].shape = (1, -1) + (1,)*(ndim - 2)
  3316
+        output[1].shape = (-1, 1) + (1,)*(ndim - 2)
  3317
+        shape[0], shape[1] = shape[1], shape[0]
  3318
+
2
Charles Harris Owner
charris added a note January 31, 2012

So 'xy' indexing only affects the first two axis?

Ralf Gommers Owner

yes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
numpy/lib/tests/test_function_base.py
((19 lines not shown))
  1044
+        z = [8, 9]
  1045
+        assert_(meshgrid(x, y)[0].shape == (4, 3))
  1046
+        assert_(meshgrid(x, y, indexing='ij')[0].shape == (3, 4))
  1047
+        assert_(meshgrid(x, y, z)[0].shape == (4, 3, 2))
  1048
+        assert_(meshgrid(x, y, z, indexing='ij')[0].shape == (3, 4, 2))
  1049
+
  1050
+        assert_raises(ValueError, meshgrid, x, y, indexing='notvalid')
  1051
+
  1052
+    def test_sparse(self):
  1053
+        [X, Y] = meshgrid([1, 2, 3], [4, 5, 6, 7], sparse=True)
  1054
+        assert_(all(X == array([[1, 2, 3]])))
  1055
+        assert_(all(Y == array([[4],
  1056
+                               [5],
  1057
+                               [6],
  1058
+                               [7]])))
  1059
+
2
Charles Harris Owner
charris added a note January 31, 2012

Any reason not to put that on a single line?

Ralf Gommers Owner

not really, changed that now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Travis E. Oliphant
Owner

It seems that this PR can be merged. Confusion in the naming can be improved later (and already exists between the default mgrid and meshgrid conventions).

Ralf Gommers
Owner

Would be better to fix it before/when merging imho, otherwise it will simply be forgotten. Sorry I haven't made the time to finish this off yet.

Luke Campbell

Looks really good, can't wait for this to be merged.

Travis E. Oliphant teoliphant merged commit 0b2bfa9 into from July 17, 2012
Travis E. Oliphant teoliphant closed this July 17, 2012
Travis E. Oliphant teoliphant referenced this pull request from a commit in teoliphant/numpy July 17, 2012
Travis E. Oliphant BUG: Fix some tests in PR #192 5b4e61b
Travis E. Oliphant teoliphant referenced this pull request from a commit July 17, 2012
Commit has since been removed from the repository and is no longer available.
Travis E. Oliphant teoliphant referenced this pull request from a commit July 17, 2012
Commit has since been removed from the repository and is no longer available.
Travis E. Oliphant teoliphant referenced this pull request from a commit July 17, 2012
Commit has since been removed from the repository and is no longer available.
Travis E. Oliphant teoliphant referenced this pull request from a commit July 17, 2012
Commit has since been removed from the repository and is no longer available.
Ralf Gommers rgommers deleted the branch June 03, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
139  numpy/lib/function_base.py
@@ -3200,62 +3200,135 @@ def add_newdoc(place, obj, doc):
3200 3200
         pass
3201 3201
 
3202 3202
 
3203  
-# From matplotlib
3204  
-def meshgrid(x,y):
  3203
+# Based on scitools meshgrid
  3204
+def meshgrid(*xi, **kwargs):
3205 3205
     """
3206  
-    Return coordinate matrices from two coordinate vectors.
  3206
+    Return coordinate matrices from two or more coordinate vectors.
  3207
+
  3208
+    Make N-D coordinate arrays for vectorized evaluations of
  3209
+    N-D scalar/vector fields over N-D grids, given
  3210
+    one-dimensional coordinate arrays x1, x2,..., xn.
3207 3211
 
3208 3212
     Parameters
3209 3213
     ----------
3210  
-    x, y : ndarray
3211  
-        Two 1-D arrays representing the x and y coordinates of a grid.
  3214
+    x1, x2,..., xn : array_like
  3215
+        1-D arrays representing the coordinates of a grid.
  3216
+    indexing : {'xy', 'ij'}, optional
  3217
+        Cartesian ('xy', default) or matrix ('ij') indexing of output.
  3218
+        See Notes for more details.
  3219
+    sparse : bool, optional
  3220
+         If True a sparse grid is returned in order to conserve memory.
  3221
+         Default is False.
  3222
+    copy : bool, optional
  3223
+        If False, a view into the original arrays are returned in
  3224
+        order to conserve memory.  Default is True.  Please note that
  3225
+        ``sparse=False, copy=False`` will likely return non-contiguous arrays.
  3226
+        Furthermore, more than one element of a broadcast array may refer to
  3227
+        a single memory location.  If you need to write to the arrays, make
  3228
+        copies first.
3212 3229
 
3213 3230
     Returns
3214 3231
     -------
3215  
-    X, Y : ndarray
3216  
-        For vectors `x`, `y` with lengths ``Nx=len(x)`` and ``Ny=len(y)``,
3217  
-        return `X`, `Y` where `X` and `Y` are ``(Ny, Nx)`` shaped arrays
3218  
-        with the elements of `x` and y repeated to fill the matrix along
3219  
-        the first dimension for `x`, the second for `y`.
  3232
+    X1, X2,..., XN : ndarray
  3233
+        For vectors `x1`, `x2`,..., 'xn' with lengths ``Ni=len(xi)`` ,
  3234
+        return ``(N1, N2, N3,...Nn)`` shaped arrays if indexing='ij'
  3235
+        or ``(N2, N1, N3,...Nn)`` shaped arrays if indexing='xy'
  3236
+        with the elements of `xi` repeated to fill the matrix along
  3237
+        the first dimension for `x1`, the second for `x2` and so on.
  3238
+
  3239
+    Notes
  3240
+    -----
  3241
+    This function supports both indexing conventions through the indexing keyword
  3242
+    argument.  Giving the string 'ij' returns a meshgrid with matrix indexing,
  3243
+    while 'xy' returns a meshgrid with Cartesian indexing.  In the 2-D case
  3244
+    with inputs of length M and N, the outputs are of shape (N, M) for 'xy'
  3245
+    indexing and (M, N) for 'ij' indexing.  In the 3-D case with inputs of
  3246
+    length M, N and P, outputs are of shape (N, M, P) for 'xy' indexing and (M,
  3247
+    N, P) for 'ij' indexing.  The difference is illustrated by the following
  3248
+    code snippet::
  3249
+
  3250
+        xv, yv = meshgrid(x, y, sparse=False, indexing='ij')
  3251
+        for i in range(nx):
  3252
+            for j in range(ny):
  3253
+                # treat xv[i,j], yv[i,j]
  3254
+
  3255
+        xv, yv = meshgrid(x, y, sparse=False, indexing='xy')
  3256
+        for i in range(nx):
  3257
+            for j in range(ny):
  3258
+                # treat xv[j,i], yv[j,i]
3220 3259
 
3221 3260
     See Also
3222 3261
     --------
3223 3262
     index_tricks.mgrid : Construct a multi-dimensional "meshgrid"
3224  
-                         using indexing notation.
  3263
+                     using indexing notation.
3225 3264
     index_tricks.ogrid : Construct an open multi-dimensional "meshgrid"
3226  
-                         using indexing notation.
  3265
+                     using indexing notation.
3227 3266
 
3228 3267
     Examples
3229 3268
     --------
3230  
-    >>> X, Y = np.meshgrid([1,2,3], [4,5,6,7])
3231  
-    >>> X
3232  
-    array([[1, 2, 3],
3233  
-           [1, 2, 3],
3234  
-           [1, 2, 3],
3235  
-           [1, 2, 3]])
3236  
-    >>> Y
3237  
-    array([[4, 4, 4],
3238  
-           [5, 5, 5],
3239  
-           [6, 6, 6],
3240  
-           [7, 7, 7]])
  3269
+    >>> nx, ny = (3, 2)
  3270
+    >>> x = np.linspace(0, 1, nx)
  3271
+    >>> y = np.linspace(0, 1, ny)
  3272
+    >>> xv, yv = meshgrid(x, y)
  3273
+    >>> xv
  3274
+    array([[ 0. ,  0.5,  1. ],
  3275
+           [ 0. ,  0.5,  1. ]])
  3276
+    >>> yv
  3277
+    array([[ 0.,  0.,  0.],
  3278
+           [ 1.,  1.,  1.]])
  3279
+    >>> xv, yv = meshgrid(x, y, sparse=True)  # make sparse output arrays
  3280
+    >>> xv
  3281
+    array([[ 0. ,  0.5,  1. ]])
  3282
+    >>> yv
  3283
+    array([[ 0.],
  3284
+           [ 1.]])
3241 3285
 
3242 3286
     `meshgrid` is very useful to evaluate functions on a grid.
3243 3287
 
3244 3288
     >>> x = np.arange(-5, 5, 0.1)
3245 3289
     >>> y = np.arange(-5, 5, 0.1)
3246  
-    >>> xx, yy = np.meshgrid(x, y)
3247  
-    >>> z = np.sin(xx**2+yy**2)/(xx**2+yy**2)
  3290
+    >>> xx, yy = meshgrid(x, y, sparse=True)
  3291
+    >>> z = np.sin(xx**2 + yy**2) / (xx**2 + yy**2)
  3292
+    >>> h = plt.contourf(x,y,z)
3248 3293
 
3249 3294
     """
3250  
-    x = asarray(x)
3251  
-    y = asarray(y)
3252  
-    numRows, numCols = len(y), len(x)  # yes, reversed
3253  
-    x = x.reshape(1,numCols)
3254  
-    X = x.repeat(numRows, axis=0)
  3295
+    if len(xi) < 2:
  3296
+        msg = 'meshgrid() takes 2 or more arguments (%d given)' % int(len(xi) > 0)
  3297
+        raise ValueError(msg)
  3298
+
  3299
+    args = np.atleast_1d(*xi)
  3300
+    ndim = len(args)
  3301
+
  3302
+    copy_ = kwargs.get('copy', True)
  3303
+    sparse = kwargs.get('sparse', False)
  3304
+    indexing = kwargs.get('indexing', 'xy')
  3305
+    if not indexing in ['xy', 'ij']:
  3306
+        raise ValueError("Valid values for `indexing` are 'xy' and 'ij'.")
  3307
+
  3308
+    s0 = (1,) * ndim
  3309
+    output = [x.reshape(s0[:i] + (-1,) + s0[i + 1::]) for i, x in enumerate(args)]
  3310
+
  3311
+    shape = [x.size for x in output]
  3312
+
  3313
+    if indexing == 'xy':
  3314
+        # switch first and second axis
  3315
+        output[0].shape = (1, -1) + (1,)*(ndim - 2)
  3316
+        output[1].shape = (-1, 1) + (1,)*(ndim - 2)
  3317
+        shape[0], shape[1] = shape[1], shape[0]
  3318
+
  3319
+    if sparse:
  3320
+        if copy_:
  3321
+            return [x.copy() for x in output]
  3322
+        else:
  3323
+            return output
  3324
+    else:
  3325
+        # Return the full N-D matrix (not only the 1-D vector)
  3326
+        if copy_:
  3327
+            mult_fact = np.ones(shape, dtype=int)
  3328
+            return [x * mult_fact for x in output]
  3329
+        else:
  3330
+            return np.broadcast_arrays(*output)
3255 3331
 
3256  
-    y = y.reshape(numRows,1)
3257  
-    Y = y.repeat(numCols, axis=1)
3258  
-    return X, Y
3259 3332
 
3260 3333
 def delete(arr, obj, axis=None):
3261 3334
     """
28  numpy/lib/tests/test_function_base.py
@@ -1026,6 +1026,34 @@ def test_simple(self):
1026 1026
                                [6, 6, 6],
1027 1027
                                [7, 7, 7]])))
1028 1028
 
  1029
+    def test_single_input(self):
  1030
+        assert_raises(ValueError, meshgrid, np.arange(5))
  1031
+
  1032
+    def test_indexing(self):
  1033
+        x = [1, 2, 3]
  1034
+        y = [4, 5, 6, 7]
  1035
+        [X, Y] = meshgrid(x, y, indexing='ij')
  1036
+        assert_(all(X == array([[1, 1, 1, 1],
  1037
+                               [2, 2, 2, 2],
  1038
+                               [3, 3, 3, 3]])))
  1039
+        assert_(all(Y == array([[4, 5, 6, 7],
  1040
+                               [4, 5, 6, 7],
  1041
+                               [4, 5, 6, 7]])))
  1042
+
  1043
+        # Test expected shapes:
  1044
+        z = [8, 9]
  1045
+        assert_(meshgrid(x, y)[0].shape == (4, 3))
  1046
+        assert_(meshgrid(x, y, indexing='ij')[0].shape == (3, 4))
  1047
+        assert_(meshgrid(x, y, z)[0].shape == (4, 3, 2))
  1048
+        assert_(meshgrid(x, y, z, indexing='ij')[0].shape == (3, 4, 2))
  1049
+
  1050
+        assert_raises(ValueError, meshgrid, x, y, indexing='notvalid')
  1051
+
  1052
+    def test_sparse(self):
  1053
+        [X, Y] = meshgrid([1, 2, 3], [4, 5, 6, 7], sparse=True)
  1054
+        assert_(all(X == array([[1, 2, 3]])))
  1055
+        assert_(all(Y == array([[4], [5], [6], [7]])))
  1056
+
1029 1057
 
1030 1058
 class TestPiecewise(TestCase):
1031 1059
     def test_simple(self):
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.