所以我编写了一个Python程序来处理一些数据处理 任务。
这是我想要的计算语言的一个非常简短的说明:
parse "%s %lf %s" aa bb cc | group_by aa | quickselect --key=bb 0:5 | \
flatten | format "%s %lf %s" aa bb cc
也就是说,对于每一行,解析出一个单词,一个浮点数和另一个单词。将它们视为玩家ID,分数和日期。我想要每个球员的前五个得分和日期。数据大小并非微不足道,但并不大;大约630兆字节。
我想知道我应该用什么真正的可执行语言编写它 让它同样简短(如下面的Python)但速度要快得多。
#!/usr/bin/python
# -*- coding: utf-8; -*-
import sys
top_5 = {}
for line in sys.stdin:
aa, bb, cc = line.split()
# We want the top 5 for each distinct value of aa. There are
# hundreds of thousands of values of aa.
bb = float(bb)
if aa not in top_5: top_5[aa] = []
current = top_5[aa]
current.append((bb, cc))
# Every once in a while, we drop the values that are not in
# the top 5, to keep our memory footprint down, because some
# values of aa have thousands of (bb, cc) pairs.
if len(current) > 10:
current.sort()
current[:-5] = []
for aa in top_5:
current = top_5[aa]
current.sort()
for bb, cc in current[-5:]:
print aa, bb, cc
以下是一些示例输入数据:
3 1.5 a
3 1.6 b
3 0.8 c
3 0.9 d
4 1.2 q
3 1.5 e
3 1.8 f
3 1.9 g
这是我得到的输出:
3 1.5 a
3 1.5 e
3 1.6 b
3 1.8 f
3 1.9 g
4 1.2 q
3
有七个值,因此我们删除c
和d
值
因为他们的bb
值会使他们超出前5名。因为4
有
只有一个值,它的“前5”只包含那个值。
这比在MySQL中执行相同的查询运行得更快(至少,在 我们发现做查询的方式)但我很确定它的消费 大部分时间都在Python字节码解释器中。我认为 另一种语言,我可能会得到它来处理数百个 每秒数千行而不是每分钟。所以我想 用更快实现的语言编写它。
但我不确定选择哪种语言。
我无法弄清楚如何在SQL中将其表达为单个查询,并且
实际上,我对MySQL的能力甚至不感兴趣
select * from foo into outfile 'bar';
输入数据。
C是一个明显的选择,但是line.split()
之类的东西,对列表进行排序
2元组,并制作哈希表需要编写一些代码
不在标准库中,所以我最终会得到100行代码
或更多,而不是14。
C ++似乎可能是一个更好的选择(它有字符串,地图, 对和标准库中的向量)但它看起来像代码 STL会比较混乱。
OCaml没问题,但它有相当于line.split()
的情况,
我会对地图的表现感到难过吗?
Common Lisp可能有用吗?
是否有类似于Matlab的数据库计算 这让我把循环推入快速代码?有人试过Pig吗?
(编辑:通过提供一些示例输入和输出数据来回复davethegr8的评论,并修复了Python程序中的错误!)
(补充编辑:哇,这个评论帖子到目前为止非常棒。谢谢,大家!)
编辑:
有一个eerily similar question asked on sbcl-devel in 2007(谢谢,Rainer!),这是来自Will Hartung的awk
脚本,用于生成一些测试数据(尽管它没有Zipfian分布的真实数据) :
BEGIN {
for (i = 0; i < 27000000; i++) {
v = rand();
k = int(rand() * 100);
print k " " v " " i;
}
exit;
}
答案 0 :(得分:9)
答案 1 :(得分:6)
您可以使用更智能的数据结构并仍然使用python。 我在我的机器上运行了你的参考实现和我的python实现,甚至比较了输出结果。
这是你的:
$ time python ./ref.py < data-large.txt > ref-large.txt
real 1m57.689s
user 1m56.104s
sys 0m0.573s
这是我的:
$ time python ./my.py < data-large.txt > my-large.txt
real 1m35.132s
user 1m34.649s
sys 0m0.261s
$ diff my-large.txt ref-large.txt
$ echo $?
0
这是来源:
#!/usr/bin/python
# -*- coding: utf-8; -*-
import sys
import heapq
top_5 = {}
for line in sys.stdin:
aa, bb, cc = line.split()
# We want the top 5 for each distinct value of aa. There are
# hundreds of thousands of values of aa.
bb = float(bb)
if aa not in top_5: top_5[aa] = []
current = top_5[aa]
if len(current) < 5:
heapq.heappush(current, (bb, cc))
else:
if current[0] < (bb, cc):
heapq.heapreplace(current, (bb, cc))
for aa in top_5:
current = top_5[aa]
while len(current) > 0:
bb, cc = heapq.heappop(current)
print aa, bb, cc
更新:了解您的限制。 我还计划了一个noop代码,知道最快的python解决方案,其代码类似于原始代码:
$ time python noop.py < data-large.txt > noop-large.txt
real 1m20.143s
user 1m19.846s
sys 0m0.267s
noop.py本身:
#!/usr/bin/python
# -*- coding: utf-8; -*-
import sys
import heapq
top_5 = {}
for line in sys.stdin:
aa, bb, cc = line.split()
bb = float(bb)
if aa not in top_5: top_5[aa] = []
current = top_5[aa]
if len(current) < 5:
current.append((bb, cc))
for aa in top_5:
current = top_5[aa]
current.sort()
for bb, cc in current[-5:]:
print aa, bb, cc
答案 2 :(得分:3)
这是另一个OCaml版本 - 针对速度 - 使用Streams上的自定义解析器。太长了,但解析器的一部分是可重用的。感谢 peufeu 引发竞争:)
速度:
编译:
ocamlopt -pp camlp4o code.ml -o caml
代码:
open Printf
let cmp x y = compare (fst x : float) (fst y)
let digit c = Char.code c - Char.code '0'
let rec parse f = parser
| [< a=int; _=spaces; b=float; _=spaces;
c=rest (Buffer.create 100); t >] -> f a b c; parse f t
| [< >] -> ()
and int = parser
| [< ''0'..'9' as c; t >] -> int_ (digit c) t
| [< ''-'; ''0'..'9' as c; t >] -> - (int_ (digit c) t)
and int_ n = parser
| [< ''0'..'9' as c; t >] -> int_ (n * 10 + digit c) t
| [< >] -> n
and float = parser
| [< n=int; t=frem; e=fexp >] -> (float_of_int n +. t) *. (10. ** e)
and frem = parser
| [< ''.'; r=frem_ 0.0 10. >] -> r
| [< >] -> 0.0
and frem_ f base = parser
| [< ''0'..'9' as c; t >] ->
frem_ (float_of_int (digit c) /. base +. f) (base *. 10.) t
| [< >] -> f
and fexp = parser
| [< ''e'; e=int >] -> float_of_int e
| [< >] -> 0.0
and spaces = parser
| [< '' '; t >] -> spaces t
| [< ''\t'; t >] -> spaces t
| [< >] -> ()
and crlf = parser
| [< ''\r'; t >] -> crlf t
| [< ''\n'; t >] -> crlf t
| [< >] -> ()
and rest b = parser
| [< ''\r'; _=crlf >] -> Buffer.contents b
| [< ''\n'; _=crlf >] -> Buffer.contents b
| [< 'c; t >] -> Buffer.add_char b c; rest b t
| [< >] -> Buffer.contents b
let () =
let all = Array.make 200 [] in
let each a b c =
assert (a >= 0 && a < 200);
match all.(a) with
| [] -> all.(a) <- [b,c]
| (bmin,_) as prev::tl -> if b > bmin then
begin
let m = List.sort cmp ((b,c)::tl) in
all.(a) <- if List.length tl < 4 then prev::m else m
end
in
parse each (Stream.of_channel stdin);
Array.iteri
(fun a -> List.iter (fun (b,c) -> printf "%i %f %s\n" a b c))
all
答案 3 :(得分:3)
这是Common Lisp中的草图
请注意,对于长文件,使用READ-LINE会有一个惩罚,因为它会为每一行提供一个新字符串。然后使用READ-LINE的衍生物之一,它们使用行缓冲器浮动。您也可以检查是否希望哈希表区分大小写。
第二版
不再需要拆分字符串,因为我们在此处执行此操作。它是低级代码,希望可以获得一些速度提升。它会检查一个或多个空格作为字段分隔符以及标签。
(defun read-a-line (stream)
(let ((line (read-line stream nil nil)))
(flet ((delimiter-p (c)
(or (char= c #\space) (char= c #\tab))))
(when line
(let* ((s0 (position-if #'delimiter-p line))
(s1 (position-if-not #'delimiter-p line :start s0))
(s2 (position-if #'delimiter-p line :start (1+ s1)))
(s3 (position-if #'delimiter-p line :from-end t)))
(values (subseq line 0 s0)
(list (read-from-string line nil nil :start s1 :end s2)
(subseq line (1+ s3)))))))))
上面的函数返回两个值:键和其余的列表。
(defun dbscan (top-5-table stream)
"get triples from each line and put them in the hash table"
(loop with aa = nil and bbcc = nil do
(multiple-value-setq (aa bbcc) (read-a-line stream))
while aa do
(setf (gethash aa top-5-table)
(let ((l (merge 'list (gethash aa top-5-table) (list bbcc)
#'> :key #'first)))
(or (and (nth 5 l) (subseq l 0 5)) l)))))
(defun dbprint (table output)
"print the hashtable contents"
(maphash (lambda (aa value)
(loop for (bb cc) in value
do (format output "~a ~a ~a~%" aa bb cc)))
table))
(defun dbsum (input &optional (output *standard-output*))
"scan and sum from a stream"
(let ((top-5-table (make-hash-table :test #'equal)))
(dbscan top-5-table input)
(dbprint top-5-table output)))
(defun fsum (infile outfile)
"scan and sum a file"
(with-open-file (input infile :direction :input)
(with-open-file (output outfile
:direction :output :if-exists :supersede)
(dbsum input output))))
一些测试数据
(defun create-test-data (&key (file "/tmp/test.data") (n-lines 100000))
(with-open-file (stream file :direction :output :if-exists :supersede)
(loop repeat n-lines
do (format stream "~a ~a ~a~%"
(random 1000) (random 100.0) (random 10000)))))
(创建测试数据)
(defun test ()
(time (fsum "/tmp/test.data" "/tmp/result.data")))
第三版,LispWorks
使用一些SPLIT-STRING和PARSE-FLOAT函数,否则使用通用CL。
(defun fsum (infile outfile)
(let ((top-5-table (make-hash-table :size 50000000 :test #'equal)))
(with-open-file (input infile :direction :input)
(loop for line = (read-line input nil nil)
while line do
(destructuring-bind (aa bb cc) (split-string '(#\space #\tab) line)
(setf bb (parse-float bb))
(let ((v (gethash aa top-5-table)))
(unless v
(setf (gethash aa top-5-table)
(setf v (make-array 6 :fill-pointer 0))))
(vector-push (cons bb cc) v)
(when (> (length v) 5)
(setf (fill-pointer (sort v #'> :key #'car)) 5))))))
(with-open-file (output outfile :direction :output :if-exists :supersede)
(maphash (lambda (aa value)
(loop for (bb . cc) across value do
(format output "~a ~f ~a~%" aa bb cc)))
top-5-table))))
答案 4 :(得分:3)
我的机器上有45.7秒,有27M行数据,如下所示:
42 0.49357 0
96 0.48075 1
27 0.640761 2
8 0.389128 3
75 0.395476 4
24 0.212069 5
80 0.121367 6
81 0.271959 7
91 0.18581 8
69 0.258922 9
你的脚本对这个数据花了1m42,c ++例子也是1m46(g ++ t.cpp -o t来编译它,我对c ++一无所知)。
Java 6,并不重要。输出并不完美,但很容易修复。
package top5;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
Map<String, Pair[]> top5map = new TreeMap<String, Pair[]>();
BufferedReader br = new BufferedReader(new FileReader("/tmp/file.dat"));
String line = br.readLine();
while(line != null) {
String parts[] = line.split(" ");
String key = parts[0];
double score = Double.valueOf(parts[1]);
String value = parts[2];
Pair[] pairs = top5map.get(key);
boolean insert = false;
Pair p = null;
if (pairs != null) {
insert = (score > pairs[pairs.length - 1].score) || pairs.length < 5;
} else {
insert = true;
}
if (insert) {
p = new Pair(score, value);
if (pairs == null) {
pairs = new Pair[1];
pairs[0] = new Pair(score, value);
} else {
if (pairs.length < 5) {
Pair[] newpairs = new Pair[pairs.length + 1];
System.arraycopy(pairs, 0, newpairs, 0, pairs.length);
pairs = newpairs;
}
int k = 0;
for(int i = pairs.length - 2; i >= 0; i--) {
if (pairs[i].score <= p.score) {
pairs[i + 1] = pairs[i];
} else {
k = i + 1;
break;
}
}
pairs[k] = p;
}
top5map.put(key, pairs);
}
line = br.readLine();
}
for(Map.Entry<String, Pair[]> e : top5map.entrySet()) {
System.out.print(e.getKey());
System.out.print(" ");
System.out.println(Arrays.toString(e.getValue()));
}
System.out.println(System.currentTimeMillis() - start);
}
static class Pair {
double score;
String value;
public Pair(double score, String value) {
this.score = score;
this.value = value;
}
public int compareTo(Object o) {
Pair p = (Pair) o;
return (int)Math.signum(score - p.score);
}
public String toString() {
return String.valueOf(score) + ", " + value;
}
}
}
伪造数据的AWK脚本:
BEGIN {
for (i = 0; i < 27000000; i++) {
v = rand();
k = int(rand() * 100);
print k " " v " " i;
}
exit;
}
答案 5 :(得分:2)
答案 6 :(得分:2)
相当简单的Caml(27 * 10 ^ 6行 - 27秒,C ++ by hrnt - 29秒)
open Printf
open ExtLib
let (>>) x f = f x
let cmp x y = compare (fst x : float) (fst y)
let wsp = Str.regexp "[ \t]+"
let () =
let all = Hashtbl.create 1024 in
Std.input_lines stdin >> Enum.iter (fun line ->
let [a;b;c] = Str.split wsp line in
let b = float_of_string b in
try
match Hashtbl.find all a with
| [] -> assert false
| (bmin,_) as prev::tl -> if b > bmin then
begin
let m = List.sort ~cmp ((b,c)::tl) in
Hashtbl.replace all a (if List.length tl < 4 then prev::m else m)
end
with Not_found -> Hashtbl.add all a [b,c]
);
all >> Hashtbl.iter (fun a -> List.iter (fun (b,c) -> printf "%s %f %s\n" a b c))
答案 7 :(得分:1)
说到计算时间的下限:
让我们分析一下上面的算法:
for each row (key,score,id) :
create or fetch a list of top scores for the row's key
if len( this list ) < N
append current
else if current score > minimum score in list
replace minimum of list with current row
update minimum of all lists if needed
设N是前N的N. 设R是数据集中的行数 设K是不同键的数量
我们可以做出哪些假设?
R * sizeof(行)&gt; RAM或者至少它足够大,我们不想加载它,使用哈希按键分组,并对每个bin进行排序。出于同样的原因,我们不会对整个事情进行排序。
Kragen喜欢哈希表,因此K * sizeof(每个键状态)&lt;&lt; RAM,很可能它适合L2 / 3缓存
Kragen没有排序,所以K * N&lt;&lt; R即每个键具有多于N个条目
(注意:A&lt;&lt; B表示A相对于B较小)
如果数据具有随机分布,那么
在少量行之后,大多数行将被每个键的最小条件拒绝,每行的成本为1比较。
所以每行的成本是1个哈希查找+ 1个比较+ epsilon *(列表插入+(N + 1)比较最小)
如果分数具有随机分布(比如介于0和1之间)且上述条件成立,则两个epsilons都将非常小。
实验证明:
上面的2700万行数据集在前N个列表中产生5933个插入。通过简单的键查找和比较拒绝所有其他行。 epsilon = 0.0001
粗略地说,成本是每行1次查找+比较,这需要几纳秒。
在目前的硬件上,对于IO成本,尤其是解析成本,这是不可忽视的。
答案 8 :(得分:1)
这是一个C ++解决方案。但是,我没有很多数据来测试它,所以我不知道它实际上有多快。
[edit]感谢这个帖子中awk脚本提供的测试数据,我 设法清理并加快了代码的速度。我并不想找出最快的版本 - 意图是提供一个相当快的版本,这个版本并不像人们认为STL解决方案那样难看。
这个版本应该是第一个版本的两倍(在大约35秒内通过2700万行)。 Gcc用户,记得 用-O2编译它。
#include <map>
#include <iostream>
#include <functional>
#include <utility>
#include <string>
int main() {
using namespace std;
typedef std::map<string, std::multimap<double, string> > Map;
Map m;
string aa, cc;
double bb;
std::cin.sync_with_stdio(false); // Dunno if this has any effect, but anyways.
while (std::cin >> aa >> bb >> cc)
{
if (m[aa].size() == 5)
{
Map::mapped_type::iterator iter = m[aa].begin();
if (bb < iter->first)
continue;
m[aa].erase(iter);
}
m[aa].insert(make_pair(bb, cc));
}
for (Map::const_iterator iter = m.begin(); iter != m.end(); ++iter)
for (Map::mapped_type::const_iterator iter2 = iter->second.begin();
iter2 != iter->second.end();
++iter2)
std::cout << iter->first << " " << iter2->first << " " << iter2->second <<
std::endl;
}
答案 9 :(得分:1)
有趣的是,最初的Python解决方案是迄今为止最干净的看(尽管C ++示例很接近)。
如何在原始代码上使用Pyrex或Psyco?
答案 10 :(得分:1)
有人试过用awk来解决这个问题。特别是'mawk'?根据这篇博文:http://anyall.org/blog/2009/09/dont-mawk-awk-the-fastest-and-most-elegant-big-data-munging-language/
,它应该比Java和C ++更快编辑:只是想澄清一下,在博客文章中提出的唯一声明是,对于特定适合awk风格处理的某类问题,mawk虚拟机可以击败Java中的'vanilla'实现。 C ++。
答案 11 :(得分:1)
既然你问过Matlab,我就是这样做的,就像你要求的那样。我尝试没有任何for循环,但我确实有一个,因为我不想花很长时间。如果您担心内存,那么您可以使用fscanf从块中提取数据而不是读取整个缓冲区。
fid = fopen('fakedata.txt','r');
tic
A=fscanf(fid,'%d %d %d\n');
A=reshape(A,3,length(A)/3)'; %Matlab reads the data into one long column'
Names = unique(A(:,1));
for i=1:length(Names)
indices = find(A(:,1)==Names(i)); %Grab all instances of key i
[Y,I] = sort(A(indices,2),1,'descend'); %sort in descending order of 2nd record
A(indices(I(1:min([5,length(indices(I))]))),:) %Print the top five
end
toc
fclose(fid)
答案 12 :(得分:0)
Pig版本会像这样(未经测试):
Data = LOAD '/my/data' using PigStorage() as (aa:int, bb:float, cc:chararray);
grp = GROUP Data by aa;
topK = FOREACH grp (
sorted = ORDER Data by bb DESC;
lim = LIMIT sorted 5;
GENERATE group as aa, lim;
)
STORE topK INTO '/my/output' using PigStorage();
猪没有针对性能进行优化;它的目标是使用并行执行框架来处理多TB数据集。它确实有一个本地模式,所以你可以试试,但我怀疑它会击败你的剧本。
答案 13 :(得分:0)
我喜欢午休时间的挑战。这是一个1小时的实施。
好的,当你不想做一些极其奇特的废话之类的添加时,没有什么可以阻止你使用自定义的基础10浮点格式,其中唯一实现的运算符是比较,对吧?洛尔。
我在之前的项目中有一些快速的代码,所以我只是导入了它。
http://www.copypastecode.com/11541/
此C源代码大约需要6.6秒来解析580MB的输入文本(2700万行),其中一半时间是fgets,lol。然后计算top-n大约需要0.05秒,但我不确定,因为top-n所需的时间小于定时器噪声。
你将是那个通过XDDDDDDDDDD
测试它的正确性的人 有趣的是吗?答案 14 :(得分:0)
好吧,请抓一杯咖啡,阅读strtod的源代码 - 这是令人难以置信的,但如果你想漂浮,则需要 - &gt;文字 - &gt;漂浮以回馈你开始时的同一个浮动......真的......
解析整数要快得多(但在python中并不是那么多,但在C中,是的)。
无论如何,将数据放在Postgres表中:
SELECT count( key ) FROM the dataset in the above program
=&GT; 7秒(所以读取27M记录需要7秒)
CREATE INDEX topn_key_value ON topn( key, value );
191 s
CREATE TEMPORARY TABLE topkeys AS SELECT key FROM topn GROUP BY key;
12 s
(您可以使用索引更快地获取'key'的不同值,但它需要一些轻量级的plpgsql黑客攻击)
CREATE TEMPORARY TABLE top AS SELECT (r).* FROM (SELECT (SELECT b AS r FROM topn b WHERE b.key=a.key ORDER BY value DESC LIMIT 1) AS r FROM topkeys a) foo;
时间:15,310毫秒
INSERT INTO top SELECT (r).* FROM (SELECT (SELECT b AS r FROM topn b WHERE b.key=a.key ORDER BY value DESC LIMIT 1 OFFSET 1) AS r FROM topkeys a) foo;
时间:17,853毫秒
INSERT INTO top SELECT (r).* FROM (SELECT (SELECT b AS r FROM topn b WHERE b.key=a.key ORDER BY value DESC LIMIT 1 OFFSET 2) AS r FROM topkeys a) foo;
时间:13,983毫秒
INSERT INTO top SELECT (r).* FROM (SELECT (SELECT b AS r FROM topn b WHERE b.key=a.key ORDER BY value DESC LIMIT 1 OFFSET 3) AS r FROM topkeys a) foo;
时间:16,860毫秒
INSERT INTO top SELECT (r).* FROM (SELECT (SELECT b AS r FROM topn b WHERE b.key=a.key ORDER BY value DESC LIMIT 1 OFFSET 4) AS r FROM topkeys a) foo;
时间:17,651毫秒
INSERT INTO top SELECT (r).* FROM (SELECT (SELECT b AS r FROM topn b WHERE b.key=a.key ORDER BY value DESC LIMIT 1 OFFSET 5) AS r FROM topkeys a) foo;
时间:19,216 ms
SELECT * FROM top ORDER BY key,value;
正如你所看到的,计算top-n非常快(假设n很小)但是创建(强制)索引非常慢,因为它涉及完整的排序。
您最好的选择是使用快速解析的格式(二进制,或为您的数据库编写自定义C聚合,这将是恕我直言的最佳选择)。如果python可以在1秒内完成,那么C程序中的运行时不应超过1秒。
答案 15 :(得分:0)
这是一个很好的午休挑战,他,他。
Top-N是众所周知的数据库杀手。如上面的帖子所示,没有办法在常见的SQL中有效地表达它。
至于各种实现,你必须记住,这里的缓慢部分不是排序或前N,它是文本的解析。你最近看过glibc的strtod()的源代码吗?
例如,我得到了,使用Python:
Read data : 80.5 s
My TopN : 34.41 s
HeapTopN : 30.34 s
你很可能永远不会得到非常快的时间,无论你使用什么语言,除非你的数据采用某种格式,解析比文本快得多。例如,将测试数据加载到postgres需要70秒,其中大部分也是文本解析。
如果你的topN中的N很小,比如5,我的算法的C实现可能是最快的。如果N可以更大,那么堆是更好的选择。
所以,既然你的数据可能在数据库中,而你的问题是数据而不是实际的处理,如果你真的需要一个超快的TopN引擎,你应该做的就是写一个C您选择的数据库模块。由于postgres对于任何事情都更快,我建议使用postgres,而且为它编写C模块并不困难。
这是我的Python代码:
import random, sys, time, heapq
ROWS = 27000000
def make_data( fname ):
f = open( fname, "w" )
r = random.Random()
for i in xrange( 0, ROWS, 10000 ):
for j in xrange( i,i+10000 ):
f.write( "%d %f %d\n" % (r.randint(0,100), r.uniform(0,1000), j))
print ("write: %d\r" % i),
sys.stdout.flush()
print
def read_data( fname ):
for n, line in enumerate( open( fname ) ):
r = line.strip().split()
yield int(r[0]),float(r[1]),r[2]
if not (n % 10000 ):
print ("read: %d\r" % n),
sys.stdout.flush()
print
def topn( ntop, data ):
ntop -= 1
assert ntop > 0
min_by_key = {}
top_by_key = {}
for key,value,label in data:
tup = (value,label)
if key not in top_by_key:
# initialize
top_by_key[key] = [ tup ]
else:
top = top_by_key[ key ]
l = len( top )
if l > ntop:
# replace minimum value in top if it is lower than current value
idx = min_by_key[ key ]
if top[idx] < tup:
top[idx] = tup
min_by_key[ key ] = top.index( min( top ) )
elif l < ntop:
# fill until we have ntop entries
top.append( tup )
else:
# we have ntop entries in list, we'll have ntop+1
top.append( tup )
# initialize minimum to keep
min_by_key[ key ] = top.index( min( top ) )
# finalize:
return dict( (key, sorted( values, reverse=True )) for key,values in top_by_key.iteritems() )
def grouptopn( ntop, data ):
top_by_key = {}
for key,value,label in data:
if key in top_by_key:
top_by_key[ key ].append( (value,label) )
else:
top_by_key[ key ] = [ (value,label) ]
return dict( (key, sorted( values, reverse=True )[:ntop]) for key,values in top_by_key.iteritems() )
def heaptopn( ntop, data ):
top_by_key = {}
for key,value,label in data:
tup = (value,label)
if key not in top_by_key:
top_by_key[ key ] = [ tup ]
else:
top = top_by_key[ key ]
if len(top) < ntop:
heapq.heappush(top, tup)
else:
if top[0] < tup:
heapq.heapreplace(top, tup)
return dict( (key, sorted( values, reverse=True )) for key,values in top_by_key.iteritems() )
def dummy( data ):
for row in data:
pass
make_data( "data.txt" )
t = time.clock()
dummy( read_data( "data.txt" ) )
t_read = time.clock() - t
t = time.clock()
top_result = topn( 5, read_data( "data.txt" ) )
t_topn = time.clock() - t
t = time.clock()
htop_result = heaptopn( 5, read_data( "data.txt" ) )
t_htopn = time.clock() - t
# correctness checking :
for key in top_result:
print key, " : ", " ".join (("%f:%s"%(value,label)) for (value,label) in top_result[key])
print key, " : ", " ".join (("%f:%s"%(value,label)) for (value,label) in htop_result[key])
print
print "Read data :", t_read
print "TopN : ", t_topn - t_read
print "HeapTopN : ", t_htopn - t_read
for key in top_result:
assert top_result[key] == htop_result[key]
答案 16 :(得分:0)
挑选“前5名”看起来像这样。请注意,没有排序。 top_5词典中的任何列表都不会超过5个元素。
from collections import defaultdict
import sys
def keep_5( aList, aPair ):
minbb= min( bb for bb,cc in aList )
bb, cc = aPair
if bb < minbb: return aList
aList.append( aPair )
min_i= 0
for i in xrange(1,6):
if aList[i][0] < aList[min_i][0]
min_i= i
aList.pop(min_i)
return aList
top_5= defaultdict(list)
for row in sys.stdin:
aa, bb, cc = row.split()
bb = float(bb)
if len(top_5[aa]) < 5:
top_5[aa].append( (bb,cc) )
else:
top_5[aa]= keep_5( top_5[aa], (bb,cc) )
答案 17 :(得分:0)
这不像
那么简单 SELECT DISTINCT aa, bb, cc FROM tablename ORDER BY bb DESC LIMIT 5
当然,如果不对数据进行测试,很难说出最快的速度。如果这是你需要快速运行的东西,那么优化数据库以使查询更快,而不是优化查询可能是有意义的。
当然,如果你还需要平面文件,你也可以使用它。