我正在使用正则表达式和熊猫来读取文件中的文本行,并有选择地将数据拉入数据帧。
假设我有以下文本行
Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25"
我想将所有这些信息拉入数据帧,使其如下所示:
Name Occupation Age
Bob Builder 42
我想忽略阅读有关第二个人的任何信息,因为他们的职业是空白的。
法典:
with open(txt, 'r') as txt
for line in txt:
line = line.strip
a = re.findall(r'Name : "(S+)"', line)
if a:
b = re.findall(r'Occupation : "(S+)"', line)
if b:
c = re.findall(r'Age : "(S+)"', line)
if c:
df = df.append({'Name' : a, 'Occupation' : b, 'Age' : c}, ignore_index = True)
这将返回以下(不正确的)数据帧
Name Occupation Age
["Bob", "Jim"] ["Builder"] ["42","25"]
我想修改此代码,使其永远不会包含"Jim"所处的情况,即如果此人没有"职业",则不要将他们的信息读取到数据帧中。您还可以看到此代码不正确,因为它现在说"Jim"的职业为"Builder"。
如果给我以下几行文字:
Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25" Name : "Steve" Occupation : "Clerk" Age : "110"
生成的 df 将是:
Name Occupation Age
["Bob", "Steve"] ["Builder", "Clerk"] ["42","110"]
这很方便,因为我不会再遇到任何索引问题,所以我可以将这个 df 扩展到我的最终目标(知道如何做):
Name Occupation Age
Bob Builder 42
Steve Clerk 110
根据您的评论,Name
、Occupation
和Age
的 3 个键始终处于相同的顺序,因此我们可以使用单个正则表达式模式来检索字段值,同时确保匹配的值为非空。下面是一个使用 Series.str.extractall() 的示例:
# example texts copied from your post
str="""
Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25" Name : "Steve" Occupation : "Clerk" Age : "110"
Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25"
"""
# read all lines into one field dataframe with column name as 'text'
df = pd.read_csv(pd.io.common.StringIO(str), squeeze=True, header=None).to_frame('text')
# 3 fields which have the same regex sub-pattern
fields = ['Name', 'Occupation', 'Age']
# regex pattern used to retrieve values of the above fields. There are 3 sub-patterns
# corresponding to the above 3 fields and joined by at least one white spaces(s+)
ptn = r's+'.join([ r'{0}s*:s*"(?P<{0}>[^"]+)"'.format(f) for f in fields ])
print(ptn)
#Names*:s*"(?P<Name>[^"]+)"s+Occupations*:s*"(?P<Occupation>[^"]+)"s+Ages*:s*"(?P<Age>[^"]+)"
哪里:
- 子模式
Names*:s*"(?P<Name>[^"]+)"
基本上与Name : "([^"]+)"
相同,但冒号:
周围有0到更多空白区域和命名捕获组。 "([^"]+)"
中+
加号字符是为了确保用双引号括起来的值不为空,因此将跳过 Jim 的个人资料,因为他的职业是空的。- 使用命名捕获组,以便我们可以在运行 Series.str.extractall() 后拥有正确的列名,否则生成的列名将默认为
0
、1
和2
。
然后你可以从 Series.str.extractall() 检查结果:
df['text'].str.extractall(ptn)
Name Occupation Age
match
0 0 Bob Builder 42
1 Steve Clerk 110
1 0 Bob Builder 42
删除 1 级索引,您将获得一个包含原始索引的数据帧。如果任务中使用了其他列,则可以将其联接回原始数据帧。
df['text'].str.extractall(ptn).reset_index(level=1, drop=True)
###
Name Occupation Age
0 Bob Builder 42
0 Steve Clerk 110
1 Bob Builder 42
使用正则表达式 -->re.finditer
与正则表达式分组。
前任:
import re
import pandas as pd
s = 'Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25"'
name = re.findall(r'Name : "(.*)" ', s)
occupation = re.findall(r'Occupation : "(.*)" ', s)
age = re.findall(r'Age : "(.*)" ', s)
regexPattern = re.compile(r'Name : "(?P<name>.*?)"s+Occupation : "(?P<occupation>.*?)"s+Age : "(?P<age>.*?)"')
df = pd.DataFrame([i.groupdict() for i in regexPattern.finditer(s) if len(filter(None, i.groupdict().values())) == 3])
print(df)
输出:
age name occupation
0 42 Bob Builder
你说这些字符串有一个固定的格式,Name
先出现,Occupation
跟随,然后Age
。您可以使用
df = pd.DataFrame()
pat = r'Names*:s*"([^"]+)"s*Occupations*:s*"([^"]+)"s*Ages*:s*"(d+)"'
s='Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25" Name : "Steve" Occupation : "Clerk" Age : "110"'
for name, occupation, age in re.findall(pat, s):
df = df.append({'Name' : name, 'Occupation' : occupation, 'Age' : age}, ignore_index = True)
输出:
>>> df
Age Name Occupation
0 42 Bob Builder
1 110 Steve Clerk
正则表达式是
Names*:s*"([^"]+)"s*Occupations*:s*"([^"]+)"s*Ages*:s*"(d+)"
请参阅正则表达式演示。由于捕获组中的量词设置为+
(一次或多次出现),因此这些值永远不会为空。为避免前两个中的空值,您可以更改模式Names*:s*"([^"]*[^s"][^"]*)"s*Occupations*:s*"([^"]*[^s"][^"]*)"s*Ages*:s*"(d+)"
,请参阅此演示。
详
Name
-Name
s*:s*
-:
用 0+ 空格括起来"
- 双引号([^"]+)
- 第 1 组:除"
以外的一个或多个字符"
- 双引号s*
- 0+ 空格Occupations*:s*"
([^"]+)
- 第 2 组:除"
以外的一个或多个字符Age
、:
用 0+ 空格括起来,然后"
(d+)
- 第 3 组:一个或多个数字"
- 双引号
"s*Ages*:s*"
-"
、0+ 空格、