【问题标题】:Enumerate a list sorted by two fields in Python在 Python 中枚举按两个字段排序的列表
【发布时间】:2019-03-08 15:52:13
【问题描述】:

我有一个这样的数组:
字段 4 是 1,2,3 的平均值,字段 5 是 1,2,3 的最小值。

[['name0', 24, 19, 25, 22.67, 19],
 ['name1', 25, 19, 25, 23.0, 19],
 ['name2', 25, 19, 25, 23.0, 19],
 ['name3', 24, 22, 23, 23.0, 22],
 ['name4', 27, 19, 25, 23.67, 19],
 ['name5', 27, 19, 25, 23.67, 19],
 ['name6', 28, 19, 26, 24.33, 19],
 ['name7', 28, 19, 26, 24.33, 19],
 ['name8', 28, 19, 26, 24.33, 19],
 ['name9', 26, 22, 27, 25.0, 22],
 ['name10', 27, 23, 25, 25.0, 23],
 ['name11', 30, 19, 27, 25.33, 19],
 ['name12', 24, 31, 28, 27.67, 24],
 ['name13', 28, 27, 28, 27.67, 27],
 ['name14', 27, 29, 27, 27.67, 27],
 ['name15', 29, 26, 29, 28.0, 26],
 ['name16', 29, 26, 30, 28.33, 26],
 ['name17', 30, 31, 26, 29.0, 26],
 ['name18', 33, 27, 30, 30.0, 27],
 ['name19', 29, 31, 30, 30.0, 29],
 ['name20', 30, 36, 31, 32.33, 30],
 ['name21', 36, 30, 32, 32.67, 30],
 ['name22', 38, 33, 36, 35.67, 33],
 ['name23', 30, 27, 99, 52.0, 27],
 ['name24', 99, 27, 32, 52.67, 27],
 ['name25', 37, 99, 36, 57.33, 36]]

先按字段 4 排序,然后按字段 5。
我想列举这个列表,创建一种“排名”或“讲台”。

enumerate() 不起作用,因为如您所见,某些字段与字段 4 和 5 绑定,因此它们的“排名”应该相同。
例如,第一个值应如下所示:

[['1', 'name0', 24, 19, 25, 22.67, 19],
 ['2', 'name1', 25, 19, 25, 23.0, 19],
 ['2', 'name2', 25, 19, 25, 23.0, 19],
 ['3', 'name3', 24, 22, 23, 23.0, 22],
 ['4', 'name4', 27, 19, 25, 23.67, 19],
 ...]

想不出一个干净的方法来解决这个问题。 感谢您的帮助。

【问题讨论】:

  • 尝试查看sorteditemgetter。不会使排名神奇地出现,您必须自己弄清楚。但是 enumerate 只枚举一个列表..仅此而已 ;)
  • 在我们给你clean代码之前,请分享你尝试过的non-clean代码。
  • 排名应该基于哪一栏?
  • 不干净的代码还没写完,我中途放弃了,因为我意识到它会变得不可读。此外,排名应基于第 4 列和第 5 列。
  • 做一个很好的例子,你不需要把所有 100 个子列表和所有 6 个元素放在一起。

标签: python sorting enumerate


【解决方案1】:

假设列表已排序,您可以使用...恰当命名的groupbyitemgetter,按其第 4 和第 5 个元素对子列表进行分组。在groupby返回的迭代器上使用enumerate

from itertools import groupby
from operator import itemgetter

# data = [['name0', ...
[ [str(i+1)] + l for i, (k, g) in enumerate(groupby(data, key=itemgetter(4, 5))) for l in g ]

输出:

[
    ['1', 'name0', 24, 19, 25, 22.67, 19],
    ['2', 'name1', 25, 19, 25, 23.0, 19],
    ['2', 'name2', 25, 19, 25, 23.0, 19],
    ['3', 'name3', 24, 22, 23, 23.0, 22],
    ['4', 'name4', 27, 19, 25, 23.67, 19],
    ['4', 'name5', 27, 19, 25, 23.67, 19],
    ['5', 'name6', 28, 19, 26, 24.33, 19],
    ['5', 'name7', 28, 19, 26, 24.33, 19],
    ['5', 'name8', 28, 19, 26, 24.33, 19],
    ['6', 'name9', 26, 22, 27, 25.0, 22],
    ['7', 'name10', 27, 23, 25, 25.0, 23],
    ['8', 'name11', 30, 19, 27, 25.33, 19],
    ['9', 'name12', 24, 31, 28, 27.67, 24],
    ['10', 'name13', 28, 27, 28, 27.67, 27],
    ['10', 'name14', 27, 29, 27, 27.67, 27],
    ['11', 'name15', 29, 26, 29, 28.0, 26],
    ['12', 'name16', 29, 26, 30, 28.33, 26],
    ['13', 'name17', 30, 31, 26, 29.0, 26],
    ['14', 'name18', 33, 27, 30, 30.0, 27],
    ['15', 'name19', 29, 31, 30, 30.0, 29],
    ['16', 'name20', 30, 36, 31, 32.33, 30],
    ['17', 'name21', 36, 30, 32, 32.67, 30],
    ['18', 'name22', 38, 33, 36, 35.67, 33],
    ['19', 'name23', 30, 27, 99, 52.0, 27],
    ['20', 'name24', 99, 27, 32, 52.67, 27],
    ['21', 'name25', 37, 99, 36, 57.33, 36]
]

【讨论】:

    【解决方案2】:

    i = 1 开始并遍历它们并分配排名,如果下一行不同,则仅增加i += 1

    【讨论】:

      【解决方案3】:

      您可以在将其中一个与None值填充后,通过将列表与自身进行压缩来配对相邻项目,这样您就可以遍历压缩对以比较关键字段,如果它们相同,则重用前面的排名:

      for i, ((*_, prev_mean, prev_min), (*_, mean, _min)) in enumerate(zip([(None, None)] + l, l)):
          l[i].insert(0, str(l[i - 1][0] if mean == prev_mean and _min == prev_min else i + 1))
      

      假设您的列表列表存储为变量ll 变为:

      [['1', 'name0', 24, 19, 25, 22.67, 19],
       ['2', 'name1', 25, 19, 25, 23.0, 19],
       ['2', 'name2', 25, 19, 25, 23.0, 19],
       ['4', 'name3', 24, 22, 23, 23.0, 22],
       ['5', 'name4', 27, 19, 25, 23.67, 19],
       ['5', 'name5', 27, 19, 25, 23.67, 19],
       ['7', 'name6', 28, 19, 26, 24.33, 19],
       ['7', 'name7', 28, 19, 26, 24.33, 19],
       ['7', 'name8', 28, 19, 26, 24.33, 19],
       ['10', 'name9', 26, 22, 27, 25.0, 22],
       ['11', 'name10', 27, 23, 25, 25.0, 23],
       ['12', 'name11', 30, 19, 27, 25.33, 19],
       ['13', 'name12', 24, 31, 28, 27.67, 24],
       ['14', 'name13', 28, 27, 28, 27.67, 27],
       ['14', 'name14', 27, 29, 27, 27.67, 27],
       ['16', 'name15', 29, 26, 29, 28.0, 26],
       ['17', 'name16', 29, 26, 30, 28.33, 26],
       ['18', 'name17', 30, 31, 26, 29.0, 26],
       ['19', 'name18', 33, 27, 30, 30.0, 27],
       ['20', 'name19', 29, 31, 30, 30.0, 29],
       ['21', 'name20', 30, 36, 31, 32.33, 30],
       ['22', 'name21', 36, 30, 32, 32.67, 30],
       ['23', 'name22', 38, 33, 36, 35.67, 33],
       ['24', 'name23', 30, 27, 99, 52.0, 27],
       ['25', 'name24', 99, 27, 32, 52.67, 27],
       ['26', 'name25', 37, 99, 36, 57.33, 36]]
      

      【讨论】:

      • 我尝试过这样的事情,但它变得太难以阅读了。感谢您的回答。
      • 我有一个问题。您会在工作中使用此代码吗?对我来说很难阅读。我看到 SO 的人试图将所有内容放在一条线上。
      【解决方案4】:

      使用Pandasdense rank

      import pandas as pd
      
      df = pd.DataFrame(data = [['name0', 24, 19, 25, 22.67, 19],
       ['name1', 25, 19, 25, 23.0, 19],
       ['name2', 25, 19, 25, 23.0, 19],
       ['name3', 24, 22, 23, 23.0, 22],
       ['name4', 27, 19, 25, 23.67, 19],
       ['name5', 27, 19, 25, 23.67, 19],
       ['name6', 28, 19, 26, 24.33, 19],
       ['name7', 28, 19, 26, 24.33, 19],
       ['name8', 28, 19, 26, 24.33, 19],
       ['name9', 26, 22, 27, 25.0, 22],
       ['name10', 27, 23, 25, 25.0, 23],
       ['name11', 30, 19, 27, 25.33, 19],
       ['name12', 24, 31, 28, 27.67, 24],
       ['name13', 28, 27, 28, 27.67, 27],
       ['name14', 27, 29, 27, 27.67, 27],
       ['name15', 29, 26, 29, 28.0, 26],
       ['name16', 29, 26, 30, 28.33, 26],
       ['name17', 30, 31, 26, 29.0, 26],
       ['name18', 33, 27, 30, 30.0, 27],
       ['name19', 29, 31, 30, 30.0, 29],
       ['name20', 30, 36, 31, 32.33, 30],
       ['name21', 36, 30, 32, 32.67, 30],
       ['name22', 38, 33, 36, 35.67, 33],
       ['name23', 30, 27, 99, 52.0, 27],
       ['name24', 99, 27, 32, 52.67, 27],
       ['name25', 37, 99, 36, 57.33, 36]], columns= ['1', '2', '3', '4', '5', '6'])
      
      df["rank"] = df['5'].rank(method = "dense")
      df
      
      >
          1   2   3   4   5   6   rank
      0   name0   24  19  25  22.67   19  1.0
      1   name1   25  19  25  23.00   19  2.0
      2   name2   25  19  25  23.00   19  2.0
      3   name3   24  22  23  23.00   22  2.0
      4   name4   27  19  25  23.67   19  3.0
      5   name5   27  19  25  23.67   19  3.0
      6   name6   28  19  26  24.33   19  4.0
      7   name7   28  19  26  24.33   19  4.0
      8   name8   28  19  26  24.33   19  4.0
      9   name9   26  22  27  25.00   22  5.0
      10  name10  27  23  25  25.00   23  5.0
      11  name11  30  19  27  25.33   19  6.0
      12  name12  24  31  28  27.67   24  7.0
      13  name13  28  27  28  27.67   27  7.0
      14  name14  27  29  27  27.67   27  7.0
      15  name15  29  26  29  28.00   26  8.0
      16  name16  29  26  30  28.33   26  9.0
      17  name17  30  31  26  29.00   26  10.0
      18  name18  33  27  30  30.00   27  11.0
      19  name19  29  31  30  30.00   29  11.0
      20  name20  30  36  31  32.33   30  12.0
      21  name21  36  30  32  32.67   30  13.0
      22  name22  38  33  36  35.67   33  14.0
      23  name23  30  27  99  52.00   27  15.0
      24  name24  99  27  32  52.67   27  16.0
      25  name25  37  99  36  57.33   36  17.0
      

      如果你想要列表列表 -

      df = df.set_index('rank').reset_index()
      df.values.tolist()
      

      【讨论】:

      • 这给了我一个排序列表,但索引从不重复。正如您在我的示例中看到的那样,有两个索引为“2”,等等。
      • 您可以将排名列声明为索引
      • 更新了答案
      猜你喜欢
      • 2013-07-10
      • 1970-01-01
      • 2017-07-21
      • 2011-07-09
      • 1970-01-01
      • 2012-03-10
      相关资源
      最近更新 更多