【问题标题】:Read data from text format into Python Pandas dataframe将文本格式的数据读入 Python Pandas 数据框
【发布时间】:2016-02-02 16:38:17
【问题描述】:

我在 Windows 上运行 Python 2.7。

我有一个包含 500K+ 电子邮件的大文本文件 (2 GB)。该文件没有明确的文件类型,格式为:

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) 收到电子邮件的每个人的公司

在文本文件中有超过 50 万封电子邮件,并且电子邮件最多可以有 16,000 个收件人。电子邮件中提到的人名或他们工作的公司的方式没有规律。

我想获取这个大文件并在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

【问题讨论】:

  • @whoever 否决了这个问题。你能简要解释一下你为什么投反对票吗?在这个问题上,即使是正确的方向,我也会很感激
  • 对不起@BeeGee,我在做的时候分心了。这将有助于了解您的努力以及您实际在哪里挣扎。否则,唯一可能的答案是为您编写解析器,我认为这不是重点。您只是在编写一些要求并要求提供代码。除此之外,这个问题很清楚并且写得很好,我愿意提供帮助。
  • @Goyo 感谢您帮助我解决我的问题。我将编写自己的解析器,如果确实没有模块,我会发布我的解决方案
  • 不,如果您正在为该工作寻找一个单一的衬垫,我认为至少在 python 标准库或熊猫中没有。

标签: python python-2.7 pandas dataframe datasource


【解决方案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.

但同样,您不必这样做就可以对您的数据使用该功能。只需使用文件路径调用它即可。

【讨论】:

  • 我理解这种痒。这适用于我的示例,所以我给了你赞成票,但手动设置文本和列不能很好地适应我的解决方案。
  • 恐怕我的观点不够清楚。我编辑了我的答案以显示您应该如何使用它。当然,您不希望将文件的内容复制到字符串中,这只是为了测试。在以前的版本中,您必须打开文件并将文件对象传递给函数。不,我为您编写了一个函数,因此您只需传递文件名。
【解决方案2】:

我不知道做到这一点的绝对最佳方法。你当然不会忽视一个明显的单线,这可能会让你放心。

看起来您当前的解析器(称为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 非常擅长将其余的字符串按您想要的方式处理。

【讨论】:

    猜你喜欢
    • 2017-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-05
    • 2019-09-01
    • 1970-01-01
    • 2018-03-23
    • 2020-08-27
    相关资源
    最近更新 更多