有效处理文本文件

时间:2015-12-04 11:23:58

标签: python file

假设我有一个(文本)文件,其结构如下(名称,分数):

 a         0
 a         1
 b         0
 c         0
 d         3
 b         2

等等。我的目标是将每个名字的分数相加,并从最高分到最低分排序。所以在这种情况下,我想要以下输出:

 d         3
 b         2
 a         1
 c         0

事先我不知道文件中会有什么名字。

我想知道是否有一种有效的方法可以做到这一点。我的文本文件最多可包含50,000个条目。

我能想到的唯一方法就是从第1行开始,记住该名称,然后查看整个文件以查找该名称和总和。这看起来非常低效,所以我想知道是否有更好的方法来做到这一点。

3 个答案:

答案 0 :(得分:12)

将所有数据读入字典:

from collections import defaultdict
from operator import itemgetter

scores = defaultdict(int)
with open('my_file.txt') as fobj:
    for line in fobj:
        name, score = line.split()
        scores[name] += int(score)

和排序:

for name, score in sorted(scores.items(), key=itemgetter(1), reverse=True):
    print(name, score)

打印:

d 3
b 2
a 1
c 0

性能

为了检查这个答案与@SvenMarnach的答案的表现,我将两种方法都放入一个函数中。这里fobj是一个打开阅读的文件。 我使用io.StringIO因此有希望无法测量IO延迟:

from collections import Counter

def counter(fobj):
    scores = Counter()
    fobj.seek(0)
    for line in fobj:
        key, score = line.split()
        scores.update({key: int(score)})
    return scores.most_common()

from collections import defaultdict
from operator import itemgetter

def default(fobj):
    scores = defaultdict(int)
    fobj.seek(0)
    for line in fobj:
        name, score = line.split()
        scores[name] += int(score)
    return sorted(scores.items(), key=itemgetter(1), reverse=True)

collections.Counter的结果:

%timeit counter(fobj)
10000 loops, best of 3: 59.1 µs per loop

collections.defaultdict的结果:

%timeit default(fobj)
10000 loops, best of 3: 15.8 µs per loop

看起来defaultdict的速度提高了四倍。我不会猜到这一点。但是在性能方面,您需要来衡量。

答案 1 :(得分:7)

这是collections.Counter的一个很好的用例:

from collections import Counter

scores = Counter()
with open('my_file') as f:
    for line in f:
        key, score = line.split()
        scores.update({key: int(score)})

for key, score in scores.most_common():
    print(key, score)

答案 2 :(得分:0)

熊猫可以相当容易地做到这一点:

import pandas as pd
data = pd.read_csv('filename.txt', names=['Name','Score'])
sorted = data.groupby('Name').sum().sort_values('Score', ascending=False)
print sorted