对列表进行排序和过滤

时间:2017-02-09 20:22:19

标签: python list sorting

我有这样的清单:

[['Richard', 1, 'Group A'], ['Mark', 3, 'Group A'],
 ['Alan', 4, 'Group B'], ['Dave', 3, 'Group B'],
 ['Gordon', 2, 'Group A']]

我想过滤,以便只保留每个组中最小的数字(Richard的数字为1,Mark为3,Alan为4等),以便列表看起来像:

[['Richard', 1, 'Group A'], ['Dave', 3, 'Group B']]

我用lambda键排序:

filteredList = sorted(list,key=lambda x: x[2])

但是当我在每个小组内进行排序并摆脱排名较高的个人时,我被阻止了。

有没有一种简单的方法可以在Python中实现这一点,还是应该迭代并测试每一行?

5 个答案:

答案 0 :(得分:4)

重新键入组名称的数据。不要将数据命名为list,因为它会影响内置名称。

>>> results = {}
>>> for name, number, group in data:
...     key = group
...     value = number, name
...     results[key] = min(value, results.get(key, value))
...     
>>> [[name, number, group] for group, (number, name) in results.items()]
[['Dave', 3, 'Group B'], ['Richard', 1, 'Group A']]

纯python数据结构很好地处理了这个问题,sort / itertools方法不是最理想的,并且增加了从O(n)到O(n logn)的复杂性。

答案 1 :(得分:3)

您可以使用collections.defaultdict根据第3项对子列表进行分组,然后在列表解析中使用min()函数和正确的键,以获得预期结果:

In [61]: from operator import itemgetter
In [62]: from collections import defaultdict
In [63]: lst = [['Richard', 1, 'Group A'], ['Mark', 3, 'Group A'], ['Alan', 4, 'Group B'], ['Dave', 3, 'Group B'], ['Gordon', 2, 'Group A']]

In [64]: d = defaultdict(list)

In [65]: for i, j, k in lst:
             d[k].append([i, j, k])
   ....:     

In [66]: [min(sub, key=itemgetter(1)) for sub in d.values()]
Out[66]: [['Dave', 3, 'Group B'], ['Richard', 1, 'Group A']]

您甚至可以通过将自定义对象传递给defaultdict()来以更优化的方式执行此操作,以便只有在新项目具有较小的第二项时才附加新项目:

from collections import defaultdict


class MyList(list):

    def __init__(self, *args, **kwargs):
        super(MyList, self).__init__(*args, **kwargs)

    def special_append(self, arg):
        if not self:
            self.append(arg)
        elif arg[1] < self[0][1]:
            self[0] = arg

演示:

lst = [['Richard', 1, 'Group A'], ['Mark', 3, 'Group A'], ['Alan', 4, 'Group B'], ['Dave', 3, 'Group B'], ['Gordon', 2, 'Group A']]

d = defaultdict(MyList)

for i, j, k in lst:
    d[k].special_append([i, j, k])

print(d)

defaultdict(<class '__main__.MyList'>, {'Group B': [['Dave', 3, 'Group B']], 'Group A': [['Richard', 1, 'Group A']]})

答案 2 :(得分:3)

这是一个简单的“bin and find min”问题。第一遍,我们将bin:

from operator import itemgetter
get_second = itemgetter(1)
results = [min(group, key=get_second) for group in bins.values()]

现在我们只需要取每个箱子的最小值:

N

到目前为止,我们有一个O(N)算法(对于我们放入dict中的每个min项,在O(1)时间内进行分箱)并找到results.sort(key=itemgetter(2)) 个运行确切地再次在每个项目上 - 所以也是O(N)...

如有必要,您可以按组名对结果进行排序:

min

我们可以同时执行from operator import itemgetter get_second = itemgetter(1) results = {} for item in input_stream: group = item[2] if group not in results: results[group] = item else: results[group] = min(item, results[group], key=get_second) 步骤和分级步骤以节省一点内存(例如,如果输入来自生成器并且批次项目):

 ordered_results = sorted(results.values(), key=itmegetter(2))

这实际上是与@wim提供的解决方案相同的想法的不同实现。完成后(如有必要)订购结果:

model test3
  parameter Real a=2 "amplitude";
  parameter Real b=3 "period";

  Real u, y;
initial equation 
  u = 1;
  y = 0;
equation 
  4*a/b*u = der(y);
  when y > a then
    u = -1;
  elsewhen y < -a then
    u = 1;
  end when;
end test3;

通过这种方式,我们只为每个组保留一个结果。成本是一些额外的代码复杂性。

答案 3 :(得分:0)

你可以使它像key=lambda x: (x[2], x[1])。然后你有两级排序。

或者,operator.itemgetter可以采用多个索引。

答案 4 :(得分:0)

我同意TemporalWolf的评论itertools.groupby是正确的方法。

from itertools import groupby
from operator import itemgetter

in_ = [['Richard', 1, 'Group A'], ['Mark', 3, 'Group A'],
       ['Alan', 4, 'Group B'], ['Dave', 3, 'Group B'],
       ['Gordon', 2, 'Group A']]

groups = groupby(in_, key=itemgetter(2))
# operator.itemgetter(N) is equivalent to lambda x: x[N]

groupby函数创建类似于:

的东西
[("Group A", [['Richard', 1, 'Group A'],
              ['Mark', 3, 'Group A'],
              ['Gordon', 2, 'Group A']]),
 ("Group B", [['Alan', 4, 'Group B'],
              ['Dave', 3, 'Group B']])]

然后迭代并使用min来查找结果很容易

minimums = []
for _, vals in groups:
    minimums.append(min(vals, key=itemgetter(1)))