我在Windows上运行Python 2.7。
我有一个大文本文件(2 GB),引用500K +电子邮件。该文件没有明确的文件类型,格式为:
email_message#: 1
email_message_sent: 10/10/1991 02:31:01
From: tomf@abc.com| Tom Foo |abc company|
To: adee@abc.com| Alex Dee |abc company|
To: benfor12@xyz.com| Ben For |xyz company|
email_message#: 2
email_message_sent: 10/12/1991 01:28:12
From: timt@abc.com| Tim Tee |abc company|
To: tomf@abc.com| Tom Foo |abc company|
To: adee@abc.com| Alex Dee |abc company|
To: benfor12@xyz.com| Ben For|xyz company|
email_message#: 3
email_message_sent: 10/13/1991 12:01:16
From: benfor12@xyz.com| Ben For |xyz company|
To: tomfoo@abc.com| Tom Foo |abc company|
To: t212@123.com| Tatiana Xocarsky |numbers firm |
...
如您所见,每封电子邮件都包含以下数据:
1)发送时间
2)发送邮件的电子邮件地址
3)发送者的姓名
4)该人工作的公司
5)收到电子邮件的每个电子邮件地址
6)收到电子邮件的每个人的姓名
7)收到电子邮件的每个人的公司在文本文件中有500K +电子邮件,电子邮件最多可以有16K收件人。电子邮件中没有关于他们如何引用人员名称或他们所在公司的模式。
我想接受这个大文件并在python
中对其进行操作,以便它最终成为Pandas
Dataframe
。我希望pandas
dataframe
的格式类似于以下excel
的屏幕截图:
修改
我解决这个问题的计划是写一个"解析器"获取此文本文件并读取每一行,将每行中的文本分配给pandas
dataframe
的特定列。
我打算写下面的内容。有人可以确认这是执行此操作的正确方法吗?我想确保我没有错过来自不同pandas
的内置module
函数或函数。
#connect to object
data = open('.../Emails', 'r')
#build empty dataframe
import pandas as pd
df = pd.DataFrame()
#function to read lines of the object and put pieces of text into the
# correct column of the dataframe
for line in data:
n = data.readline()
if n.startswith("email_message#:"):
#put a slice of the text into a dataframe
elif n.startswith("email_message_sent:"):
#put a slice of the text into a dataframe
elif n.startswith("From:"):
#put slices of the text into a dataframe
elif n.startswith("To:"):
#put slices of the text into a dataframe
答案 0 :(得分:1)
我不知道绝对最好的方法。你肯定不会忽视一个明显的单行,这可能会让你放心。
看起来您当前的解析器(称为my_parse
)执行所有处理。在伪代码中:
finished_df = my_parse(original_text_file)
然而,对于如此大的文件,这有点像使用镊子飓风后的清理。两阶段解决方案可能更快,您首先将文件粗略地整理到您想要的结构中,然后使用pandas系列操作来优化其余部分。继续伪代码,您可以执行以下操作:
rough_df = rough_parse(original_text_file)
finished_df = refine(rough_df)
rough_parse
使用Python标准库的内容,refine
使用pandas系列操作,尤其是Series.str methods。
我建议rough_parse
的主要目标只是实现一个电子邮件 - 一行结构。所以基本上你会通过某种独特的分隔符替换所有换行符,这些分隔符在"$%$%$"
等文件中没有出现,除非换行符后的下一个内容是"email_message#:"
然后,Series.str真的很擅长将你想要的其他字符串拼凑起来。
答案 1 :(得分:1)
我无法抗拒痒,所以这是我的方法。
from __future__ import unicode_literals
import io
import pandas as pd
from pandas.compat import string_types
def iter_fields(buf):
for l in buf:
yield l.rstrip('\n\r').split(':', 1)
def iter_messages(buf):
it = iter_fields(buf)
k, v = next(it)
while True:
n = int(v)
_, v = next(it)
date = pd.Timestamp(v)
_, v = next(it)
from_add, from_name, from_comp = v.split('|')[:-1]
k, v = next(it)
to = []
while k == 'To':
to_add, to_name, to_comp = v.split('|')[:-1]
yield (n, date, from_add[1:], from_name[1:-1], from_comp,
to_add[1:], to_name[1:-1], to_comp)
k, v = next(it)
if not hasattr(filepath_or_buffer, read):
filepath_or_buffer
def _read_email_headers(buf):
columns=['email_message#', 'email_message_sent',
'from_address', 'from_name', 'from_company',
'to_address', 'to_name', 'to_company']
return pd.DataFrame(iter_messages(buf), columns=columns)
def read_email_headers(path_or_buf):
close_buf = False
if isinstance(path_or_buf, string_types):
path_or_buf = io.open(path_or_buf)
close_buf = True
try:
return _read_email_headers(path_or_buf)
finally:
if close_buf:
path_or_buf.close
这就是你如何使用它:
df = read_email_headers('.../data_file')
只需使用您文件的路径调用它,即可获得数据框。
现在,以下内容仅供测试之用。你不会这样做来处理现实生活中的实际数据。
由于我(或随机的StackOverflow阅读器)没有文件的副本,我必须使用字符串伪造它:
text = '''email_message#: 1
email_message_sent: 10/10/1991 02:31:01
From: tomf@abc.com| Tom Foo |abc company|
To: adee@abc.com| Alex Dee |abc company|
To: benfor12@xyz.com| Ben For |xyz company|
email_message#: 2
email_message_sent: 10/12/1991 01:28:12
From: timt@abc.com| Tim Tee |abc company|
To: tomf@abc.com| Tom Foo |abc company|
To: adee@abc.com| Alex Dee |abc company|
To: benfor12@xyz.com| Ben For|xyz company|'''
然后我可以创建一个类似文件的对象并将其传递给函数:
df = read_email_headers(io.StringIO(text))
print(df.to_string())
email_message# email_message_sent from_address from_name from_company to_address to_name to_company
0 1 1991-10-10 02:31:01 tomf@abc.com Tom Foo abc company adee@abc.com Alex Dee abc company
1 1 1991-10-10 02:31:01 tomf@abc.com Tom Foo abc company benfor12@xyz.com Ben For xyz company
2 2 1991-10-12 01:28:12 timt@abc.com Tim Tee abc company tomf@abc.com Tom Foo abc company
3 2 1991-10-12 01:28:12 timt@abc.com Tim Tee abc company adee@abc.com Alex Dee abc company
4 2 1991-10-12 01:28:12 timt@abc.com Tim Tee abc company benfor12@xyz.com Ben Fo xyz company
或者,如果我想使用实际文件:
with io.open('test_file.txt', 'w') as f:
f.write(text)
df = read_email_headers('test_file.txt')
print(df.to_string()) # Same output as before.
但是,再次你不必这样做就可以将这个功能与你的数据一起使用。只需用文件路径调用它即可。