This answer will be about efficiency, no ambitions for the beauty contest participation here. Since you mentioned speed, and only need mean values, here is a method that will be an order of magnitude faster and order of magnitude more memory-efficient than the (fine) solutions offered so far.
Code
First, we make a compiled function generator:
ClearAll[generateFastMean];
generateFastMean[maxIndex_Integer?Positive] :=
Compile[{{data, _Real, 2}},
Module[{means = Table[0., {maxIndex}], num = Table[0, {maxIndex}],
ctr = 0, i = 0, index = 0, resultIndices = Table[0, {maxIndex}],
indexHash = Table[0, {maxIndex}]},
Do[
index = IntegerPart[data[[i, 2]]];
means[[index]] += data[[i, 1]];
num[[index]]++;
If[indexHash[[index]] == 0,
indexHash[[index]] = 1;
resultIndices[[++ctr]] = index
];,
{i, Length[data]}
];
resultIndices = Take[resultIndices, ctr];
Transpose[{resultIndices, means[[resultIndices]]/num[[resultIndices]]}]
], (* Module *)
CompilationTarget -> "C", RuntimeOptions -> "Speed"];
What happens here is that I exploit the fact that your indices are not too large integers, and use arrays as hash-tables to keep the data I accumulate. At the end, I extract only those indices which are present, and compute means by dividing a list of totals by a list of index frequencies. Since your indices are smaller than 5000, I will generate the function accordingly:
fastMean = generateFastMean[5000];
The case at hand
First, we load the data (by the way, compressing is a fine idea but one can have additional savings, see below):
AK6 = Uncompress@Import["http://api2.ge.tt/0/9F8d6WD/0/blob/download", "String"];
To reduce the memory this occupies, we Flatten
the list as:
AK6PFl1 = Join @@Map[Developer`ToPackedArray, AK6]
Just to illustrate what kind of savings I am speaking about:
ByteCount /@ {AK6PFl, Flatten[AK6, 1]}
(*
==> {18238440, 127667944}
*)
So, we are looking at the order of magnitude here. Ok, now computing:
fastMean[AK6PFl1] //Short[#,5]&//AbsoluteTiming
(*
==> {0.063,{{1000.,5.00499},{1001.,5.00541},{1002.,5.00556},<<2995>>,
{3998.,3.72284},{3999.,3.72058},{4000.,3.71838}}}
*)
How to store the data
While Compress
is a good idea, I would supplement it with converting sublists to packed arrays, as illustrated above. So, you can do
Export["C:\\Temp\\AK6.dat", Compress@Map[Developer`ToPackedArray, AK6], "String"]
Then, the import is fast, and there is no unpacking (which happens with the current version of the file):
Uncompress@Import["C:\\Temp\\AK6.dat","String"]//ByteCount//AbsoluteTiming
(*
==> {0.594,18305896}
*)
The idea of combining Compress
and packed arrays (in a somehwat more complex setting since I made the data self-uncompressing), allowed me to get the remarkable performance in this case, and seems to represent a general method.
Remarks
I cheated because I did not include the compilation time, which is significant. However, if you have to do this many times, this may pay off. Of course, this will all make sense only when you need extreme performance, but I think it is good to understand what your performance limitations really are, for a given problem (I do not claim that the code has been fully optimized).
Export["data.dat.gz", Compress[AK6], "String"]
.Compress
gives an ASCII (i.e. 7-bit) string, so I did an additional gzip compression inExport
to shave off some 10-12%. Ideally one could justExport
toWDX
(which I think is already compressed) but with this amount of data it is just too slow. $\endgroup$data = Uncompress@Import["data.dat.gz", "String"];
.Import
andExport
will automatically handle compressing to or uncompressing fromgz
if they detect that the file is gzip-compressed. $\endgroup$