【问题标题】:Python2.7: Extract slice from a list based on a pattern in a Pythonic wayPython2.7:以Pythonic方式基于模式从列表中提取切片
【发布时间】:2017-04-17 23:18:22
【问题描述】:

我的列表中有大量数据。该列表由短字符串组成。列表中隐藏了与特定模式匹配的长度为 5 的切片:

[<date>, <date>, <4 digit integer>, <string>, <$ amount>]

如何从我的数据集中提取这些切片?它们可以出现在任何位置(因此它们的索引不能保证是 5 的倍数)并且散布在可以匹配部分模式的其他数据(也包括字符串)中。

我从类似于以下内容开始:

for item in data:
    if re.search(<date pattern>, item):
        if not date1:
            date1 = item
        else:
            date2 = item
    if re.search(<4 digit integer pattern>, item):
        if date1 and date2 and not fourdigit:
            fourdigit = item
        else:
           date1 = None
           date2 = None
    ....

但这很复杂,容易出错,而且根本不是pythonic。

下一种方法是从数据列表中提取 5 个项目的滑动窗口,并检查所有项目是否与其模式匹配。如果不是,则将索引增加 1(即,将窗口滑动 1)并检查下一个切片。如果模式匹配,保存切片,并将索引增加 5。类似于:

index = 0
while index < (len(data)-5):
    sliceof5 = data[index:index+5]
    if slice_matches_pattern(sliceof5):
        matching_items.append(sliceof5)
        index += 5
    else:
        index += 1

这很有效,并且更容易实现并且以前的解决方案更不容易出错,但看起来也不是很pythonic。

是否有可能使用列表理解来做到这一点?比如:

matching_items = [ sliceof5 if slice_matches_pattern(sliceof5) for sliceof5 in data ]

但是,如何使列表理解中的for 有时向前跳 1 有时向前跳 5。

是否有其他的 Pythonic 方法来实现这一点?

【问题讨论】:

  • 我的第一直觉是使用re 可靠地匹配每个字符串,并使用列表推导或循环将其应用于列表中的所有字符串。另一种选择是将所有字符串连接成一个大字符串(或字符串块)并使用更全面的re匹配器(多行?)
  • 我得到的第二个建议(尤其是在看到@double_j 的答案之后)。我没有得到您的第一个建议,尤其是列表理解部分。例如,一行中可能有 3 个日期,其中只有最后两个日期可以成为我正在寻找的整体模式的一部分。以及如何匹配 5 个模式的序列?在列表理解过程中,您必须以某种方式“记住”您从列表中拉出的项目。

标签: python python-2.7 list list-comprehension


【解决方案1】:

您的第二个解决方案似乎很好。我会将其更改为生成器(yield 您找到的切片),但不会更多。

不过,您可以通过在切片的第 2 项中查找日期来使其运行得更快。如果不是日期,您可以将两个添加到索引中。

当然,如果你能把所有东西都变成一个大的正则表达式来匹配你的整个模式,你会做得更好。

【讨论】:

    【解决方案2】:

    不知道您的数据是什么样的,遵循@hpaulj 的想法,这是一种正则表达式方法。

    import re
    
    data = [
        '2016-12-01', '2016-12-02', '1234', 'spam', '$100',  # collect
        '2016-12-02', 'spam',  # discard
        '2016-12-01', '2016-12-02', '1234', 'spam', '$100',  # collect
        '1234', '2016-12-01',  # discard
        '2016-12-01', '2016-12-02', '1234', 'spam', '$100',  # collect
        '$100', '1234', '1234',  # discard
        '2016-12-01', '2016-12-02', '1234', 'spam', '$100'  # collect
    ]
    
    pattern_sep_str = '||'  # change to something unique in the data
    
    pattern_sep = re.escape(pattern_sep_str)
    date_pattern = r'[0-9]{4}-[0-9]{2}-[0-9]{2}'
    int_pattern = r'[0-9]{4}'
    str_pattern = r'[a-zA-Z]+'
    amount_pattern = r'\$[0-9,.]+'
    
    pattern_combined = ''.join([
        '(', date_pattern, pattern_sep, date_pattern, pattern_sep,
        int_pattern, pattern_sep, str_pattern, pattern_sep,
        amount_pattern, ')'
    ])
    
    results = re.findall(pattern_combined, pattern_sep_str.join(data))
    
    print([x.split(pattern_sep_str) for x in results])
    
    >>> [['2016-12-01', '2016-12-02', '1234', 'spam', '$100'], ['2016-12-01', '2016-12-02', '1234', 'spam', '$100'], ['2016-12-01', '2016-12-02', '1234', 'spam', '$100'], ['2016-12-01', '2016-12-02', '1234', 'spam', '$100']]
    

    【讨论】:

      猜你喜欢
      • 2015-06-11
      • 1970-01-01
      • 2010-10-29
      • 1970-01-01
      • 2016-07-15
      • 2011-04-23
      • 1970-01-01
      • 2017-05-09
      • 2021-11-22
      相关资源
      最近更新 更多