按对元素的频率对列表对进行排序

时间:2010-07-19 10:08:17

标签: python sorting

我对Python完全陌生,在尝试各种随机的点点滴滴时,我发现了一个问题,我相信我已经“解决了”,但代码并没有感觉正确 - 我强烈怀疑会有更好的方法来获得理想的结果。

仅供参考 - 我在Windows上使用的是最新版本的Python 3。

问题定义

简单地说,我正在做的是对一对对的列表进行排序,这样包含最少对中出现的元素的对被排序到前面。

这些对的格式为[i,j] 0 <= i <= j < n,其中n是元素的已知最大值。列表中没有重复的对。

元素i的计数是表单[i,j][j,i][i,i]j的对数(不是对元素)的简单计数1}}是导致有效对的任何值。

在排序后的结果中,如果[i,j][k,l]count(i) < count(k)count(i) == count(k) count(j) < count(l)count(j) == count(l)对应出现在[i,j]对之前1}}这两个可以按任意顺序排列 - 我并不担心这种类型是稳定的,虽然会是奖金。)

在排序后的结果中,如果是,则[k,l]对应出现在min(count(i),count(j)) < min(count(k),count(l))对之前 min(count(i),count(j)) == min(count(k),count(l))
max(count(i),count(j)) < max(count(k),count(l))[0,1]
换句话说,如果该对为10的计数为1,但input [[0,0],[1,2],[1,4],[2,2],[2,3],[3,3],[3,4]] 的计数为400,则该对仍应处于(或至少非常接近)列表的前面 - 他们需要按对中最不频繁的元素进行排序。

这是我建立的一个人为的例子:

0: 1   [0,0]
1: 2   [1,2],[1,4]
2: 3   [1,2],[2,2],[2,3]
3: 3   [2,3],[3,3],[3,4]
4: 2   [1,4],[3,4]

以下是单个元素计数及其来源对:

output: [[0,0],[1,4],[1,2],[3,4],[2,2],[2,3],[3,3]]
scores:   1     1-2   1-3   2-3   3     3     3

这是结果,以及配对分数:

0

在这里,1的计数为1(它出现在一个对中,虽然是两次),所以首先出现。 [1,4]计数为2,因此在[1,2]之前显示为4,因为2的计数为2,#my implementation uncommented to reduce post size, see history for comments def sortPairList( data , n ): count = [] for i in range(0,n): count.append( 0 ) #count up the data for p in data: count[p[0]] += 1 if p[1] != p[0]: count[p[1]] += 1 maxcount = 0 for i in range(0,n): if count[i] > maxcount: maxcount = count[i] def elementFrequency(p): if count[ p[0] ] < count[ p[1] ]: return count[ p[0] ] + float(count[ p[1] ]) / (maxcount+1) else: return count[ p[1] ] + float(count[ p[0] ]) / (maxcount+1) data.sort( key=elementFrequency ) 的计数为3,等等。

我目前的解决方案

如上所述,我相信这种说法可以准确地发挥作用,但它只是觉得必须有更好的方法去做这件事。无论如何,这是我到目前为止所得到的:

input:    [[0,0],[0,3],[0,5],[0,7],[1,1],[1,2],[1,8],[2,4],[2,5],[3,4],[3,5],[3,9],[4,4],[4,7],[4,8],[6,8],[7,7],[7,9],[8,9]]
expected: [[6,8],[1,1],[1,2],[2,5],[0,5],[1,8],[3,5],[3,9],[7,9],[8,9],[2,4],[0,0],[0,3],[0,7],[7,7],[3,4],[4,7],[4,8],[4,4]]

有关更“Python”方式的任何建议吗?
或者我目前的尝试出了什么问题?

新测试案例(见答案的评论)

{{1}}

4 个答案:

答案 0 :(得分:4)

我可能会使用Counter(需要Python≥2.7或≥3.1)来计算。

from collections import Counter
from itertools import chain
def sortPairList2(data):
    tally = Counter(chain(*map(set, data)))
    data.sort(key=lambda x: sorted(tally[i] for i in x))

请注意:

  1. 您可以创建an anonymous function with lambda。例如,

    >>> c = 4
    >>> a = lambda p: p - c
    >>> a(7)
    3
    
  2. 排序键不必是数字。任何可比较的东西都可以用作关键功能的返回值。在我的代码中,list用于排序。

  3. Python中有许多比较简单的习惯用法。

    • 可以使用count而不是该循环来初始化count = [0] * n
    • 可以使用the max function获取maxcountmaxcount = max(count)
  4. List comprehension在Python中经常使用。如果您的目标是将迭代变换为另一个迭代,则更喜欢理解循环。

答案 1 :(得分:1)

>>> n = 4
>>> freqs = {i: sum(i in j for j in inp) for i in range(n+1)}
>>> def key(x):
    a, b = x
    return min(freqs[a], freqs[b]), max(freqs[a], freqs[b])

>>> sorted(inp, key=key)

P.S。请注意,input是变量的错误名称,因为它会内置阴影。

答案 2 :(得分:0)

当KennyTM解决方案有效时,我试图自己做。

我的解决方案预先计算频率并将其存储在str(n)是关键字典的字典中。将Python2中已知的比较函数更改为与Python3一起使用的密钥时遇到了一些麻烦,但我在ActiveState code找到了食谱

item_cnt = {}

def icount(n):
    return item_cnt[str(n)]

def add_item(n):
    sn = str(n)
    try:
        item_cnt[sn] += 1
    except KeyError:
        item_cnt[sn] = 1

# sort callback
def cmp_items(ij, kl):
    i, j = ij
    k, l = kl
    if icount(i) < icount(k) or icount(i) == icount(k) and icount(j) < icount(l):
        return -1
    return 1

input = [[0,0],[1,2],[1,4],[2,2],[2,3],[3,3],[3,4]]
# count all items
for (i, j) in input:
    add_item(i)
    add_item(j)

# works with Python 2.x
#input.sort(cmp_items)
# works with Python2.6 and Python 3.x
# to convert compare function to key look at:
# http://code.activestate.com/recipes/576653-convert-a-cmp-function-to-a-key-function/
input.sort(key=cmp_to_key(cmp_items))
print(input)

答案 3 :(得分:0)

与KennyTM的解决方案类似,但对于Python 2.5或更高版本:

import collections

def sort_by_occurence(sequences):
    tally = collections.defaultdict(int)
    for sequence in sequences:
        for item in sequence:
            tally[item] += 1
    sequences.sort(key=lambda x:map(tally.get, x))


pair_list = [[0,0],[1,2],[1,4],[2,2],[2,3],[3,3],[3,4]]
sort_by_occurence(pair_list)
print pair_list