【问题标题】:Python - How to perform indirect sorting with a user-defined function?Python - 如何使用用户定义的函数执行间接排序?
【发布时间】:2016-03-08 01:06:40
【问题描述】:

numpy.argsort 返回一个排序列表来执行间接排序,但它似乎不接受用户定义的函数来比较两个元素。

我想知道如何根据与用户定义的函数的比较来获得排序列表。

就我而言,我有一个结果表:

a = [[1,2,3,5,6,7,8],[4,6,2,5,6,3,4],...]

我要比较的功能是:

def argMedian(A):
    s   = np.array([sum(A[:i+1]) for i in range(Nmentions)])
    mid = float(s[Nmentions-1])/2
    return np.argwhere(mid < s)[0][0]

def tieBreaking(A, B):
Ac = A
Bc = B
medA = argMedian(Ac)
medB = argMedian(Bc)
while medA == medB:
    Ac[medA] -= 1
    Bc[medB] -= 1
    medA = argMedian(Ac)
    medB = argMedian(Bc)
return 1 if medA > medB else -1

这是majority judgment 的实现。一个列表包含每个等级的投票数。如果两个列表之间的中位数相等,则删除两个列表的中位数投票,并再次测试中位数的比较。在这里,我认为索引 0 是最好的等级,索引 7 是最差的。

我需要进行间接排序,因为我想知道排名。

【问题讨论】:

  • 该比较函数没有意义,因为您使用中位数作为列表中的索引,但值的中位数可能不是有效索引。此外,即使它有效,它也会改变你的列表,这可能不是你想要的。请描述您希望如何对列表进行排序。
  • 嗯,我认为只要所有值都不相等,中位数的上限就提供了一个有效的索引,不是吗?为了避免改变列表,我应该先复制它们吗?这个想法是从多数判断实现平局算法:en.wikipedia.org/wiki/Majority_judgment#Example_application
  • 如果您的列表是[100, 200, 300],那么中位数的上限是 200,但 200 不是列表中的有效索引。
  • 此外,该页面上描述的算法不涉及修改单个值(正如您似乎试图通过从某些值中减去 1 来做的那样)。相反,它从投票列表中删除值。
  • 对。我已经更新了实现并带来了更多关于上下文的信息

标签: python sorting numpy


【解决方案1】:

如果您使用 Python >= 3.4,则可以使用statistics.median_low()

from random import randrange
from statistics import median_low

a = [[randrange(8) for _ in range(7)] for _ in range(10)]

print("unsorted")
for item in a:
    print(item)

a.sort(key=median_low)

print("\nsorted")
for item in a:
    print(item)

输出:

unsorted
[4, 2, 2, 1, 4, 7, 4]
[2, 2, 2, 7, 5, 5, 6]
[3, 0, 0, 5, 5, 3, 1]
[4, 7, 6, 2, 6, 7, 3]
[4, 7, 7, 1, 2, 7, 7]
[6, 2, 6, 5, 6, 7, 2]
[7, 1, 6, 0, 0, 7, 1]
[0, 5, 1, 2, 1, 7, 7]
[2, 7, 6, 7, 5, 4, 7]
[6, 5, 2, 3, 5, 0, 3]

sorted
[7, 1, 6, 0, 0, 7, 1]
[0, 5, 1, 2, 1, 7, 7]
[3, 0, 0, 5, 5, 3, 1]
[6, 5, 2, 3, 5, 0, 3]
[4, 2, 2, 1, 4, 7, 4]
[2, 2, 2, 7, 5, 5, 6]
[4, 7, 6, 2, 6, 7, 3]
[6, 2, 6, 5, 6, 7, 2]
[2, 7, 6, 7, 5, 4, 7]
[4, 7, 7, 1, 2, 7, 7]

编辑完整的替代解决方案:

考虑对列表进行如下排序: 1.找到list的median_low,移到list的最前面 2. 找到list[1:]的median_low,移动到第二个位置 3.求list[2:]的median_low ...

您可以按中值对原始列表进行排序,或创建元素按中值排序的键。

def def keyfunc(x):
    t = x[:]
    return [t.pop(t.index(median_low(t))) for _ in range(len(t))]

a = [
    [4, 2, 2, 1, 4, 7, 4],
    [2, 2, 2, 7, 5, 5, 6],
    [3, 0, 0, 5, 5, 3, 1],
    [7, 1, 6, 0, 0, 6, 1],    # tie for first four rounds, but then wins
    [4, 7, 6, 2, 6, 7, 3],
    [6, 2, 6, 5, 6, 7, 2],
    [7, 1, 6, 0, 0, 7, 1],    # tie for first four rounds
    [0, 5, 1, 2, 1, 7, 7],
    [2, 7, 6, 7, 5, 4, 7],
    [6, 5, 2, 3, 5, 0, 3]
]

a.sort(key=keyfunc)

print("\nsorted")
for item in a:
    print(item)

输出:

sorted
[7, 1, 6, 0, 0, 6, 1]
[7, 1, 6, 0, 0, 7, 1]
[0, 5, 1, 2, 1, 7, 7]
[3, 0, 0, 5, 5, 3, 1]
[6, 5, 2, 3, 5, 0, 3]
[4, 2, 2, 1, 4, 7, 4]
[2, 2, 2, 7, 5, 5, 6]
[4, 7, 6, 2, 6, 7, 3]
[6, 2, 6, 5, 6, 7, 2]
[2, 7, 6, 7, 5, 4, 7]

【讨论】:

  • 这是个好主意,但在相等的情况下,平局的算法会丢掉一个分数。不幸的是,没有内置函数可以做到这一点。
  • 正如维基百科页面所说:如果出现平局,则取平局,删除中间值,然后重新排序。重复,直到你有一个赢家。使用list.remove() 从列表中删除特定值。
【解决方案2】:

您不需要使用 numpy 对列表中的元素进行排序。您可以对列表或 sorted() 使用 sort() 函数。如果需要,在那里定义您的自定义比较器。

【讨论】:

  • 其实我确实需要进行间接排序:我要的是知道要排序的索引,而不是排序的结果
【解决方案3】:

对于使用基本 python 计算间接排序,存在一些技巧。它们在Equivalent of Numpy.argsort() in basic python? 中进行了比较。

采取最快的解决方案,可以添加比较功能。

在 Python 2.7 中:

sorted(range(len(results)), cmp=tieBreaking, key=results.__getitem__)

在 Python 3 中,应该注意 cmp 已经消失,但函数 cmp_to_key 可以替换它。

【讨论】:

    猜你喜欢
    • 2015-07-12
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    • 2014-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多