如何在 Python 中并行逐行读取两个文件?

3
我一整天都在尝试解决这个问题,但没有成功。
我有一个名为“infile”的“原始文件”,它是我想要编辑的文件。此外,我还有另一个作为“字典”的文件,让我们称其为“inlist”。
以下是“infile”的示例:
PRMT6   10505   Q96LA8  HMGA1   02829   NP_665906
WDR77   14387   NP_077007   SNRPE   00548   NP_003085
NCOA3   03570   NP_858045   RELA    01241   NP_068810
ITCH    07565   Q96J02  DTX1    03991   NP_004407

并且在列表中:

NP_060607   Q96LA8
NP_001244066    Q96J02
NP_077007   Q9BQA1
NP_858045   Q9Y6Q9

我的当前方法是按照各自的列拆分行,通过现有制表符拆分行。目标是读取输入文件的每一行并检查一些内容:
  1. 如果输入文件的第3列中的元素在inlist的第1列中找到,则将该元素更改为inlist第2列中的相应元素
  2. 如果输入文件的第3列中的元素在inlist的第2列中找到,则不进行任何操作
  3. 对于输入文件的第5列也是同样的操作
这将检索到以下输出:
PRMT6   10505   Q96LA8  HMGA1   02829   Q(...)
WDR77   14387   Q9BQA1  SNRPE   00548   Q(...)
NCOA3   03570   Q9Y6Q9  RELA    01241   Q(...)
ITCH    07565   Q96J02  DTX1    03991   Q(...)

注意:并非所有的代码都以 Q 开头。

我试过使用 while 循环,但没有成功。由于我刚入门编程,还不想在这么早的时候就失去动力,所以我感到很羞愧,并不愿意在此处发表代码。解决这个问题的完美方法是:

for line in inlist #, infile: <--- THIS PART! Reading both files, splitting both files, replacing both files...
        inlistcolumns = line.split('\t')
        infilecolumns = line.split('\t')
        if inlistcolumns[0] in infilecolumns[2]:
            outfile.write(str(infilecolumns[0]) + "\t" + str(infilecolumns[1]) + "\t" + str(inlistcolumns[1]) + "\t" + str(infilecolumns[3]) + "\t" + str(infilecolumns[4]) + "\t" + str(infilecolumns[5]) + "\n")
        elif inlistcolumns[0] in infilecolumns[5]:
            outfile.write(str(infilecolumns[0]) + "\t" + str(infilecolumns[1]) + "\t" + str(infilecolumns[2]) + "\t" + str(infilecolumns[3]) + "\t" + str(infilecolumns[4]) + "\t" + str(inlistcolumns[1]) + "\n")
        else:
            outfile.write('\t'.join(infilecolumns) + '\n')

希望能得到您的帮助,谢谢!

好的,在 Sephallia 和 Jlengrand 的提示下,我明白了:

for line in infile:
    try:
    # Read lines in the dictionary
        line2 = inlist.readline()
        inlistcolumns = line.split('\t')
        infilecolumns = line.split('\t')
        if inlistcolumns[0] in infilecolumns[2]:
            outfile.write(str(infilecolumns[0]) + "\t" + str(infilecolumns[1]) + "\t" + str(inlistcolumns[1]) + "\t" + str(infilecolumns[3]) + "\t" + str(infilecolumns[4]) + "\t" + str(infilecolumns[5]))
        elif inlistcolumns[0] in infilecolumns[5]:
                outfile.write(str(infilecolumns[0]) + "\t" + str(infilecolumns[1]) + "\t" + str(infilecolumns[2]) + "\t" + str(infilecolumns[3]) + "\t" + str(infilecolumns[4]) + "\t" + str(inlistcolumns[1]))
        else:
                    outfile.write('\t'.join(infilecolumns))
    except IndexError:
        print "End of dictionary reached. Restarting from top."

问题在于,似乎if语句没有起到作用,因为输出文件与输入文件相同。我可能做错了什么?
编辑2:
如一些人所请求的那样,这是完整的代码:
    import os

def replace(infilename, linename, outfilename):
    # Open original file and output file
    infile = open(infilename, 'rt')
    inlist = open(linename, 'rt')
    outfile = open(outfilename, 'wt')

    # Read lines and find those to be replaced
    for line in infile:
        infilecolumns = line.split('\t')
        line2 = inlist.readline()
        inlistcolumns = line2.split('\t')
        if inlistcolumns[0] in infilecolumns[2]:
            outfile.write(str(infilecolumns[0]) + "\t" + str(infilecolumns[1]) + "\t" + str(inlistcolumns[1]) + "\t" + str(infilecolumns[3]) + "\t" + str(infilecolumns[4]) + "\t" + str(infilecolumns[5]))
        elif inlistcolumns[0] in infilecolumns[5]:
            outfile.write(str(infilecolumns[0]) + "\t" + str(infilecolumns[1]) + "\t" + str(infilecolumns[2]) + "\t" + str(infilecolumns[3]) + "\t" + str(infilecolumns[4]) + "\t" + str(inlistcolumns[1]))
        outfile.write('\t'.join(infilecolumns))

    # Close files
    infile.close()
    inlist.close()
    outfile.close()


if __name__ == '__main__':
    wdir = os.getcwd()
    outdir = os.path.join(wdir, 'results.txt')
    outname = os.path.basename(outdir)
    original = raw_input("Type the name of the file to be parsed\n")
    inputlist = raw_input("Type the name of the libary to be used\n")
    linesdir = os.path.join(wdir, inputlist)
    linesname = os.path.basename(linesdir)
    indir = os.path.join(wdir, original)
    inname = os.path.basename(indir)

    replace(indir, linesdir, outdir)

    print "Successfully applied changes.\nOriginal: %s\nLibrary: %s\nOutput:%s" % (inname, linesname, outname)

第一个要使用的文件是hprdtotal.txt:https://www.dropbox.com/s/hohvlcdqvziewte/hprdmap.txt,第二个是hprdmap.txt:https://www.dropbox.com/s/9hd0e3a8rt95pao/hprdtotal.txt。希望这可以帮到您。

1
更多是思考而不是答案,为什么不用 for line1 in inlist 循环,然后有一个单独的变量,比如 line2,每次循环时从 infile 中获取下一行呢? - Sephallia
@hamed,问题在于我无法随意替换文本块。 - Edward Coelho
@Sephallia 我尝试了那个方法。 不幸的是,一个文件的结束比另一个早,所以我不能真的这么做,因为它会给我一个“超出范围错误”。 - Edward Coelho
@EdwardCoelho 嗯,你可以在 for 循环内部使用 try-catch 块。然后,当你捕获到异常时,你可以将较短的文件重置为起始位置。在这种情况下,你可能希望让 for 循环控制更长的循环。 - Sephallia
我尝试过,但最终if语句没有起作用。有任何想法吗? - Edward Coelho
显示剩余3条评论
5个回答

1

这样的东西不会简单地起作用吗?

(根据您的片段)

for line in infile: # read file 1 one line after the other
        try
            line2 = inlist.readline() # read a line of file 2
        catch Exception:
            print "End of file 2 reached"
        inlistcolumns = line.split('\t')
        infilecolumns = line.split('\t')
        if inlistcolumns[0] in infilecolumns[2]:
            outfile.write(str(infilecolumns[0]) + "\t" + str(infilecolumns[1]) + "\t" + str(inlistcolumns[1]) + "\t" + str(infilecolumns[3]) + "\t" + str(infilecolumns[4]) + "\t" + str(infilecolumns[5]) + "\n")
        elif inlistcolumns[0] in infilecolumns[5]:
            outfile.write(str(infilecolumns[0]) + "\t" + str(infilecolumns[1]) + "\t" + str(infilecolumns[2]) + "\t" + str(infilecolumns[3]) + "\t" + str(infilecolumns[4]) + "\t" + str(inlistcolumns[1]) + "\n")
        else:
            outfile.write('\t'.join(infilecolumns) + '\n')

我真的不明白为什么不先将文件保存在内存中,然后进行简单的模式匹配。你有没有合适的理由同时读取两个文件?(文件1的第45行是否与文件2的第45行匹配?)


我尝试过类似的方法,但这正是我已经告诉Sephallia的:一个文件比另一个文件结束得早,导致索引错误(超出范围)。虽然他(或她)的最后一条评论是个好主意。 无论如何,感谢您的反馈! (顺便说一句,我不是那个给你投反对票的人) - Edward Coelho
我已经回复了,但他还没有清晰地回答。 - jlengrand
好的,很高兴能帮助你;我正在使用字典的一个版本上工作 ^^ - jlengrand
@Edward 我可以提供一些帮助,但我不太明白你想做什么,所以很难回答 :s - jlengrand
@jlengrand 你有什么想法吗?我还在努力解决这个问题,但是找不到解决方案。 - Edward Coelho
显示剩余9条评论

1
你需要做的第一步是将inlist文件读入内存,以便进行检查。
initems = []
for line in inlist:
    split = line.split()
    t = tuple(split[0], split[1])
    initems.append(t)
firstItems = dict(initems)
secondItems = [x[1] for x in initems]

这将为您提供可与之匹配的数据。然后打开您的输入文件并阅读它,检查与您的数据相符的内容。

for line in infile:
    split = line.split('\t')
    if split[2] in firstItems.keys():
        split[2] = firstItems[split[2]] # proper field position
    if split[5] in firstItems.keys():
        split[5] = firstItems[split[5]] # proper field position
    outfile.write('\t'.join(split)+'\n')

我明白你的想法,但这样会不会占用大量内存? - Edward Coelho
@EdwardCoelho 你的输入文件有多大?这将使用与文件大小相同的内存,查找将快速运行,一旦程序退出,它将从内存中删除。 - Spencer Rathbun
文件并不是太大,但在您建议的修改后,它执行与我上面所做的编辑相同,需要更多时间。我并不是说这是一种错误的方法,因为它完全合理,我真的明白,但看起来问题出在if语句中。而且那应该是我犯了错误。 - Edward Coelho
@EdwardCoelho 噢,糟糕!我再次查看了您的原始问题,似乎我错过了键周围的“-”。比较是精确的,因此它永远不会在第一个if中找到任何内容。我已使用相关修复程序更新了我的答案。哎呀,那也不对。 - Spencer Rathbun
不用担心周围的' - '。那些只是强调我不想改变的ID。也许我会把它们去掉,因为它们造成了普遍的混乱。 - Edward Coelho
@EdwardCoelho 好的,我觉得这次编辑的效果要好得多。你希望检查两个字段,并在有需要时进行调整。然后将结果输出。你可能需要针对破折号进行一些调整。 - Spencer Rathbun

1
我建议将inlist作为查找表加载到内存中 - 在Python中是一个dict,然后循环遍历infile并使用查找表来决定是否要进行替换。
我不确定我是否完全理解了你的逻辑,但这是你可以构建的基础。
import csv

lookup = {}
uniq2nd = set()
with open('inlist') as f:
    tabin = csv.reader(f, delimiter='\t')
    for c1, c2 in tabin:
        lookup[c1] = c2
        uniq2nd.add(c2)

with open('infile') as f, open('outfile', 'wb') as fout:
    tabin = csv.reader(f, delimiter='\t')
    tabout = csv.writer(fout, delimiter='\t')
    for row in csv.reader(tabin):
        if row[2] not in uniq2nd: # do nothing if col2 of inlist
            row[2] = lookup.get(row[2], row[2]) # replace or keep same
        # etc...
    csvout.writerow(row)

我从未使用过csv模块,而且作为一个新手程序员,这对我来说有点“中文”xD。 - Edward Coelho
1
@EdwardCoelho 这只是一种更智能的处理分隔文件的方式,其字段分隔符可能在字符串分隔符内(例如CSV格式)。非常容易使用,虽然对于制表符分隔的 .split('\t' 来说是合理的,但对于CSV文件来说,它只会引起麻烦... 值得一看 :) - Jon Clements
很酷,当我感到自信时我会尝试的;D 谢谢提示! - Edward Coelho

1
#!/usr/bin/python

inFile = open("file1.txt")
inList = open("file2.txt")
oFile = open("output.txt", "w")

entry = {}
dictionary = {}

# Creates the dict for inFile
for line in inFile:
    lineData = line.split('\t')
    data = []
    for element in lineData:
        element = element.strip()
        data.append(element)
    entry[lineData[0]] = data

# Creates the dict for inList
for line in inList:
    lineData = line.split('\t')
    dictionary[lineData[0].strip()] = lineData[1].strip()


# Applies transformation to inFile
for item in entry.values():
    if item[2].startswith("-"):
        item[2] = item[2][1:-1]
    for key in dictionary.items():
        if item[2] == key[0]:
            item[2] = key[1]        
    item[5] = item[2]

# Writes the output file
for item in entry.values():
    for element in item:
        oFile.write(str(element))
        oFile.write('\t')
    oFile.write('\n')

作为注意事项,请确保使用正确的分隔符适当地格式化您的 inFile 和 inList。在这种情况下,我使用制表符(\t)来拆分行。

你提到了“-”分隔符,但没有进一步的说明,所以我只是将它们删除了。如果你希望对这些条目进行特定的处理,请告诉我。 - wtfomgjohnny
哦,我只是使用“-”来指向输入文件中的那些特定值。我想确保人们注意到它们 ^^' - Edward Coelho
你能具体一点吗?它在哪里出错了? - wtfomgjohnny

0

好的,我找到了。 这是我所做的:

data = {}
    for line in inlist:
        k, v = [x.strip() for x in line.split('\t')]
        data[k] = v

    for line in infile:
        infilecolumns = line.strip().split('\t')

        value1 = data.get(infilecolumns[2])
        value2 = data.get(infilecolumns[5])

        if value1:
            infilecolumns[2] = value1
        if value2:
            infilecolumns[5] = value2

        outfile.write('\t'.join(infilecolumns) + '\n')

这样做可以轻松地得到所需的输出。 感谢您所有的答案,对我帮助很大!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接