我正在使用正则表达式和熊猫来读取文件中的文本行,并有选择地将数据拉入数据框。
说我有以下文本行
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
答案 0 :(得分:2)
根据您的评论,三个键Name
,Occupation
和Age
总是相同的顺序,因此我们可以使用单个正则表达式模式来检索字段值,同时确保匹配的值是非空的。以下是使用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)
#Name\s*:\s*"(?P<Name>[^"]+)"\s+Occupation\s*:\s*"(?P<Occupation>[^"]+)"\s+Age\s*:\s*"(?P<Age>[^"]+)"
位置:
Name\s*:\s*"(?P<Name>[^"]+)"
与 Name : "([^"]+)"
基本相同,但是可选地,可以使用 0 来实现更多冒号:
和命名的捕获组周围的空白。 +
中的加号 "([^"]+)"
是为了确保用双引号引起来的值不是EMPTY,因此将跳过Jim的个人资料,因为他的职业是空的。 0
,1
和{{1} }。然后您可以从Series.str.extractall()检查结果:
2
删除1级索引,您将获得一个具有原始索引的数据框。如果您的任务中使用了其他列,则可以将其重新连接到原始数据框。
df['text'].str.extractall(ptn)
Name Occupation Age
match
0 0 Bob Builder 42
1 Steve Clerk 110
1 0 Bob Builder 42
答案 1 :(得分:0)
使用正则表达式-> 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
答案 2 :(得分:0)
您说这些字符串具有固定格式,首先是Name
,然后是Occupation
,然后是Age
。您可以使用
df = pd.DataFrame()
pat = r'Name\s*:\s*"([^"]+)"\s*Occupation\s*:\s*"([^"]+)"\s*Age\s*:\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
正则表达式为
Name\s*:\s*"([^"]+)"\s*Occupation\s*:\s*"([^"]+)"\s*Age\s*:\s*"(\d+)"
请参见regex demo。由于捕获组中的量词设置为+
(一次或多次出现),因此这些值永远不会为空。为避免前两个值都是空值,可以将模式更改为Name\s*:\s*"([^"]*[^\s"][^"]*)"\s*Occupation\s*:\s*"([^"]*[^\s"][^"]*)"\s*Age\s*:\s*"(\d+)"
,请参见this demo。
详细信息
Name
-Name
\s*:\s*
-:
内含0+空格"
-双引号([^"]+)
-第1组:除"
之外的一个或多个字符"
-双引号\s*
-超过0个空格Occupation\s*:\s*"
([^"]+)
-第2组:除"
之外的一个或多个字符"\s*Age\s*:\s*"
-"
,0+空格,Age
,:
,并用0+空格包围,然后用"
(\d+)
-第3组:一个或多个数字"
-双引号