如何在Python中存储标准列表?

时间:2015-06-04 09:24:49

标签: python

我正在迭代地生成数据列表。这些列表中的每一个都具有相同数量的值,我的目标是存储N最差列表,此标准由特定列定义。 我尝试了几件事,但没有一件让我满意,而且我想知道我是否错过了什么。

对于这个例子,让我们假设我的每一行包含5个元素,并且我想保留10个最差的行。 我想比较最后一个值,知道这个值总是正面的。

array = [[0] * 5] * 10
while (...)
    # processing
    # I now have a "my_row" that looks like [5, 102.24, -3.12, 2, 7.37] for instance
    indexes = [array.index(row) for row in array if row[-1] == min(r[-1] for r in array)] # can return several indexes
    if array[indexes[0]][-1] < my_row[-1]:
        array[indexes[0]] = my_row

然而,这种解决方案远非优雅,也不是最优的。 有没有人知道如何更好地编码?

感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

sortedContainers库有一个sortedlistwithkey容器可以执行您想要的操作:

rows = [[5, 102.24, -3.12, 2, 9.36], [2, 102.24, -3.12, 2, 388], [2, 102.24, -3.12, 1, 1.54],
        [5, 102.24, -3.12, 2, 1.11], [5, 102.24, -3.12, 2, 7.35], [5, 102.24, -3.12, 2, 54],
        [5, 102.24, -3.12, 2, 1.53]]

from sortedcontainers import sortedlistwithkey
from operator import itemgetter
array = sortedlistwithkey.SortedListWithKey(key=itemgetter(-1))

n = 3
for row in rows:
    array.add(row)
    if len(array) > n:
            array.pop(0)
print(array.as_list())

输出:

[[5, 102.24, -3.12, 2, 9.36], [5, 102.24, -3.12, 2, 54], [2, 102.24, -3.12, 2, 388]]

您所要做的就是每次弹出最低元素。

或者否定键值并从最后弹出:

from sortedcontainers import sortedlistwithkey

array = sortedlistwithkey.SortedListWithKey(key=lambda x: -x[-1])
n = 3
for row in rows:
    array.add(row)
    if len(array) > n:
        array.pop()
print(array.as_list())

输出:

[[2, 102.24, -3.12, 2, 388], [5, 102.24, -3.12, 2, 54], [5, 102.24, -3.12, 2, 9.36]]

最大尺寸的数组将增长到n + 1,您无需进行排序,复制或切片。

如果您只关心最后一个值,也可以稍微修改bisect_right函数:

def bisect_right(a, x, lo=0, hi=None):
    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo + hi) // 2
        if x > a[mid][-1]:
            hi = mid
        else:
            lo = mid + 1
    return lo

array = []
n = 3
for row in rows:
    b = bisect_right(array, row[-1])
    array.insert(b, row)
    if len(array) > n:
        array.pop()
print(array)

输出:

[[2, 102.24, -3.12, 2, 388], [5, 102.24, -3.12, 2, 100], [2, 102.24, -3.12, 97]]

所有行具有相同最大值的行:

rows = [ [5, 102.24, -3.12, 2, 100], [2, 102.24, -3.12, 2, 2], [2, 102.24, -3.12, 97],
        [5, 102.24, -3.12, 2, 1.11], [5, 102.24, -3.12, 2, 23], [5, 102.24, -3.12, 2, 54],
        [5, 102.24, -3.12, 2, 1.53], [5, 102.24, -3.12, 2, 100], [5, 102.24, -3.12, 2, 100]]

输出:

[[5, 102.24, -3.12, 2, 100], [5, 102.24, -3.12, 2, 100], [5, 102.24, -3.12, 2, 100]]

如果您关心多个值,也可以将更多密钥传递给sortedlistwithkey

array = sortedlistwithkey.SortedListWithKey(key=lambda x: (-x[-1], -x[-2]))

您还可以通过执行一些简单的类型转换来加速bisect_function并使用cython编译:

def bisect_right(a, int x, int lo=0, int hi= -1):
    cdef int mid
    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi == -1:
        hi = len(a)
    while lo < hi:
        mid = (lo + hi) // 2
        if x > a[mid][-1]:
            hi = mid
        else:
            lo = mid + 1
    return lo

答案 1 :(得分:0)

首先,[[0] * 5] * 10没有为您创建一个正确的列表,您创建了一个嵌套列表,其所有元素都指向一个对象(一个地址),您可以使用列表推导创建它:

array = [[0 for _ in range(5)] for _ in range(10)]

对于您的代码,您可以使用zip函数来获取列,而不是list.index您可以使用enumerate

while (...)
    # processing
    # I now have a "my_row" that looks like [5, 102.24, -3.12, 2, 7.37] for instance
    col=zip(*array)
    indexes = [i for i,row in enumerate(array) if row[-1] == min(col[-1])] # can return several indexes
    if array[indexes[0]][-1] < my_row[-1]:
        array[indexes[0]] = my_row

同样基于您实际想要做的事情,它可以更有效率,例如您处理大型列表,您可以使用itertools模块,例如使用itertools.izip而不是zip。或者你可以减少你的条件。例如,如果你想要数组中的一行,其最后一行是最小的,你只需使用min函数和key函数:

from operator import itemgetter
index = min(enumerate(array),key=lambda x:itemgetter(-1)(x[1]))[0]

另一个例子考虑以下嵌套列表:

>>> array = [[i for i in range(5)] for _ in  range(10)]
>>> array
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]

>>> import random
>>> for i in array:
...   random.shuffle(i)
... 
>>> array
[[1, 2, 4, 0, 3], [2, 1, 0, 4, 3], [2, 0, 3, 4, 1], [4, 3, 2, 0, 1], [4, 3, 2, 1, 0], [0, 2, 4, 1, 3], [1, 4, 0, 3, 2], [2, 3, 1, 0, 4], [2, 3, 4, 0, 1], [3, 4, 2, 0, 1]]

现在我想用最小的最后一项罚款行:

>>> index =min(enumerate(array),key=lambda x:itemgetter(-1)(x[1]))[0]
>>> index
4

您可以使用heapq.nlargest获取N个最大元素。例如,您可以使用以下代码根据最后一个元素获取前5行:

>>> heapq.nlargest(5,array,key=itemgetter(-1))
[[2, 3, 1, 0, 4], [1, 2, 4, 0, 3], [2, 1, 0, 4, 3], [0, 2, 4, 1, 3], [1, 4, 0, 3, 2]]

如果您想要这些索引,可以使用zipenumerate

>>> zip(*heapq.nlargest(5,enumerate(array),key=lambda x:itemgetter(-1)(x[1])))[0]
(7, 0, 1, 5, 6)

答案 2 :(得分:0)

我不完全确定我是否遵循您的示例代码(您使用“行”来引用同一语句中的两个不同的东西没有帮助),所以我做了一个更简单的示例,其中数据是字符串 - 但您可以使用数组或元组或任意对象,因为您可以定义自定义“条件”函数进行排序:

data = ["abc", "bup", "zok", "foo", "gek", "ick"]
criteria = lambda item: item[1] # use any rule you want instead
N = 3

toplist = sorted(data[:N], key=criteria)
for item in data[N:]:
    if criteria(item) < criteria(toplist[-1]):
        toplist.append(item)
        toplist = sorted(toplist, key=criteria)
        toplist = toplist[:N] # only keep the top N items

print toplist

最后,根据您的标准,“toplist”是您的前N个元素

有关效果的说明:

排序不会非常昂贵,因为您每次都要排序最多N + 1个元素,并且只有在实际存在要添加的元素时才这样做(如果您没有病理数据,则应该是少数元素)。

通过利用列表已经排序并使用insert_in_sorted函数这一事实,您可以稍微改进一些事情,这是留给读者的练习。