使用pandas从字符串中删除所有字母数字单词。

3

我有一个包含字符串的pandas数据框列,看起来像这样:

'2fvRE-Ku89lkRVJ44QQFN ABACUS LABS, 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' object has no attribute 'isdigit'

有人能帮帮我如何从字符串中删除字母数字并仅保留pandas数据框列中的文本部分吗?


请同时分享一部分数据。抱歉,您没有提供要翻译的具体内容。请提供需要翻译的文本,我会尽快为您完成翻译。 - suraj deshmukh
我用简化版修改了我的解决方案,仍然遵循您的测试逻辑,不包含单词中的字母和数字。 - SeaBean
3个回答

3

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

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

让我们来分解一下:
  1. 正则表达式,用于仅选择没有数字的单词。
  2. 对于每个df ['Text']的值,提取正则表达式匹配的列表。
  3. 使用' '.join函数聚合每个列表,将列表中的值连接在一起,并在它们之间添加空格。

这个正则表达式的作用是:

  • 要捕获字符串开头/结尾处的“单词”,必须使用非捕获回溯和前后查找(分别在字母捕获组之前和之后)。
  • 前瞻会在字符串末尾停止(而不是任何白色字符)。
  • 定义为[a-zA-Z,]的字符允许小写和大写字母以及逗号等“单词”。

性能

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

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

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


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测试的布尔掩码,我们仅选择不包含同时包含字母和数字的行条目:
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内,我们使用两个负向先行断言来检查同时包含字母和数字字符。我们需要两个负向先行断言而不是一个,因为字母可能出现在数字之前或者反过来。

& 是一个二进制与运算符;建议使用 and 来表示布尔值的与运算。请参考这个问题 - sophros
@sophros 不幸的是,您提供的参考链接是 Python 的,而不是 Pandas 的。对于 Pandas 布尔掩码,我们需要使用 & 而不是 and。请参见此答案 - SeaBean
有趣啊。每天都可以学到新东西……感谢您的指引! - sophros
1
@PushpaGadde 很高兴能够帮忙!如果需要进一步的澄清,请告诉我。 - SeaBean

2

您可以使用

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

请参见正则表达式演示

正则表达式细节

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接