Какого черта, я присоединюсь к вечеринке. Вот моя версия:
Flatten/@Flatten[Thread/@Transpose@{#,Mean/@#[[All,All,2]]}&@GatherBy[e,First],1]
Думаю, должно быть достаточно быстро.
EDIT
В ответ на критику @ Mr.Wizard (моим первым решением было изменение порядка в списке), а также для небольшого изучения высокопроизводительного угла проблемы, есть 2 альтернативных решения:
getMeans[e_] :=
Module[{temp = ConstantArray[0, Max[#[[All, 1, 1]]]]},
temp[[#[[All, 1, 1]]]] = Mean /@ #[[All, All, 2]];
List /@ temp[[e[[All, 1]]]]] &[GatherBy[e, First]];
getMeansSparse[e_] :=
Module[{temp = SparseArray[{Max[#[[All, 1, 1]]] -> 0}]},
temp[[#[[All, 1, 1]]]] = Mean /@ #[[All, All, 2]];
List /@ Normal@temp[[e[[All, 1]]]]] &[GatherBy[e, First]];
Первый является самым быстрым, торгующим памятью на скорость, и может применяться, когда все ключи являются целыми числами, и ваше максимальное значение "ключа" (2 в вашем примере) не слишком велико. Второе решение свободно от последнего ограничения, но медленнее. Вот большой список пар:
In[303]:=
tst = RandomSample[#, Length[#]] &@
Flatten[Map[Thread[{#, RandomInteger[{1, 100}, 300]}] &,
RandomSample[Range[1000], 500]], 1];
In[310]:= Length[tst]
Out[310]= 150000
In[311]:= tst[[;; 10]]
Out[311]= {{947, 52}, {597, 81}, {508, 20}, {891, 81}, {414, 47},
{849, 45}, {659, 69}, {841, 29}, {700, 98}, {858, 35}}
Здесь ключей может быть от 1 до 1000, из них 500, и для каждого ключа есть 300 случайных чисел. Теперь некоторые показатели:
In[314]:= (res0 = getMeans[tst]); // Timing
Out[314]= {0.109, Null}
In[317]:= (res1 = getMeansSparse[tst]); // Timing
Out[317]= {0.219, Null}
In[318]:= (res2 = tst[[All, {1}]] /.
Reap[Sow[#2, #] & @@@ tst, _, # -> Mean@#2 &][[2]]); // Timing
Out[318]= {5.687, Null}
In[319]:= (res3 = tst[[All, {1}]] /.
Dispatch[
Reap[Sow[#2, #] & @@@ tst, _, # -> Mean@#2 &][[2]]]); // Timing
Out[319]= {0.391, Null}
In[320]:= res0 === res1 === res2 === res3
Out[320]= True
Мы можем видеть, что getMeans
является самым быстрым здесь, getMeansSparse
вторым самым быстрым, и решение @ Mr.Wizard несколько медленнее, но только когда мы используем Dispatch
, иначе это намного медленнее. Мои и @ Mr.Wizard решения (с Dispatch) схожи по духу, разница в скорости происходит из-за (разреженной) индексации массива, которая более эффективна, чем поиск по хешу. Конечно, все это имеет значение только тогда, когда ваш список действительно большой.
РЕДАКТИРОВАТЬ 2
Вот версия getMeans
, которая использует Compile
с целью C и возвращает числовые значения (а не рациональные). Это примерно в два раза быстрее, чем getMeans
, и самое быстрое из моих решений.
getMeansComp =
Compile[{{e, _Integer, 2}},
Module[{keys = e[[All, 1]], values = e[[All, 2]], sums = {0.} ,
lengths = {0}, , i = 1, means = {0.} , max = 0, key = -1 ,
len = Length[e]},
max = Max[keys];
sums = Table[0., {max}];
lengths = Table[0, {max}];
means = sums;
Do[key = keys[[i]];
sums[[key]] += values[[i]];
lengths[[key]]++, {i, len}];
means = sums/(lengths + (1 - Unitize[lengths]));
means[[keys]]], CompilationTarget -> "C", RuntimeOptions -> "Speed"]
getMeansC[e_] := List /@ getMeansComp[e];
Код 1 - Unitize[lengths]
защищает от деления на ноль для неиспользованных ключей. Нам нужен каждый номер в отдельном подсписке, поэтому мы должны звонить getMeansC
, а не getMeansComp
напрямую. Вот некоторые измерения:
In[180]:= (res1 = getMeans[tst]); // Timing
Out[180]= {0.11, Null}
In[181]:= (res2 = getMeansC[tst]); // Timing
Out[181]= {0.062, Null}
In[182]:= N@res1 == res2
Out[182]= True
Вероятно, это можно считать сильно оптимизированным численным решением. Тот факт, что полное общее, краткое и красивое решение @ Mr.Wizard медленнее всего в 6-8 раз, говорит о хорошем общем кратком решении, поэтому, если вы не хотите выжать каждую микросекунду из него, я бы придерживаться @ Mr.Wizard's (с Dispatch
). Но важно знать, как оптимизировать код, а также в какой степени его можно оптимизировать (чего можно ожидать).