【问题标题】:Sorting numpy array on multiple columns in Python在Python中对多列上的numpy数组进行排序
【发布时间】:2013-10-09 23:46:55
【问题描述】:

我正在尝试对 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']]

我使用了以下代码:

    idx=np.lexsort((order_array[:,2],order_array[:,1],order_array[:,0]))
    order_array=order_array[idx]

结果数组是

[['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']]

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

我正在使用以下代码来获取 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)

【问题讨论】:

  • Sorting a 2D numpy array by multiple axes 的可能重复项使用该答案,但使用对您的数据(不是所有字符串)有意义的 dtype,例如dt = dt=[('y',np.uint32),('m',np.uint32),('d',np.uint32),('sym','S4'),('bs','S4'),('huh',np.uint32)]

标签: python sorting numpy


【解决方案1】:

tldr:在对数值数组进行数值计算时,NumPy 大放异彩。尽管有可能(见下文)NumPy 不太适合这种情况。使用 Pandas 可能会更好。


问题原因:

值被排序为字符串。您需要将它们排序为ints

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

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

这是因为order_array 包含字符串。您需要在适当的地方将这些字符串转换为ints

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

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

order_array = np.array(rows_list)

NumPy 默认创建一个 homogenous 数组。在同构数组中,每个值都具有相同的 dtype。所以 NumPy 试图在你所有的 值并选择了一个字符串 dtype,从而阻碍了您将字符串转换为整数的努力!

您可以通过检查order_array.dtype 自行检查数据类型:

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

In [43]: order_array.dtype
Out[43]: dtype('|S4')

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


使用对象 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)

这里的问题是 np.lexsortnp.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')

更好的选择是将前三列合并到 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)

尽管这很简单,但我不喜欢 dtype 对象的 NumPy 数组。 您既没有获得 NumPy 数组的速度也没有节省内存空间的收益 本机数据类型。此时,您可能会发现正在使用 Python 列表列表 更快,语法上更容易处理。


使用结构化数组:

仍然提供速度和内存优势的更 NumPy-ish 解决方案是 使用structured array(与同构数组相反)。做一个 带有np.array 的结构化数组,您需要明确提供数据类型:

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')])

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

order_array.sort(order=['year', 'month', 'day'])

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

您原来的同构数组是二维的。相比之下,所有 结构化数组是一维的:

In [51]: order_array.shape
Out[51]: (8,)

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

In [52]: order_array[3]
Out[52]: (2008, 1, 30, 'GOOG', 'Sell', 100)

使用同构数组,您可以使用order_array[:, i] 访问列 现在,使用结构化数组,您可以按名称访问它们:例如order_array['year'].


或者,使用 Pandas:

如果你可以安装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

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

或者,如果您一开始就没有使用“日期”列,您当然可以不理会rows_list,并从头开始构建带有年、月、日列的DataFrame。排序仍然很容易:

df.sort(['year', 'month', 'day'])

【讨论】:

  • 谢谢。但我正在将字符串转换为 int。我已编辑问题以包含用于创建 order_array 的代码。期待您的帮助
  • @user2842122 - 那些“整数”正在被转换回字符串。 unutbu - 我认为这里最简单的解决方案可能是引入一个 NumPy recarray,由 NumPy datetime object 和剩余的字符串和整数数据组成。有a complete example here
  • @AronAhmadia:感谢您的评论!是的,我正在考虑添加类似的内容,但我担心这个答案已经太长了,Pandas 可能仍然是更好的选择。
  • 当你手里拿着一把锤子时,一切看起来都像钉子:) 我同意作为 SciPy 堆栈的一部分,Pandas 应该可用,并且为此类工作提供更友好的界面。
  • @unutbu:非常感谢您提供清晰的解释和各种解决方案。尽管“rows_list.sort”解决方案对我来说似乎是最容易实现的,但我采纳了您的建议并使用 pandas - 而不是 numpy 数组 - 来解决。不过,我有一个疑问 - 对于 rows_list.sort 和 pandas 解决方案,您首先将我的三列 (yyyy,mm,dd) 转换为一个日期时间列。这是为什么?是否可以仅对一列而不是三列进行排序? Bcos 它确实会产生一个问题,因为我的最终数组必须有三列(yyyy,mm,dd)而不是一列。
猜你喜欢
  • 1970-01-01
  • 2021-05-18
  • 1970-01-01
  • 2011-02-19
  • 1970-01-01
  • 2017-04-12
相关资源
最近更新 更多