我有一个xy坐标列表作为列表:
print(xy[0:10])
[[104.44464000013596, 21.900339999891116],
[9.574480000151937, 0.32839999976022227],
[9.932610000251373, 0.19092000005798582],
[9.821009999711748, 0.26556000039374794],
[9.877130000349268, -0.6701499997226392],
[149.51198999973872, -28.469329999879562],
[149.35872999988965, -28.684280000021943],
[9.859010000211413, -0.03293000041912819],
[9.38918000035676, -0.9979400000309511],
[77.35380000007001, 32.926530000359264]]
这里显示的是前10个,但我的列表中有约100,000个坐标对。
我想从此列表中删除所有重复的列表,但要有效。作为一个易于理解的示例,我想创建一个产生以下结果的函数remove_dupes
:
a = [[1, 2], [3, 4], [5, 6], [1, 2], [1, 2], [8, 9], [3, 4]]
b = remove_dupes(a)
print(b)
b = [[5, 6], [8 ,9]]
请注意,保留订单很重要。
但是,由于列表很大,因此我发现使用.count()方法并遍历列表非常耗时。我还尝试了set()和numpy的独特功能的各种技巧。
这是我能想到的最快的版本:
xy = [[x1,y1], [x2,y2], ... [xn, yn]]
def remove_dupes(xy):
xy = [tuple(p) for p in xy] # Tupelize coordinates list for hashing
p_hash = [hash(tuple(p)) for p in xy] # Hash each coordinate-pair list to a single value
counts = Counter(p_hash) # Get counts (dictionary) for each unique hash value
p_remove = [key for (key, value) in counts.items() if value > 1] # Store keys with count > 1
p_hash = np.array(p_hash) # Cast from list to numpy array
remove = np.zeros((len(xy),1), dtype=np.bool) # Initialize storage
for p in p_remove: # Loop through each non-unique hash and set all the indices where it appears to True // Most time-consuming portion
remove[np.where(p==p_hash)[0]] = True
xy = np.array(xy) # Cast back to numpy array for indexing
xy = xy[remove.flat==False, :] # Keep only the non-duplicates
return xy
对于〜100,000个值,这需要约2秒的时间(如果有更多重复的对,三元组等,则需要更长的时间)。让我感到困扰的是,有像numpy.unique()这样的函数可以在不到一秒钟的时间内返回计数和索引,但是我无法弄清楚如何使它们的输出一致以解决这个问题。我浏览了其他类似的数十个Stackexchange帖子,但没有发现既高效又优雅的东西。有没有人提出比我在这里提出的解决方案更优雅的建议?
编辑:
我已经收到两个提供正确结果(并保留顺序)的答案。 RafaelC提供了Pandas选项,而DYZ提供了Counter选项。我对如何正确安排时间不太熟悉,但是我两次都运行了100次迭代(对同一数据),结果如下(使用time.time())
熊猫:13.02秒
计数器:28.15秒
我不确定为什么Pandas实施更快?区别在于Pandas解决方案返回了元组(没关系),因此我尝试了Counter解决方案而没有转换回列表,仍然是25秒。
答案 0 :(得分:3)
我会使用pandas
s = pd.Series(list(map(tuple, l)))
s[~s.duplicated(keep=False)].tolist()
需要
211 ms ± 16.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
对于100000个条目,速度提高了10倍。
答案 1 :(得分:2)
考虑使用计数器:
from collections import Counter
首先,将您的列表转换为元组,因为元组是不可变的。然后对元组进行计数,并仅选择只发生一次的元组。这是非重复的设置:
nodups = {k for k,cnt in Counter(map(tuple, a)).items() if cnt == 1}
现在,由于顺序很重要,请针对非重复项过滤原始列表:
[list(k) for k in map(tuple, a) if k in nodups]
#[[5, 6], [8, 9]]
答案 2 :(得分:1)
在Python 3.6+词典中,它们保持其插入顺序,因此DYZ的Counter
解决方案可以通过以下方式得到很大改进:
[list(k) for k, c in Counter(map(tuple, a)).items() if c == 1]
在我的计算机上,它比熊猫解决方案快。
RafaelC的熊猫解决方案也可以大大加速。关键是要从Series
切换到DataFrame
:
s = pd.DataFrame(a)
return s[~s.duplicated(keep=False)].values.tolist()
在我的计算机上,它的速度几乎是原始熊猫解决方案的两倍。加快速度的关键在于避免在大熊猫(list(map(tuple, l))
)之外进行准备工作。
答案 3 :(得分:-1)
我有一个高效且内置的解决方案
function dateFromElement($element) {
return new Date($element.data('date').split('.').reverse().join('-'));
}
$('#selsort').on('change', function() {
$('[data-date]')
.sort((date1El, date2El) => $(this).val() === 'asc'
? dateFromElement($(date1El)) - dateFromElement($(date2El))
: dateFromElement($(date2El)) - dateFromElement($(date1El)))
.appendTo($('[data-wrapper]'));
});
注意:我已经测试了两种方法,分别使用 numpy 和 itertools 。 Numpy在长度为10000000的数据中花费了13秒,而intertool在长度为10000000的数据中花费了1秒