在Python中对多个列上的numpy数组进行排序

use*_*122 6 python sorting numpy

我试图在column1上排序以下数组,然后是column2然后是column3

[['2008' '1' '23' 'AAPL' 'Buy' '100']
 ['2008' '1' '30' 'AAPL' 'Sell' '100']
 ['2008' '1' '23' 'GOOG' 'Buy' '100']
 ['2008' '1' '30' 'GOOG' 'Sell' '100']
 ['2008' '9' '8' 'GOOG' 'Buy' '100']
 ['2008' '9' '15' 'GOOG' 'Sell' '100']
 ['2008' '5' '1' 'XOM' 'Buy' '100']
 ['2008' '5' '8' 'XOM' 'Sell' '100']]
Run Code Online (Sandbox Code Playgroud)

我使用了以下代码:

    idx=np.lexsort((order_array[:,2],order_array[:,1],order_array[:,0]))
    order_array=order_array[idx]
Run Code Online (Sandbox Code Playgroud)

结果数组是

[['2008' '1' '23' 'AAPL' 'Buy' '100']
 ['2008' '1' '23' 'GOOG' 'Buy' '100']
 ['2008' '1' '30' 'AAPL' 'Sell' '100']
 ['2008' '1' '30' 'GOOG' 'Sell' '100']
 ['2008' '5' '1' 'XOM' 'Buy' '100']
 ['2008' '5' '8' 'XOM' 'Sell' '100']
 ['2008' '9' '15' 'GOOG' 'Sell' '100']
 ['2008' '9' '8' 'GOOG' 'Buy' '100']]
Run Code Online (Sandbox Code Playgroud)

问题是最后两行是错误的.正确的数组应该将最后一行作为倒数第二行.我已经尝试了一切,但我无法理解为什么会这样.会感激一些帮助.

我使用以下代码获取order_array.

 for i in ….
    x= ldt_timestamps[i] # this is a list of timestamps
    s_sym=……
    list=[int(x.year),int(x.month),int(x.day),s_sym,'Buy',100]   
    rows_list.append(list) 

 order_array=np.array(rows_list)
Run Code Online (Sandbox Code Playgroud)

unu*_*tbu 9

tldr:NumPy在对数值数组进行数值计算时会发光.虽然有可能(见下文)NumPy不适合这个.你可能最好使用Pandas.


问题的原因:

值正在按字符串排序.你需要将它们排序为ints.

In [7]: sorted(['15', '8'])
Out[7]: ['15', '8']

In [8]: sorted([15, 8])
Out[8]: [8, 15]
Run Code Online (Sandbox Code Playgroud)

这发生是因为order_array包含字符串.您需要将这些字符串转换为ints适当的位置.

将dtypes从string-dtype转换为数字dtype需要为新数组分配空间.因此,您可能最好order_array从一开始就修改您创建的方式.

有趣的是,即使您在调用时将值转换为整数

order_array = np.array(rows_list)
Run Code Online (Sandbox Code Playgroud)

NumPy默认创建一个同质数组.在同构数组中,每个值都具有相同的dtype.所以NumPy试图找到你所有值中的共同点并选择一个字符串dtype,阻止你将字符串转换为int的努力!

您可以通过检查来检查自己的dtype order_array.dtype:

In [42]: order_array = np.array(rows_list)

In [43]: order_array.dtype
Out[43]: dtype('|S4')
Run Code Online (Sandbox Code Playgroud)

现在,我们如何解决这个问题?


使用对象dtype:

最简单的方法是使用'object'dtype

In [53]: order_array = np.array(rows_list, dtype='object')

In [54]: order_array
Out[54]: 
array([[2008, 1, 23, AAPL, Buy, 100],
       [2008, 1, 30, AAPL, Sell, 100],
       [2008, 1, 23, GOOG, Buy, 100],
       [2008, 1, 30, GOOG, Sell, 100],
       [2008, 9, 8, GOOG, Buy, 100],
       [2008, 9, 15, GOOG, Sell, 100],
       [2008, 5, 1, XOM, Buy, 100],
       [2008, 5, 8, XOM, Sell, 100]], dtype=object)
Run Code Online (Sandbox Code Playgroud)

这里的问题是,np.lexsort或者np.sort不适用于dtype数组object.要解决该问题,您可以rows_list 在创建之前对其进行排序order_list:

In [59]: import operator

In [60]: rows_list.sort(key=operator.itemgetter(0,1,2))
Out[60]: 
[(2008, 1, 23, 'AAPL', 'Buy', 100),
 (2008, 1, 23, 'GOOG', 'Buy', 100),
 (2008, 1, 30, 'AAPL', 'Sell', 100),
 (2008, 1, 30, 'GOOG', 'Sell', 100),
 (2008, 5, 1, 'XOM', 'Buy', 100),
 (2008, 5, 8, 'XOM', 'Sell', 100),
 (2008, 9, 8, 'GOOG', 'Buy', 100),
 (2008, 9, 15, 'GOOG', 'Sell', 100)]

order_array = np.array(rows_list, dtype='object')
Run Code Online (Sandbox Code Playgroud)

更好的选择是将前三列合并到datetime.date对象中:

import operator
import datetime as DT

for i in ...:
    seq = [DT.date(int(x.year), int(x.month), int(x.day)) ,s_sym, 'Buy', 100]   
    rows_list.append(seq)
rows_list.sort(key=operator.itemgetter(0,1,2))        
order_array = np.array(rows_list, dtype='object')

In [72]: order_array
Out[72]: 
array([[2008-01-23, AAPL, Buy, 100],
       [2008-01-30, AAPL, Sell, 100],
       [2008-01-23, GOOG, Buy, 100],
       [2008-01-30, GOOG, Sell, 100],
       [2008-09-08, GOOG, Buy, 100],
       [2008-09-15, GOOG, Sell, 100],
       [2008-05-01, XOM, Buy, 100],
       [2008-05-08, XOM, Sell, 100]], dtype=object)
Run Code Online (Sandbox Code Playgroud)

即使这很简单,我也不喜欢NypPy数组的dtype对象.使用本机dtypes,NumPy数组既没有速度也没有内存节省空间.此时,您可能会发现使用Python列表更快,语法更容易处理.


使用结构化数组:

更多NumPy-ish解决方案仍然提供速度和内存优势,是使用结构化数组(而不是同类数组).要制作一个结构化数组,np.array你需要明确地提供一个dtype:

dt = [('year', '<i4'), ('month', '<i4'), ('day', '<i4'), ('symbol', '|S8'),
      ('action', '|S4'), ('value', '<i4')]
order_array = np.array(rows_list, dtype=dt)

In [47]: order_array.dtype
Out[47]: dtype([('year', '<i4'), ('month', '<i4'), ('day', '<i4'), ('symbol', '|S8'), ('action', '|S4'), ('value', '<i4')])
Run Code Online (Sandbox Code Playgroud)

要对结构化数组进行排序,您可以使用以下sort方法:

order_array.sort(order=['year', 'month', 'day'])
Run Code Online (Sandbox Code Playgroud)

要使用结构化数组,您需要了解同构数组和结构化数组之间的一些差异:

你原来的同质阵列是二维的.相比之下,所有结构化数组都是一维的:

In [51]: order_array.shape
Out[51]: (8,)
Run Code Online (Sandbox Code Playgroud)

如果使用int索引结构化数组或遍历数组,则返回行:

In [52]: order_array[3]
Out[52]: (2008, 1, 30, 'GOOG', 'Sell', 100)
Run Code Online (Sandbox Code Playgroud)

使用齐次数组,您可以使用order_array[:, i] Now 访问列,使用结构化数组,您可以通过名称访问它们:例如order_array['year'].


或者,使用熊猫:

如果您可以安装Pandas,我想您可能最开心使用Pandas DataFrame:

In [73]: df = pd.DataFrame(rows_list, columns=['date', 'symbol', 'action', 'value'])
In [75]: df.sort(['date'])
Out[75]: 
         date symbol action  value
0  2008-01-23   AAPL    Buy    100
2  2008-01-23   GOOG    Buy    100
1  2008-01-30   AAPL   Sell    100
3  2008-01-30   GOOG   Sell    100
6  2008-05-01    XOM    Buy    100
7  2008-05-08    XOM   Sell    100
4  2008-09-08   GOOG    Buy    100
5  2008-09-15   GOOG   Sell    100
Run Code Online (Sandbox Code Playgroud)

Pandas具有有用的功能,可以按日期对齐时间序列,填充缺失值,分组和聚合/转换行或列.


通常,对于年,月,日而言,使用单个日期列而不是三个整数值列更有用.

如果您需要将年,月,日作为单独的列进行输出,比如说csv,那么您可以将日期列替换为年,月,日列,如下所示:

In [33]: df = df.join(df['date'].apply(lambda x: pd.Series([x.year, x.month, x.day], index=['year', 'month', 'day'])))

In [34]: del df['date']

In [35]: df
Out[35]: 
  symbol action  value  year  month  day
0   AAPL    Buy    100  2008      1   23
1   GOOG    Buy    100  2008      1   23
2   AAPL   Sell    100  2008      1   30
3   GOOG   Sell    100  2008      1   30
4    XOM    Buy    100  2008      5    1
5    XOM   Sell    100  2008      5    8
6   GOOG    Buy    100  2008      9    8
7   GOOG   Sell    100  2008      9   15
Run Code Online (Sandbox Code Playgroud)

或者,如果您没有使用"日期"列开头,您当然可以rows_list单独留下并从头开始构建包含年,月,日列的DataFrame.排序仍然很容易:

df.sort(['year', 'month', 'day'])
Run Code Online (Sandbox Code Playgroud)