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)
我要做的唯一操作是查询一个键以返回与之关联的值列表.
如果您想要在评论中说出的内容,那么您可以在熊猫中轻松完成:假设您有一个具有相同布局的文件,但条目会重复,因为在您的示例中,您将所有重复项添加到列表中:
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)