【问题标题】:Python: remove repeated values only if at end of listPython:仅在列表末尾删除重复值
【发布时间】:2016-01-06 11:06:12
【问题描述】:

我有一个 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 值的方法。我可以这样做,但我担心性能,这是一个问题,因为它会执行几十万次。

【问题讨论】:

    标签: python list nan


    【解决方案1】:

    没有内置函数或方法。但是你可以使用循环:

    while responses and math.isnan(responses[-1]):
        del responses[-1]
    

    如您所见,这在线性时间中运行并且不使用额外空间。

    【讨论】:

      【解决方案2】:
      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
      

      【讨论】:

      • > 会执行几十万次。
      • 使用responses = responses[:3],您正在创建一个新列表。尝试计时 responses[:] = responses[:3]del responses[3:](不过,请记住,仅当 responses 是一个大列表时,计时的差异才是相关的)
      • 顺便说一句,请注意:通过使用timeit()setup 参数初始化列表,您只初始化了一次。也就是说,timeit() 会运行你的代码 10000 次,但列表只会被修改一次。
      • @AaronDuke 正确更新以解决 Andrea 的评论,单个切片明显更快。这个答案不是最佳的。
      【解决方案3】:

      您可以反转它并使用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]
      

      【讨论】:

        【解决方案4】:

        我要做的是遍历列表一次,然后找到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)。

        更好的是向后迭代列表。

        【讨论】:

        • 很难想出一个不是 O(n) 的解决方案。这个的实际表现是什么?你可以和timeit联系吗?
        猜你喜欢
        • 2016-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多