Repository: numpy/numpy

Issue:
Add order='F' option for numpy.eye
For example, in the simplified case that the matrix is square:

```python
id_mat = np.zeros((N, N), order='F')
id_mat.flat[::N + 1] = 1.0
```

I'm happy to send a PR to add this as an extra kwarg, but I don't want to do it unless maintainers are open to it.

It may not be desired to add this. Two reasons come to mind:

- this would add a branch inside `np.eye` for **every** call, which could be a performance hit for code that relies heavily on `np.eye`
- this can already be achieved just by using `np.eye` and then taking the transpose


Discussion:
Can you add some reasons why it _is_ desired to have this as well?
For code that passes data into Fortran routines (via `f2py`, `ctypes`, `cffi`, Cython, etc.), it's quite helpful to be able to just pass a pointer to the data as-is. This is just a generic argument for having an `ndarray` in Fortran order, but I think it should apply to `np.eye()` just as well as it applies to `np.zeros()` or `np.empty()` (both of which have an `order` kwarg).

An argument "against" the "take the transpose" response would be data ownership:

```python
>>> import numpy as np
>>> id_mat = np.eye(3)
>>> id_mat.flags
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
...
>>> id_mat.T.flags
  C_CONTIGUOUS : False
  F_CONTIGUOUS : True
  OWNDATA : False
...
>>> 
>>> 
>>> id_mat = np.zeros((3, 3), order='F')
>>> id_mat.flat[::3 + 1] = 1.0
>>> id_mat
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
>>> id_mat.flags
  C_CONTIGUOUS : False
  F_CONTIGUOUS : True
  OWNDATA : True
...
```
It seems unobjectionable to me.
Thanks for the explanation - despite spending the last couple days dealing with fortran, I'd forgotten why that would be useful!

Seems pretty reasonable. An `out` argument might be nice too.

I wonder if no-input-argument ufuncs could become a thing...

Resolving patch:
diff --git a/numpy/lib/twodim_base.py b/numpy/lib/twodim_base.py
--- a/numpy/lib/twodim_base.py
+++ b/numpy/lib/twodim_base.py
@@ -137,7 +137,7 @@ def flipud(m):
     return m[::-1, ...]
 
 
-def eye(N, M=None, k=0, dtype=float):
+def eye(N, M=None, k=0, dtype=float, order='C'):
     """
     Return a 2-D array with ones on the diagonal and zeros elsewhere.
 
@@ -153,6 +153,11 @@ def eye(N, M=None, k=0, dtype=float):
       to a lower diagonal.
     dtype : data-type, optional
       Data-type of the returned array.
+    order : {'C', 'F'}, optional
+        Whether the output should be stored in row-major (C-style) or
+        column-major (Fortran-style) order in memory.
+
+        .. versionadded:: 1.14.0
 
     Returns
     -------
@@ -178,7 +183,7 @@ def eye(N, M=None, k=0, dtype=float):
     """
     if M is None:
         M = N
-    m = zeros((N, M), dtype=dtype)
+    m = zeros((N, M), dtype=dtype, order=order)
     if k >= M:
         return m
     if k >= 0:
diff --git a/numpy/matlib.py b/numpy/matlib.py
--- a/numpy/matlib.py
+++ b/numpy/matlib.py
@@ -173,7 +173,7 @@ def identity(n,dtype=None):
     b.flat = a
     return b
 
-def eye(n,M=None, k=0, dtype=float):
+def eye(n,M=None, k=0, dtype=float, order='C'):
     """
     Return a matrix with ones on the diagonal and zeros elsewhere.
 
@@ -189,6 +189,11 @@ def eye(n,M=None, k=0, dtype=float):
         and a negative value to a lower diagonal.
     dtype : dtype, optional
         Data-type of the returned matrix.
+    order : {'C', 'F'}, optional
+        Whether the output should be stored in row-major (C-style) or
+        column-major (Fortran-style) order in memory.
+
+        .. versionadded:: 1.14.0
 
     Returns
     -------
@@ -210,7 +215,7 @@ def eye(n,M=None, k=0, dtype=float):
             [ 0.,  0.,  0.]])
 
     """
-    return asmatrix(np.eye(n, M, k, dtype))
+    return asmatrix(np.eye(n, M=M, k=k, dtype=dtype, order=order))
 
 def rand(*args):
     """


Test patch:
diff --git a/numpy/lib/tests/test_twodim_base.py b/numpy/lib/tests/test_twodim_base.py
--- a/numpy/lib/tests/test_twodim_base.py
+++ b/numpy/lib/tests/test_twodim_base.py
@@ -95,6 +95,15 @@ def test_strings(self):
     def test_bool(self):
         assert_equal(eye(2, 2, dtype=bool), [[True, False], [False, True]])
 
+    def test_order(self):
+        mat_c = eye(4, 3, k=-1)
+        mat_f = eye(4, 3, k=-1, order='F')
+        assert_equal(mat_c, mat_f)
+        assert mat_c.flags.c_contiguous
+        assert not mat_c.flags.f_contiguous
+        assert not mat_f.flags.c_contiguous
+        assert mat_f.flags.f_contiguous
+
 
 class TestDiag(object):
     def test_vector(self):
diff --git a/numpy/tests/test_matlib.py b/numpy/tests/test_matlib.py
--- a/numpy/tests/test_matlib.py
+++ b/numpy/tests/test_matlib.py
@@ -28,10 +28,19 @@ def test_identity():
     assert_array_equal(x, np.matrix([[1, 0], [0, 1]]))
 
 def test_eye():
-    x = numpy.matlib.eye(3, k=1, dtype=int)
-    assert_array_equal(x, np.matrix([[ 0,  1,  0],
-                                     [ 0,  0,  1],
-                                     [ 0,  0,  0]]))
+    xc = numpy.matlib.eye(3, k=1, dtype=int)
+    assert_array_equal(xc, np.matrix([[ 0,  1,  0],
+                                      [ 0,  0,  1],
+                                      [ 0,  0,  0]]))
+    assert xc.flags.c_contiguous
+    assert not xc.flags.f_contiguous
+
+    xf = numpy.matlib.eye(3, 4, dtype=int, order='F')
+    assert_array_equal(xf, np.matrix([[ 1,  0,  0,  0],
+                                      [ 0,  1,  0,  0],
+                                      [ 0,  0,  1,  0]]))
+    assert not xf.flags.c_contiguous
+    assert xf.flags.f_contiguous
 
 def test_rand():
     x = numpy.matlib.rand(3)