我想从列表中删除重复的内容。对于嵌套列表中的每个第二个元素,第一个元素并不总是唯一的。第一个值对于整个列表是唯一的。这些数字在整个列表中只出现一次,但没有排序。
my_list = [[4, 'C'], [1, 'A'], [3, 'B'], [2, 'A'], [5, 'C']]删除重复项是基于嵌套列表中的第二个元素。我需要每个唯一的第二个元素的最小值,例如:
my_unique_list = [[1, 'A'], [3, 'B'], [4, 'C']]不管输出的顺序是什么。
因此,选择用于1的'A' (as 1低于[2, 'A']的2)、3的'B' ('B'没有其他值)和'C'的4 (as 4小于5,来自[5, 'C'])。
发布于 2020-01-27 11:53:42
使用字典将唯一的字母(第二个值)映射为每个字母的最小值,然后简单地将该字典中的[value, key]对作为输出:
minimi = {}
inf = float('inf')
for val, key in my_list:
# float('inf') as default value is always larger, so val is picked
# if the key isn't present yet.
minimi[key] = min(val, minimi.get(key, inf))
my_unique_list = [[v, k] for k, v in minimi.items()]通过使用字典作为中介,您可以在线性时间内过滤输入。
演示:
>>> my_list = [[4, 'C'], [1, 'A'], [3, 'B'], [2, 'A'], [5,'C']]
>>> minimi, inf = {}, float('inf')
>>> for val, key in my_list:
... minimi[key] = min(val, minimi.get(key, inf))
...
>>> minimi
{'C': 4, 'A': 1, 'B': 3}
>>> my_unique_list = [[v, k] for k, v in minimi.items()]
>>> my_unique_list
[[4, 'C'], [1, 'A'], [3, 'B']]你为什么要关心跑步时间?因为随着输入的增加,运行时间也增加了。对于需要O(N^2) (二次)时间的方法,当您从1000项增加到100万项(是大小的1000倍)时,您的运行时间将增加100万倍!对于O(N logN)方法(使用排序的方法),运行时间将增加2000倍,而上述线性方法将花费1000倍的时间,与输入规模成线性关系。
对于大的输入,这可以使‘花费一两个小时’到‘花费数百万年’之间的差别。
以下是这种方法与扎米尔的排序和集合方法(O(N logN))以及TJC World's Pandas方法(也是O(N LogN))之间的时间试验比较:
from string import ascii_uppercase
from functools import partial
from timeit import Timer
import random
import pandas as pd
def gen_data(N):
return [[random.randrange(1_000_000), random.choice(ascii_uppercase)] for _ in range(N)]
def with_dict(l, _min=min, _inf=float('inf')):
minimi = {}
m_get = minimi.get
for val, key in l:
minimi[key] = _min(val, m_get(key, _inf))
return [[v, k] for k, v in minimi.items()]
def with_set_and_sort(l):
already_encountered = set()
ae_add = already_encountered.add
return [i for i in sorted(l) if i[1] not in already_encountered and not ae_add(i[1])]
def with_pandas(l):
return (
pd.DataFrame(l)
.sort_values(by=0)
.drop_duplicates(1)
.to_numpy()
.tolist()
)
for n in (100, 1000, 10_000, 100_000, 1_000_000):
testdata = gen_data(n)
print(f"{n:,} entries:")
for test in (with_dict, with_set_and_sort, with_pandas):
count, total = Timer(partial(test, testdata)).autorange()
print(f"{test.__name__:>20}: {total/count * 1000:8.3f}ms")
print()我已经使用了我所知道的所有小性能技巧;通过将全局和属性缓存在循环之外的本地名称中,避免了对全局和属性的重复查找。
这一产出如下:
100 entries:
with_dict: 0.028ms
with_set_and_sort: 0.032ms
with_pandas: 2.070ms
1,000 entries:
with_dict: 0.242ms
with_set_and_sort: 0.369ms
with_pandas: 2.312ms
10,000 entries:
with_dict: 2.331ms
with_set_and_sort: 5.639ms
with_pandas: 5.476ms
100,000 entries:
with_dict: 23.105ms
with_set_and_sort: 127.772ms
with_pandas: 40.330ms
1,000,000 entries:
with_dict: 245.982ms
with_set_and_sort: 2494.305ms
with_pandas: 578.952ms因此,只有100个输入,排序方法看起来可能是同样快(几毫秒不同的方式),但随着投入的增长,这种方法失去了一个加速的步伐。
熊猫在这里的各个方面都输掉了。Dataframes是一个很好的工具,但在这里却是错误的工具。它们是庞大的数据结构,因此对于小输入,它们的高开销使它们处于毫秒范围内,远远落后于其他两个选项。在10k条目时,它开始超越排序和集方法,但是即使数据操作高度优化,具有更大输入的排序运行时的增长仍然不能超过线性方法。
发布于 2020-01-27 12:03:58
already_encountered = set()
my_new_list = [i for i in sorted(my_list) if i[1] not in already_encountered and not already_encountered.add(i[1])]输出:
[[1, 'A'], [3, 'B'], [4, 'C']]发布于 2020-01-27 11:57:07
利用大熊猫;
>>> import pandas as pd
>>> my_list = [[1, 'A'], [2, 'A'], [3, 'B'], [4, 'C'], [5,'C']]
>>> df = pd.DataFrame(my_list)
>>> df.sort_values(by = 0).drop_duplicates(1).to_numpy().tolist()
[[1, 'A'], [3, 'B'], [4, 'C']]https://stackoverflow.com/questions/59930540
复制相似问题