Pandas 筛选多个串联子字符串

2024-12-18 08:38:00
admin
原创
169
摘要:问题描述:我需要过滤pandas数据框中的行,以便特定字符串列至少包含提供的子字符串列表中的一个。子字符串可能包含不寻常的/正则表达式字符。比较不应涉及正则表达式并且不区分大小写。例如:lst = ['kdSj;af-!?', 'aBC+dsfa?-', 'sdKaJg|dksaf-*'] 我目前是这样敷面膜...

问题描述:

我需要过滤pandas数据框中的行,以便特定字符串列至少包含提供的子字符串列表中的一个。子字符串可能包含不寻常的/正则表达式字符。比较不应涉及正则表达式并且不区分大小写。

例如:

lst = ['kdSj;af-!?', 'aBC+dsfa?-', 'sdKaJg|dksaf-*']

我目前是这样敷面膜的:

mask = np.logical_or.reduce([df[col].str.contains(i, regex=False, case=False) for i in lst])
df = df[mask]

我的数据框很大(约 100 万行),lst长度为 100。有没有更有效的方法?例如,如果lst找到了第一个项目,我们就不必测试该行的任何后续字符串。


解决方案 1:

如果您坚持使用纯熊猫,那么出于性能和实用性的考虑,我认为您应该使用正则表达式来完成此任务。但是,您需要先正确转义子字符串中的任何特殊字符,以确保它们按字面意思匹配(而不是用作正则表达式元字符)。

使用以下方法可以轻松实现re.escape

>>> import re
>>> esc_lst = [re.escape(s) for s in lst]

然后可以使用正则表达式管道将这些转义的子字符串连接起来|。可以将每个子字符串与字符串进行检查,直到有一个匹配(或所有子字符串都经过测试)。

>>> pattern = '|'.join(esc_lst)

然后,屏蔽阶段就变成了遍历各行的单个低级循环:

df[col].str.contains(pattern, case=False)

以下是一个简单的设置,可以帮助您了解性能:

from random import randint, seed

seed(321)

# 100 substrings of 5 characters
lst = [''.join([chr(randint(0, 256)) for _ in range(5)]) for _ in range(100)]

# 50000 strings of 20 characters
strings = [''.join([chr(randint(0, 256)) for _ in range(20)]) for _ in range(50000)]

col = pd.Series(strings)
esc_lst = [re.escape(s) for s in lst]
pattern = '|'.join(esc_lst)

所提出的方法大约需要 1 秒(因此 100 万行可能需要长达 20 秒):

%timeit col.str.contains(pattern, case=False)
1 loop, best of 3: 981 ms per loop

问题中的方法使用相同的输入数据大约需要 5 秒。

值得注意的是,这些时间是“最坏情况”,因为没有匹配项(因此检查了所有子字符串)。如果有匹配项,则时间会有所改善。

解决方案 2:

您可以尝试使用Aho-Corasick 算法。在一般情况下,它是搜索字符串的长度,O(n+m+p)是搜索文本的长度,是输出匹配的数量。n`m`p

Aho-Corasick 算法通常用于在输入文本(大海捞针)中查找多种模式(针)。

pyahocorasick是该算法的 C 实现的 Python 包装器。


让我们比较一下它与一些替代方案的速度。下面的基准测试表明,using_aho_corasick在 50K 行 DataFrame 测试用例中,它比原始方法(问题中显示)快 30 倍以上:

|                    |     speed factor | ms per loop |
|                    | compared to orig |             |
|--------------------+------------------+-------------|
| using_aho_corasick |            30.7x |         140 |
| using_regex        |             2.7x |        1580 |
| orig               |             1.0x |        4300 |

In [89]: %timeit using_ahocorasick(col, lst)
10 loops, best of 3: 140 ms per loop

In [88]: %timeit using_regex(col, lst)
1 loop, best of 3: 1.58 s per loop

In [91]: %timeit orig(col, lst)
1 loop, best of 3: 4.3 s per loop

这是用于基准测试的设置。它还验证输出是否与返回的结果匹配orig

import numpy as np
import random
import pandas as pd
import ahocorasick
import re

random.seed(321)

def orig(col, lst):
    mask = np.logical_or.reduce([col.str.contains(i, regex=False, case=False) 
                                 for i in lst])
    return mask

def using_regex(col, lst):
    """https://stackoverflow.com/a/48590850/190597 (Alex Riley)"""
    esc_lst = [re.escape(s) for s in lst]
    pattern = '|'.join(esc_lst)
    mask = col.str.contains(pattern, case=False)
    return mask

def using_ahocorasick(col, lst):
    A = ahocorasick.Automaton(ahocorasick.STORE_INTS)
    for word in lst:
        A.add_word(word.lower())
    A.make_automaton() 
    col = col.str.lower()
    mask = col.apply(lambda x: bool(list(A.iter(x))))
    return mask

N = 50000
# 100 substrings of 5 characters
lst = [''.join([chr(random.randint(0, 256)) for _ in range(5)]) for _ in range(100)]

# N strings of 20 characters
strings = [''.join([chr(random.randint(0, 256)) for _ in range(20)]) for _ in range(N)]
# make about 10% of the strings match a string from lst; this helps check that our method works
strings = [_ if random.randint(0, 99) < 10 else _+random.choice(lst) for _ in strings]

col = pd.Series(strings)

expected = orig(col, lst)
for name, result in [('using_regex', using_regex(col, lst)),
                     ('using_ahocorasick', using_ahocorasick(col, lst))]:
    status = 'pass' if np.allclose(expected, result) else 'fail'
    print('{}: {}'.format(name, status))

解决方案 3:

使用更简单的例子并忽略大小写(大写或小写)

过滤并获取二进制向量:

我想查找 a pd.Series,v中包含“at”或“Og”的所有元素。如果元素包含模式,则获取 1;如果不包含模式,则获取 0。

我将使用 re

import re

我的向量:

v=pd.Series(['cAt','dog','the rat','mouse','froG'])

[Out]:

0        cAt
1        dog
2    the rat
3      mouse
4       froG

我想找到 v 中包含“at”或“Og”的所有元素。也就是说,我可以将我的定义pattern为:

pattern='at|Og'

因为我想要一个向量,如果项目包含模式则为 1,如果不包含则为 0。

我创建一个与 v 长度相同的酉向量:

v_binary=[1]*len(v)

我得到一个布尔值s,即True其中一个元素是否v包含它,pattern或者False它是否不包含它。

s=v.str.contains(pattern, flags=re.IGNORECASE, regex=True)

为了获得二进制向量,我将v_binary*乘以s

v_binary*s

[Out]

0    1
1    1
2    1
3    0
4    1
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1553  
  IPD(Integrated Product Development)流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。其中,技术评审与决策评审是IPD流程中至关重要的环节,它们既有明显的区别,又存在紧密的协同关系。深入理解这两者的区别与协同,对于企业有效实施IPD流程,提升产品开发效率与质量具有重要意义...
IPD管理流程   27  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、ClickUp、Freshdesk、GanttPRO、Planview、Smartsheet、Asana、Nifty、HubPlanner、Teamwork。在当今快速变化的商业环境中,项目管理软件已成为企业提升效率、优化资源分配和确保项目按时交付的关键工具。然而...
项目管理系统   22  
  建设工程项目质量关乎社会公众的生命财产安全,也影响着企业的声誉和可持续发展。高质量的建设工程不仅能为使用者提供舒适、安全的环境,还能提升城市形象,推动经济的健康发展。在实际的项目操作中,诸多因素会对工程质量产生影响,从规划设计到施工建设,再到后期的验收维护,每一个环节都至关重要。因此,探寻并运用有效的方法来提升建设工程...
工程项目管理制度   19  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用