【问题标题】:Sorting items into an array by property按属性将项目排序到数组中
【发布时间】:2020-11-01 11:54:15
【问题描述】:

考虑一个包含许多项目的列表,每个项目都包含有关相同N 属性的信息。这些属性中的每一个都可以采用一定数量的离散值(但可能是未知的)。该列表不一定是有序的。

我想将这些项目分类到一个N 维数组中,这样每个属性仅沿其自己的轴变化,即在沿该轴的任何切片内保持不变。

简化示例:

# Three properties
prop_1 = ['01','02','03','07']
prop_2 = ['foo','bar','baz']
prop_3 = ['yellow','red']

from itertools import product

# Consider this as the input
string_list = ['_'.join(s) for s in product(prop_1, prop_2, prop_3)]

# HOWEVER...

from random import shuffle

# Inputs may be unsorted
shuffle(string_list)

我现在想将string_list 组织成一个形状为(4,3,2) 的数组,这样第一个属性沿第一个轴变化,依此类推。也就是说,预期的输出是:

array([[['01_foo_yellow','01_foo_red'],
        ['01_bar_yellow','01_bar_red'],
        ['01_baz_yellow','01_baz_red']],

       [['02_foo_yellow','02_foo_red'],
        ['02_bar_yellow','02_bar_red'],
        ['02_baz_yellow','02_baz_red']],

       [['03_foo_yellow','03_foo_red'],
        ['03_bar_yellow','03_bar_red'],
        ['03_baz_yellow','03_baz_red']],

       [['07_foo_yellow','07_foo_red'],
        ['07_bar_yellow','07_bar_red'],
        ['07_baz_yellow','07_baz_red']]])

每个属性在任何切片中沿其自己的轴保持不变,即:

A[3,...] # All strings containing '07' as property 1
A[:,1,:] # All strings containing 'bar' as property 2
A[...,0] # All strings containing 'yellow' as property 3

即使缺少项目,该方法也应该是稳健的。例如,如果我们从输入中删除 '02_bar_yellow''03_baz_red',则输出数组的形状应该保持不变,而 None 这些条目本来会被排序:

array([[['01_foo_yellow','01_foo_red'],
        ['01_bar_yellow','01_bar_red'],
        ['01_baz_yellow','01_baz_red']],

       [['02_foo_yellow','02_foo_red'],
        [ None,          '02_bar_red'],
        ['02_baz_yellow','02_baz_red']],

       [['03_foo_yellow','03_foo_red'],
        ['03_bar_yellow','03_bar_red'],
        ['03_baz_yellow', None.      ]],

       [['07_foo_yellow','07_foo_red'],
        ['07_bar_yellow','07_bar_red'],
        ['07_baz_yellow','07_baz_red']]])

问题:

虽然以上说明了总体思路,但我实际上是想让它适用于一组“属性”是其捕获组的正则表达式 match 对象。

import re

pattern = '(\d+)_(\w+)_(\w+)'
regex = re.compile(pattern)

# Consider this as the input
matches = [re.match(s) for s in string_list]

然后我想根据每个match 对象的group() 方法给出的值进行排序。

虽然它没有完全接近解决方案,但我可以使用itertools.groupby() 沿轴按切片排序项目:

# Sort by the first capturing group
groupings = itertools.groupby(matches, key=lambda m: m.groups()[0])

grouped_strings = [[m.string for m in g] for n,g in groupings]

所以grouped_strings[3] 的内容与第一个例子中给出的切片A[3,...] 相同。但是,这些条目以扁平数组的形式给出。

我突然想到我应该能够迭代地使用itertools.groupby 来实现正确的排序,但我不太明白。同时,我想知道是否有更简单或更“pythonic”的方式来实现这一点。

【问题讨论】:

  • 你考虑过使用 numpy.如果它是一个 numpy 数组,它是对 reshape() 的简单调用。
  • 这将使事情变得非常简单,前提是输入列表可以很容易地预先正确排序(对于简单的字符串,对于其他对象则不一定如此)。另一个问题是轴的长度可能事先不知道。另外,虽然我在原始帖子中没有提到它,但我想要一种在输入列表可能缺少项目的情况下稳健的方法。
  • 如果我正确理解了您的问题,请告诉我。

标签: python arrays regex sorting grouping


【解决方案1】:

如果您最初不知道轴的长度,则需要获取它们,例如,使用setnp.unique,它们也会对结果进行排序并为您计算一个逆索引。

然后,您可以创建一个布尔掩码,指示找到哪些项目,哪些没有。将掩码索引映射到已排序的标签数组很简单。

假设您有三个感兴趣的匹配项,如示例所示:

# Findall is the lazy shortcut that assumes only one match
matches = np.array([regex.findall(s) for s in string_list])
p1, i1 = np.unique(matches[:, 0], return_inverse=True)
p2, i2 = np.unique(matches[:, 1], return_inverse=True)
p3, i3 = np.unique(matches[:, 2], return_inverse=True)
mask = np.zeros((p1.size, p2.size, p3.size), dtype=bool)
mask[i1, i2, i3] = True

p* 是数组轴的标签。由于np.unique 是基于排序的,因此它们将是唯一的并已排序。

i* 是将string_list 的元素映射到每个轴的索引。这意味着花哨的索引i1, i2, i3 表示布尔矩阵中与找到的匹配对应的位置。

这可以通过在任意数量的捕获组上循环来轻松概括。

# note the transpose
matches = np.array([regex.findall(s) for s in string_list]).T
# must be list because ragged
axes = [np.unique(x, return_inverse=True) for x in matches]
mask = np.zeros(tuple(x[0].size for x in axes), dtype=bool)
mask[tuple(x[1] for x in axes)] = True

【讨论】:

  • 这很聪明。这不是我想要的,但它显示了如何到达那里。我只需要做一些细微的调整:mask = np.full(tuple(x[0].size for x in axes), None, dtype=np.object); mask[tuple(x[1] for x in axes)] = matches.
  • 很好的解决方案!来自我的 +1 :)!
【解决方案2】:

您可以尝试使用带有初始字符串的np.reshape

import numpy as np
output=np.array(string_list).reshape(4,3,2) 

print(output)

输出:

[[['01_foo_yellow' '01_foo_red']
  ['01_bar_yellow' '01_bar_red']
  ['01_baz_yellow' '01_baz_red']]

 [['02_foo_yellow' '02_foo_red']
  ['02_bar_yellow' '02_bar_red']
  ['02_baz_yellow' '02_baz_red']]

 [['03_foo_yellow' '03_foo_red']
  ['03_bar_yellow' '03_bar_red']
  ['03_baz_yellow' '03_baz_red']]

 [['07_foo_yellow' '07_foo_red']
  ['07_bar_yellow' '07_bar_red']
  ['07_baz_yellow' '07_baz_red']]]

【讨论】:

  • 我意识到有必要澄清我在问题正文中添加的一些内容:1)输入可能未排序(这可能是微不足道的); 2) 可能事先不知道轴的长度;和 3) 输入中可能缺少项目,在这种情况下,输出数组应该有 None,否则这些项目将被排序。
  • 对不起@corvus,我只看到了你的评论和编辑:(,但 MadPhysicist 的答案就是这样!
  • 不用担心,我意识到这个问题的措辞没有达到预期的水平,但我很难解释自己。感谢您的意见!
猜你喜欢
  • 2015-04-05
  • 1970-01-01
  • 2015-11-20
  • 1970-01-01
  • 1970-01-01
  • 2015-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多