【问题标题】:Opening and closing files in a loop循环打开和关闭文件
【发布时间】:2016-01-13 21:26:15
【问题描述】:

假设我有一个包含数万个条目的列表,我想将它们写入文件。如果列表中的项目符合某些条件,我想关闭当前文件并开始一个新文件。

我有几个问题,我认为它们源于我想根据该文件中的第一个条目命名文件。此外,启动新文件的信号取决于条目是否具有与前一个相同的字段。因此,例如,假设我有一个列表:

l = [('name1', 10), ('name1', 30), ('name2', 5), ('name2', 7), ('name2', 3), ('name3', 10)]

我希望得到 3 个文件,name1.txt 应该包含 1030name2.txt 应该有 573name3.txt 应该有10。该列表已经按第一个元素排序,所以我需要做的就是检查第一个元素是否与前一个元素相同,如果不是,则启动一个新文件。

一开始我试过:

name = None
for entry in l:
    if entry[0] != name:
        out_file.close()
        name = entry[0]
        out_file = open("{}.txt".format(name))
        out_file.write("{}\n".format(entry[1]))
    else:
        out_file.write("{}\n".format(entry[1]))

out_file.close()

据我所知,这有几个问题。首先,第一次通过循环,没有out_file 可以关闭。其次,我无法关闭最后创建的out_file,因为它是在循环中定义的。以下解决了第一个问题,但看起来很笨拙:

for entry in l:
    if name:
        if entry[0] != name:
            out_file.close()
            name = entry[0]
            out_file = open("{}.txt".format(name))
            out_file.write("{}\n".format(entry[1]))
        else:
            out_file.write("{}\n".format(entry[1]))
    else:
        name = entry[0]
        out_file = open("{}.txt".format(name))
        out_file.write("{}\n".format(entry[1]))

out_file.close()

有没有更好的方法来做到这一点?

而且,这似乎不能解决关闭最后一个文件的问题,尽管这段代码运行良好——我误解了out_file 的范围吗?我认为它会被限制在for 循环内。

编辑:我可能应该提到,我的数据比这里指出的要复杂得多......它实际上不在列表中,它是 SeqRecord from BioPython

编辑 2:好的,我以为我在简化是为了避免分心。显然有相反的效果 - mea culpa。以下是上面第二个代码块的等价物:

from re import sub
from Bio import SeqIO

def gbk_to_faa(some_genbank):
    source = None
    for record in SeqIO.parse(some_genbank, 'gb'):
        if source:
            if record.annotations['source'] != source:
                out_file.close()
                source = sub(r'\W+', "_", sub(r'\W$', "", record.annotations['source']))
                out_file = open("{}.faa".format(source), "a+")
                write_all_record(out_file, record)
            else:
                write_all_record(out_file, record)
        else:
            source = sub(r'\W+', "_", sub(r'\W$', "", record.annotations['source']))
            out_file = open("{}.faa".format(source), "a+")
            write_all_record(out_file, record)

    out_file.close()


def write_all_record(file_handle, gbk_record):
    # Does more stuff, I don't think this is important
    # If it is, it's in this gist: https://gist.github.com/kescobo/49ab9f4b08d8a2691a40

【问题讨论】:

  • 关于你的最后一个问题,out-file 是否会被限制在 for 循环中,请查看 Scoping in Python for loops
  • @JGreenwell - 好的,所以基本上我完全误解了 python 的范围。很高兴知道...

标签: python pandas biopython


【解决方案1】:

使用 Python 提供的工具更容易:

from itertools import groupby
from operator import itemgetter

items = [
    ('name1', 10), ('name1', 30),
    ('name2', 5), ('name2', 7), ('name2', 3),
    ('name3', 10)
]

for name, rows in groupby(items, itemgetter(0)):
    with open(name + ".txt", "w") as outf:
        outf.write("\n".join(str(row[1]) for row in rows))

编辑:以匹配更新后的问题,这是更新后的解决方案;-)

for name, records in groupby(SeqIO.parse(some_genbank, 'gb'), lambda record:record.annotations['source']):
    with open(name + ".faa", "w+") as outf:
        for record in records:
            write_all_record(outf, record)

【讨论】:

  • 对不起,我应该提到,我的数据比我指出的要复杂一些(见编辑)。不过,这可能很有用 - 必须看看我是否能弄清楚它是否兼容。
  • 此解决方案实际上并不依赖于数据的简单性。它适用于任何排序的序列。只需确保 groupby 的 key 函数与排序数据的 key 函数兼容。
  • @Robᵩ 我刚刚发布了实际代码。数据在我正在解析的文件中排序。所以我需要读入文件,解析,然后按照一定的标准进行拆分:record.annotations['source']record 是迭代器返回的对象,record.annotations 是字典(我认为)。我可以在 itemgetter 中使用点运算符吗?
  • 看来我可以使用operator.attrgetterpython Docs
  • @kevbonham:我认为你必须使用lambda record: record.annotations['source']
【解决方案2】:

如果您不介意使用pandas,您可以按如下方式处理:

import pandas as pd
df = pd.DataFrame(l)
df.columns = ['name', 'value']
df.groupby('name').apply(lambda x: x.to_csv('{}.txt'.format(x['name'].iloc[0]), index=False, header=False))

获取三个名为 name1.txt 等的文本文件,如下所示:

name1,10
name1,30

【讨论】:

  • 我的数据结构比我在这里指出的要复杂得多,此示例仅用于说明目的。我不认为加载到 DataFrame 会很好。
  • 那么,一个更好地说明手头案例的说明性示例怎么样? pandas 标签还暗示了似乎没有相关性的地方?
  • 呃……我的错。这是我上周开始问的一个问题,然后我自己想出来的。显然它被缓存了。
  • 那么您的数据是否看起来像您链接的页面中print record 的结果,但作为一个元组?发布一个样本怎么样?
  • 刚刚发布了有效的实际代码,这是否充分揭示了数据的结构,还是我应该发布其他内容?它是一个迭代器,包含多个内容,包括我需要从中获取特定项目的字典。
【解决方案3】:

不弄乱你的代码,为什么不在关闭前检查out_file变量是否存在?

out_file=None
... #Some code
if out_file:
    out_file.close()

您也可以为此使用try/except

或者甚至可能是一个班级(虽然有点矫枉过正):

class f_temp():
    name = None
    def close(self):
        pass
out_file = f_temp()

for entry in l:
    if entry[0] != out_file.name:

...

现在阅读更多,为什么不按文件名对数据进行排序,一次只打开一个文件?


您也可以为此使用字典:

file_dict =dict()
for filename, value in l:
    if filename not in file_dict():
        file_dict[filename] = open("{}.txt".format(filename))
    file_dict[filename].write("{}\n".format(entry[1]))

for item in file_dict.items():
    item.close()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多