【问题标题】:Python - how to extract the last occurrence meeting a certain condition from a listPython - 如何从列表中提取满足特定条件的最后一次出现
【发布时间】:2016-10-24 15:35:01
【问题描述】:

例如,我有以下数据作为列表:

l = [['A', 'aa', '1', '300'],
     ['A', 'ab', '2', '30'],
     ['A', 'ac', '3', '60'],
     ['B', 'ba', '5', '50'],
     ['B', 'bb', '4', '10'],
     ['C', 'ca', '6', '50']]

现在对于'A''B''C',我想获取它们的最后一次出现,即:

[['A', 'ab', '3', '30'],
 ['B', 'bb', '4', '10'],
 ['C', 'ca', '6', '50']]

或更进一步,这些事件中的第三列,即:

['3', '4', '6']

目前,我的处理方式是:

import pandas as pd
df = pd.DataFrame(l, columns=['u', 'w', 'y', 'z'])
df.set_index('u', inplace=True)
ll = []
for letter in df.index.unique():
    ll.append((df.ix[letter, 'y'][-1]))

然后我%timeit,它显示:

>> The slowest run took 27.86 times longer than the fastest. 
>> This could mean that an intermediate result is being cached.
>> 1000000 loops, best of 3: 887 ns per loop

只是想知道是否有一种方法可以使用比我的代码更少的时间来做到这一点?谢谢!

【问题讨论】:

  • 你目前的低效方式是什么?
  • 为什么A 的最后一次出现是第二个而不是第三个数组?
  • 在您的列表中使用反向然后 - 可能与 What is the best way to get the first item from an iterable matching a condition? 重复
  • @jonrsharpe 我首先将此列表转换为熊猫数据框,将第一列设置为索引,然后迭代唯一索引值以提取每个索引的最后一次出现,我认为这不是有效的所以我正在寻找更好的方法来做到这一点。
  • "Better" 很难判断没有: 1. 我们试图变得更好;和 2. 你如何更好地衡量。

标签: python list data-manipulation


【解决方案1】:
l =  [['A', 'aa', '1', '300'],
  ['A', 'ab', '2', '30'],
  ['A', 'ac', '3', '60'],
  ['B', 'ba', '5', '50'],
  ['B', 'bb', '4', '10'],
  ['C', 'ca', '6', '50']]

import itertools
for key, group in itertools.groupby(l, lambda x: x[0]):
    print key, list(group)[-1]

不评论“效率”,因为您根本没有解释您的条件。假设列表预先按子列表的第一个元素排序。

如果列表已排序,则运行一次就足够了:

def tidy(l):
    tmp = []
    prev_row = l[0]

    for row in l:
        if row[0] != prev_row[0]:
            tmp.append(prev_row)
        prev_row = row
    tmp.append(prev_row)
    return tmp

在 timeit 测试中,这比 itertools.groupby 快约 5 倍。演示:https://repl.it/C5Af/0

[编辑:OP 更新了他们的问题,说他们已经在使用 Pandas 进行分组,这可能已经更快了]

【讨论】:

  • 抱歉,错误地编辑了这个,现在似乎无法删除它!如果可以,请随意删除,现在已将其添加到我的答案中!
  • @NilsGudat 没关系,我拒绝了编辑。我预计itertools.groupby 方法会更慢,因为它正在构建 GroupInfo 对象和新列表。很可能通过遍历列表来做到这一点,假设列表已排序,我认为它非常 Pythonic 并且更清楚地表达了它在做什么。
【解决方案2】:

{l[0]: l[2] for l in vals} 将为您提供“A”、“B”和“C”到它们最后值的映射

【讨论】:

  • 嗨,介意解释一下你的代码吗?我不太明白如何使用它来获得结果。顺便问一下,什么是“vals”?谢谢!
  • vals 是您的输入(您的列表)。至于代码本身,请阅读 dict comprehensions,您将了解它是如何工作的。
  • 是否可以让它返回像['3', '4', '6'] 这样的列表而不是字典?
【解决方案3】:

尽管我不确定我是否理解您的问题,但您可以这样做:

li = [l[i][0] for i in range(len(l))]
[l[j][2] for j in [''.join(li).rfind(i) for i in set(li)]]

请注意,输出是[3,4,6],因为A 的最后一次出现似乎是第三个数组,而不是第二个数组。

编辑,因为您似乎非常关心性能(尽管您没有说您尝试过什么以及什么是“好”):

%timeit li = [l[i][0] for i in range(len(l))]
%timeit [l[j][2] for j in [''.join(li).rfind(i) for i in set(li)]]
>> 1000000 loops, best of 3: 1.19 µs per loop
>> 100000 loops, best of 3: 2.57 µs per loop

%timeit [list(group)[-1][2] for key, group in itertools.groupby(l, lambda x: x[0])]
>> 100000 loops, best of 3: 5.11 µs per loop

因此,列表理解似乎比 itertools 略快(尽管我不是基准测试专家,可能有更好的方法来运行 itertools)。

【讨论】:

    【解决方案4】:

    一种不是非常 Python 的方法:(请注意,Nils 的解决方案是最 Python 的 - 使用列表理解)

    def get_last_row(xs,q):
        for i in range(len(xs)-1,-1,-1):
            if xs[i][0] == q:
                return xs[i][2]
    
    def get_third_cols(xs):
        third_cols = []
        for q in ["A","B","C"]:
            third_cols.append(get_last_row(xs,q))
        return third_cols
    
    print get_third_cols(xs)
    

    如果这是您上次出现的意思,这将打印 ['3', '4', '6']

    【讨论】:

      【解决方案5】:

      这将推广到任何键/值位置。请注意,输出将按照观察到第一个键的顺序。不难调整,让输出的顺序就是观察输出值的顺序

      import operator
      
      l = [['A', 'aa', '1', '300'],
        ['A', 'ab', '2', '30'],
        ['A', 'ac', '3', '60'],
        ['B', 'ba', '5', '50'],
        ['B', 'bb', '4', '10'],
        ['C', 'ca', '6', '50']]
      
      def getLast(data, key, value):
          f = operator.itemgetter(key,value)
          store = dict()
          keys = []
          for row in data:
              key, value = f(row)
              if key not in store:
                  keys.append(key)
              store[key] = value
          return [store[k] for k in keys]
      

      现在计时,

      %timeit getLast(l,0,2)
      

      给予:

      The slowest run took 9.44 times longer than the fastest. This could mean that an intermediate result is being cached 
      100000 loops, best of 3: 2.85 µs per loop
      

      还有函数输出:

      ['3', '4', '6']
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多