我正在迭代地生成数据列表。这些列表中的每一个都具有相同数量的值,我的目标是存储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
然而,这种解决方案远非优雅,也不是最优的。 有没有人知道如何更好地编码?
感谢您的帮助!
答案 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]]
如果您想要这些索引,可以使用zip
和enumerate
:
>>> 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函数这一事实,您可以稍微改进一些事情,这是留给读者的练习。