Python:仅在列表末尾删除重复值
Python: remove repeated values only if at end of list
我有一个 python 列表,其中回复的顺序很重要。我只想过滤掉出现在列表末尾的 nan
值。我想知道是否有一种 有效的 方法可以从如下列表中获取:
nan = float("nan")
responses = [1.0, nan, 9.0, nan, nan, nan, nan, nan, nan, nan, nan]
到没有任何尾随 nan
值的列表:
[1.0, nan, 9.0]
我知道如何使用列表理解过滤掉所有 nan
值:
import pandas as pd
[r for r in responses if pd.notnull(r)]
>>> [1.0, 9.0]
但是想不出一种直接的方法来过滤掉最后的 nan
值而不将所有内容都转换为字符串并使用正则表达式。我可以这样做,但是我担心性能,这是一个问题,因为它会被执行几十万次。
没有内置函数或方法。但是你可以使用一个循环:
while responses and math.isnan(responses[-1]):
del responses[-1]
如您所见,它以线性时间运行并且不使用额外的 space。
while responses and math.isnan(responses[-1]):
responses.pop()
更新:这不如直接切片快。
>>> timeit.timeit('responses = list(r)\nwhile responses and isnan(responses[-1]): responses.pop()', 'from math import isnan; nan = float("nan"); r = [1.0, nan, 9.0, nan, nan, nan, nan, nan, nan, nan, nan]')
1.3209394318982959
>>> timeit.timeit('responses = list(r)\nresponses = responses[:3]', 'from math import isnan; nan = float("nan"); r = [1.0, nan, 9.0, nan, nan, nan, nan, nan, nan, nan, nan]')
0.29652016144245863
你可以反转它并使用itertools.dropwhile
。这应该适用于任何值。
r = [1.0, nan, 9.0, nan, nan, nan, nan, nan, nan, nan, nan]
list(itertools.dropwhile(lambda x: x == r[-1], reversed(r)))[::-1] + r[-1:]
要仅过滤 nan
,您可以将 lambda x: x == r[-1]
替换为 math.isnan
:
list(itertools.dropwhile(math.isnan, reversed(r)))[::-1]
我要做的是遍历列表一次,然后找到 nans
结束序列的开始位置。像
responses = [1.0, 'nan', 9.0, 'nan', 'nan', 'nan', 'nan', 'nan', 'nan', 'nan', 'nan']
first_index = -1
for i, val in enumerate(responses):
if val == 'nan':
if first_index == -1:
first_index = i
else:
first_index = -1
responses = responses[:first_index] # [1.0, 'nan', 9.0]
然后就可以进行单片操作了。与其他解决方案相比,它有点冗长,但应该更快。
时间复杂度
根据to this page,切片操作是 O(n),迭代列表是 O(n),使得整个算法复杂度为 O(n)。
更好的方法是向后迭代列表。
我有一个 python 列表,其中回复的顺序很重要。我只想过滤掉出现在列表末尾的 nan
值。我想知道是否有一种 有效的 方法可以从如下列表中获取:
nan = float("nan")
responses = [1.0, nan, 9.0, nan, nan, nan, nan, nan, nan, nan, nan]
到没有任何尾随 nan
值的列表:
[1.0, nan, 9.0]
我知道如何使用列表理解过滤掉所有 nan
值:
import pandas as pd
[r for r in responses if pd.notnull(r)]
>>> [1.0, 9.0]
但是想不出一种直接的方法来过滤掉最后的 nan
值而不将所有内容都转换为字符串并使用正则表达式。我可以这样做,但是我担心性能,这是一个问题,因为它会被执行几十万次。
没有内置函数或方法。但是你可以使用一个循环:
while responses and math.isnan(responses[-1]):
del responses[-1]
如您所见,它以线性时间运行并且不使用额外的 space。
while responses and math.isnan(responses[-1]):
responses.pop()
更新:这不如直接切片快。
>>> timeit.timeit('responses = list(r)\nwhile responses and isnan(responses[-1]): responses.pop()', 'from math import isnan; nan = float("nan"); r = [1.0, nan, 9.0, nan, nan, nan, nan, nan, nan, nan, nan]')
1.3209394318982959
>>> timeit.timeit('responses = list(r)\nresponses = responses[:3]', 'from math import isnan; nan = float("nan"); r = [1.0, nan, 9.0, nan, nan, nan, nan, nan, nan, nan, nan]')
0.29652016144245863
你可以反转它并使用itertools.dropwhile
。这应该适用于任何值。
r = [1.0, nan, 9.0, nan, nan, nan, nan, nan, nan, nan, nan]
list(itertools.dropwhile(lambda x: x == r[-1], reversed(r)))[::-1] + r[-1:]
要仅过滤 nan
,您可以将 lambda x: x == r[-1]
替换为 math.isnan
:
list(itertools.dropwhile(math.isnan, reversed(r)))[::-1]
我要做的是遍历列表一次,然后找到 nans
结束序列的开始位置。像
responses = [1.0, 'nan', 9.0, 'nan', 'nan', 'nan', 'nan', 'nan', 'nan', 'nan', 'nan']
first_index = -1
for i, val in enumerate(responses):
if val == 'nan':
if first_index == -1:
first_index = i
else:
first_index = -1
responses = responses[:first_index] # [1.0, 'nan', 9.0]
然后就可以进行单片操作了。与其他解决方案相比,它有点冗长,但应该更快。
时间复杂度
根据to this page,切片操作是 O(n),迭代列表是 O(n),使得整个算法复杂度为 O(n)。
更好的方法是向后迭代列表。