读入大文件并制作字典

Anu*_*ush 8 c python performance cython pandas

我有一个大文件,我需要阅读并从中制作字典.我希望这个尽可能快.但是我在python中的代码太慢了.这是一个显示问题的最小示例.

首先制作一些假数据

paste <(seq 20000000) <(seq 2 20000001)  > largefile.txt
Run Code Online (Sandbox Code Playgroud)

现在这里有一段最小的python代码来读取它并制作一本字典.

import sys
from collections import defaultdict
fin = open(sys.argv[1])

dict = defaultdict(list)

for line in fin:
    parts = line.split()
    dict[parts[0]].append(parts[1])
Run Code Online (Sandbox Code Playgroud)

时序:

time ./read.py largefile.txt
real    0m55.746s
Run Code Online (Sandbox Code Playgroud)

但是它不是I/O绑定的:

time cut -f1 largefile.txt > /dev/null    
real    0m1.702s
Run Code Online (Sandbox Code Playgroud)

如果我注释掉这dict条线需要9几秒钟.似乎几乎所有的时间都花在了dict[parts[0]].append(parts[1]).

有什么方法可以加快速度吗?我不介意使用cython甚至C,如果这会产生很大的不同.或者熊猫可以在这帮忙吗?

以下是大小为10000000行的文件的配置文件输出.

python -m cProfile read.py test.data         20000009 function calls in 42.494 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 bisect.py:1(<module>)
        1    0.000    0.000    0.001    0.001 collections.py:1(<module>)
        1    0.000    0.000    0.000    0.000 collections.py:25(OrderedDict)
        1    0.000    0.000    0.000    0.000 collections.py:386(Counter)
        1    0.000    0.000    0.000    0.000 heapq.py:31(<module>)
        1    0.000    0.000    0.000    0.000 keyword.py:11(<module>)
        1   30.727   30.727   42.494   42.494 read.py:2(<module>)
 10000000    4.855    0.000    4.855    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
 10000000    6.912    0.000    6.912    0.000 {method 'split of 'str' objects}
        1    0.000    0.000    0.000    0.000 {open}
Run Code Online (Sandbox Code Playgroud)

更新. 我们可以假设parts [1]是一个整数,而parts [0]是一个短的固定长度字符串.

我的假数据不是很好,因为每个键只能获得一个值.这是一个更好的版本.

perl -E 'say int rand 1e7, $", int rand 1e4 for 1 .. 1e7' > largefile.txt
Run Code Online (Sandbox Code Playgroud)

我要做的唯一操作是查询一个键以返回与之关联的值列表.

Vik*_*kez 7

如果您想要在评论中说出的内容,那么您可以在熊猫中轻松完成:假设您有一个具有相同布局的文件,但条目会重复,因为在您的示例中,您将所有重复项添加到列表中:

1 1
2 2
1 3
3 4
1 5
5 6
Run Code Online (Sandbox Code Playgroud)

然后您可以读取和操作数据:

In [1]: df = pd.read_table('largefile.txt', header=None, index_col=0)
In [2]: df.loc[2]
Out[2]:
1    2
Name: 2, dtype: int64

In [3]: df.loc[1]
Out[3]:
   1
0
1  1
1  3
1  5
Run Code Online (Sandbox Code Playgroud)

Pandas将所有内容存储在DataFrames和Series对象中,这些对象都被编入索引,因此不会对输出产生太多麻烦,第一列是索引,第二列是重要的列,它将为您提供所需的数字.

您可以使用pandas做更多事情......例如,您可以按文件中的第一列进行分组并执行聚合:

In [64]: df = pd.read_table('largefile.txt', header=None).groupby(0)
In [65]: df.sum()
Out[65]:
   1
0
1  9
2  2
3  4
5  6
In [66]: df.mean()
Out[66]:
   1
0
1  3
2  2
3  4
5  6    
In [67]: df[0].count()
Out[67]:
0
1    3
2    1
3    1
5    1
dtype: int64
Run Code Online (Sandbox Code Playgroud)

我知道这不是如何加快字典速度的答案,但是从评论中提到的内容来看,这可能是另一种解决方案.

编辑 - 添加时间

与最快的字典解决方案相比,将数据加载到pandas DataFrame中:

test_dict.py

import sys
d = {}
with open(sys.argv[1]) as fin:
    for line in fin:
        parts = line.split(None, 1)
        d[parts[0]] = d.get(parts[0], []) + [parts[1]]
Run Code Online (Sandbox Code Playgroud)

test_pandas.py

import sys
import pandas as pd
df = pd.read_table(sys.argv[1], header=None, index_col=0)
Run Code Online (Sandbox Code Playgroud)

在Linux机器上定时:

$ time python test_dict.py largefile.txt
real    1m13.794s
user    1m10.148s
sys     0m3.075s

$ time python test_pandas.py largefile.txt
real    0m10.937s
user    0m9.819s
sys     0m0.504s
Run Code Online (Sandbox Code Playgroud)

编辑:用于新的示例文件

In [1]: import pandas as pd
In [2]: df = pd.read_table('largefile.txt', header=None,
                           sep=' ', index_col=0).sort_index()
In [3]: df.index
Out[3]: Int64Index([0, 1, 1, ..., 9999998, 9999999, 9999999], dtype=int64)
In [4]: df[1][0]
Out[4]: 6301
In [5]: df[1][1].values
Out[5]: array([8936, 5983])
Run Code Online (Sandbox Code Playgroud)