【问题标题】:Remove all alphanumeric words from a string using pandas使用熊猫从字符串中删除所有字母数字单词
【发布时间】:2021-08-27 14:43:46
【问题描述】:

我有一个 pandas 数据框列,其中的字符串看起来像

'2fvRE-Ku89lkRVJ44QQFN 算盘实验室,INC'

我想把它转换成样子

'ABACUS LABS, INC'.

我的代码:

list1 = data_df['Vendor'].str.split()
print(list1)
excludeList = list()
for y in list1:
    if (any([x for x in y if x.isalpha()]) and any([x for x in y if x.isdigit()])) :
      excludeList.append(y)
    if y.isdigit() or len(y) == 1:
      excludeList.append(y)
resList = [x for x in list1 if x not in excludeList]
print(restList)

但是它给了我一个错误

'list' 对象没有属性 'isdigit'

谁能帮助我如何从字符串中删除字母数字单词并仅在我的 pandas 数据框列中保留文本部分?

【问题讨论】:

  • 请也分享一部分数据。
  • 用简化版本编辑了我的解决方案。仍然坚持你的测试逻辑,没有字母和数字。

标签: python regex pandas


【解决方案1】:

你可以使用

data_df = pd.DataFrame({'Vendor': ['2fvRE-Ku89lkRVJ44QQFN ABACUS LABS, INC', 'abc123 CAT LABS, INC']})
data_df['Vendor'].str.replace(r'^(?:[A-Za-z-]+\d|[\d-]+[A-Za-z])[\w-]*\s*', '', regex=True)
# => 0    ABACUS LABS, INC
#    1       CAT LABS, INC
#    Name: Vendor, dtype: object

请参阅regex demo

正则表达式详细信息

  • ^ - 字符串开头
  • (?:[A-Za-z-]+\d|[\d-]+[A-Za-z]) - 一个或多个字母/破折号,然后是一个数字,或者一个或多个数字/破折号,然后是一个字母
  • [\w-]* - 零个或多个字或- 字符
  • \s* - 零个或多个空白字符。

【讨论】:

    【解决方案2】:

    假设您在数据框df 中有列Text,您可以尝试:

    df2 = df['Text'].str.split().explode()
    m = df2.str.contains(r'[A-Za-z]') & df2.str.contains(r'\d')
    df_out = df2[~m].groupby(level=0).agg(' '.join)
    df_out = df_out.to_frame(name='Text')
    

    说明

    我们将文本拆分为单独的单词,然后将单词列表分解为多行,一行中包含一个单词。然后我们使用.str.contains()通过正则表达式测试单词是否包含任何字母字符和数字,如下所示:

    .str.contains(r'[A-Za-z]')     # test any character in [A-Za-z] in string
    

    .str.contains(r'\d')           # test any numeric digit in string
    

    然后使用 alpha 和 digit 测试的布尔掩码 m,我们仅选择那些不包含 both alpha 和数字的行条目:

    df2[~m]
    

    然后,我们通过使用将过滤后的单词(没有字母数字单词)组装回一个句子

    groupby(level=0).agg(' '.join)
    

    这里,我们按level=0分组,这是爆炸前的原始行索引(即原始行号)。


    演示

    data = {'Text': ['2fvRE-Ku89lkRVJ44QQFN ABACUS LABS, INC', 'abc123 CAT LABS, INC']}
    df = pd.DataFrame(data)
    
                                         Text
    0  2fvRE-Ku89lkRVJ44QQFN ABACUS LABS, INC
    1                    abc123 CAT LABS, INC
    
    
    df2 = df['Text'].str.split().explode()
    m = df2.str.contains(r'[A-Za-z]') & df2.str.contains(r'\d') 
    df_out = df2[~m].groupby(level=0).agg(' '.join)
    df_out = df_out.to_frame(name='Text')
    
    
                   Text
    0  ABACUS LABS, INC
    1     CAT LABS, INC
    

    编辑

    我们也可以简化为:

    df2 = df['Text'].str.findall(r'\b(?!.*[A-Za-z]+.*\d+)(?!.*\d+.*[A-Za-z]+.*).+\b').str.join(' ').str.strip()
    

    说明

    这里我们使用的正则表达式还是要遵守排除字母数字词的要求。正则表达式:

    r'\b(?!.*[A-Za-z]+.*\d+)(?!.*\d+.*[A-Za-z]+.*).+\b'
    

    在单词边界 \b .... \b 内,我们使用 2 个否定前瞻来检查 字母和数字字符。我们需要 2 个负前瞻而不是 1 个,因为 alpha 可能出现在数字之前,反之亦然。

    【讨论】:

    • & 是二元与运算符;建议将and 用于“布尔和”。请参阅this question
    • @sophros 不幸的是,您的参考链接适用于 Python,而不是 Pandas。对于 Pandas 布尔掩码,我们需要 & 而不是 and。请看this answer
    • ...有趣。你每天都会学到一些东西...感谢您的指点!
    • @PushpaGadde 很高兴为您提供帮助!如果需要进一步澄清,请告诉我。
    【解决方案3】:

    您可以使用正则表达式来确保快速而优雅的解决方案:

    df2 = df['Text'].str.findall(r'((?<=\s)[a-zA-Z,]+(?=\s|$))').agg(' '.join)
    

    让我们分解一下:

    1. Regular expression 只提取不带数字的单词。
    2. 根据df['Text'] 的每个值提取正则表达式的匹配列表。
    3. 使用' '.join 函数聚合每个列表,该函数将列表中的值连接起来,并在中间添加空格。

    正则表达式正在这样做:

    • 要仅捕获位于字符串开头/结尾的“单词”,必须使用非捕获lookbehind 和lookaheads(分别在字母捕获组之前和之后)。
    • lookahead 也将停止在字符串的末尾(而不是任何白色字符)。
    • “单词”中接受的字符定义为[a-zA-Z,],它允许字母小写和大写以及逗号。

    性能

    与@SeaBean 解决方案相比,我的机器上的时间差异是显着的(每 200 万条记录数据帧):

    • 我的:6.6522 秒
    • SeaBean 的:25.1773 秒(慢 3.79 倍)

    与 SeaBean 相比,我的解决方案对内存的影响也较小,因为他正在创建额外的临时数据帧。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-09
      • 2018-10-25
      • 1970-01-01
      • 2016-10-21
      • 2023-01-11
      • 2013-04-30
      相关资源
      最近更新 更多