就地排序按片在列上失败

时间:2016-11-29 06:20:25

标签: python arrays sorting numpy in-place

我正在尝试使用this answer中的解决方案按特定列(就地)对numpy数组进行排序。在大多数情况下它可以工作,但它在任何数组上失败,这是另一个数组的视图:

In [35]: columnnum = 2

In [36]: a = np.array([[1,2,3], [4,7,5], [9,0,1]])

In [37]: a
Out[37]: 
array([[1, 2, 3],
       [4, 7, 5],
       [9, 0, 1]])

In [38]: b = a[:,(0, 2)]

In [39]: b
Out[39]: 
array([[1, 3],
       [4, 5],
       [9, 1]])

In [40]: a.view(','.join([a.dtype.str] * a.shape[1])).sort(order=['f%d' % columnnum], axis=0)

In [41]: a
Out[41]: 
array([[9, 0, 1],
       [1, 2, 3],
       [4, 7, 5]])

In [42]: b.view(','.join([b.dtype.str] * b.shape[1])).sort(order=['f%d' % columnnum], axis=0)
ValueError: new type not compatible with array.

看起来numpy不支持视图的视图,这有一定的意义,但我现在无法弄清楚如何获取任何数组所需的视图,无论是它本身是一个观点与否。到目前为止,我还没有找到任何方法来获取有关构建我需要的新视图的必要信息。

目前,我正在使用l = l[l[:,columnnum].argsort()]就地排序方法,该方法工作正常,但由于我在大型数据集上运行,我想避免额外的内存开销{{ 1}}调用(索引列表)。有没有办法获取有关视图的必要信息或按列进行排序?

1 个答案:

答案 0 :(得分:1)

In [1019]: a=np.array([[1,2,3],[4,7,5],[9,0,1]])
In [1020]: b=a[:,(0,2)]

这是您要排序的a;具有3个字段的结构化数组。它使用相同的数据缓冲区,但将3个整数组解释为字段而不是列。

In [1021]: a.view('i,i,i')
Out[1021]: 
array([[(1, 2, 3)],
       [(4, 7, 5)],
       [(9, 0, 1)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4')])

按照相同的逻辑,您尝试view b

In [1022]: b.view('i,i')
/usr/local/bin/ipython3:1: DeprecationWarning: Changing the shape of non-C contiguous array by
descriptor assignment is deprecated. To maintain
the Fortran contiguity of a multidimensional Fortran
array, use 'a.T.view(...).T' instead
  #!/usr/bin/python3
....
ValueError: new type not compatible with array.

但是如果我使用3个字段而不是2个字段,它可以工作(但有相同的警告):

In [1023]: b.view('i,i,i')
/usr/local/bin/ipython3:1: DeprecationWarning:...
Out[1023]: 
array([[(1, 4, 9), (3, 5, 1)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4')])

问题是bFortran order。查看b.flags

In [1026]: a.strides
Out[1026]: (12, 4)
In [1027]: b.strides
Out[1027]: (4, 12)

b是副本,而不是视图。我不知道为什么b的这种结构改变了秩序。

听到警告,我可以这样做:

In [1047]: b.T.view('i,i,i').T
Out[1047]: 
array([[(1, 4, 9), (3, 5, 1)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4')])

b的默认副本(订单c)可以被视为2个字段:

In [1042]: b1=b.copy()
In [1043]: b1.strides
Out[1043]: (8, 4)
In [1044]: b1.view('i,i')
Out[1044]: 
array([[(1, 3)],
       [(4, 5)],
       [(9, 1)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4')])

脚注:https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html

  

高级索引结果的内存布局针对每个索引操作进行了优化,并且不能假定特定的内存顺序。

====================

在这种情况下,

b是使用高级索引构建的,因此是一个副本,即使真正的视图也可能无法以这种方式查看:

In [1052]: a[:,:2].view('i,i')
....
ValueError: new type not compatible with array.

In [1054]: a[:,:2].copy().view('i,i')
Out[1054]: 
array([[(1, 2)],
       [(4, 7)],
       [(9, 0)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4')])

视图正在选择值的子集:'i,i,x,i,i,x,i,i,x ......',并且不会转换为结构化dtype。

a的结构化视图:'(i,i,i),(i,i,i),...'

您可以选择结构化数组的字段子集:

In [1059]: a1=a.view('i,i,i')
In [1060]: a1
Out[1060]: 
array([[(1, 2, 3)],
       [(4, 7, 5)],
       [(9, 0, 1)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4')])
In [1061]: b1=a1[['f0','f2']]
In [1062]: b1
Out[1062]: 
array([[(1, 3)],
       [(4, 5)],
       [(9, 1)]], 
      dtype=[('f0', '<i4'), ('f2', '<i4')])

但是你可以用这样的观点做些什么。可以在a1中更改值,并在ab1中看到。但是如果我尝试更改b1中的值,则会出错。  这是在发展方面。