Математический алгоритм двумерного бинарного алгоритма

У меня возникли проблемы с разработкой подходящего алгоритма биннинга в Mathematica. У меня есть большой (~ 100 тыс. Элементов) набор данных формы   Т = {{x1, y1, z1}, {x2, y2, z2},....} и я хочу объединить его в 2D-массив размером около 100x100 бит, причем значение bin задается суммой значений Z, которые попадают в каждую ячейку.

В настоящее время я повторяю каждый элемент таблицы, используя Select, чтобы выбрать, в каком бункере он должен быть основан на списках границ bin, и добавив значение z в список значений, занимающих этот бит. В конце я нарисую Total на список ящиков, суммируя их содержимое (я делаю это, потому что иногда хочу делать другие вещи, например максимизировать).

Я попытался использовать Gather и другие подобные функции для этого, но вышеупомянутый метод был смехотворно быстрее, хотя, возможно, я плохо использую Gather. Во всяком случае, по-прежнему требуется несколько минут, чтобы выполнить сортировку по моему методу, и я чувствую, что Mathematica может сделать лучше. У кого-нибудь есть хороший эффективный алгоритм?

Ответы

Ответ 1

Вот метод, основанный на постулате Szabolcs, который примерно на порядок выше.

data = RandomReal[5, {500000, 3}];
(*500k values*)
zvalues = data[[All, 3]];

epsilon = 1*^-10;(*prevent 101 index*)
(*rescale and round (x,y) coordinates to index pairs in the 1..100 range*)
indexes = 1 + Floor[(1 - epsilon) 100 Rescale[data[[All, {1, 2}]]]];

res2 = Module[{gb = GatherBy[Transpose[{indexes, zvalues}], First]}, 
    SparseArray[
     gb[[All, 1, 1]] -> 
      Total[gb[[All, All, 2]], {2}]]]; // AbsoluteTiming

Дает {2.012217, Null}

AbsoluteTiming[
 System`SetSystemOptions[ 
  "SparseArrayOptions" -> {"TreatRepeatedEntries" -> 1}];
 res3 = SparseArray[indexes -> zvalues];
 System`SetSystemOptions[ 
  "SparseArrayOptions" -> {"TreatRepeatedEntries" -> 0}];
 ]

Дает {0.195228, Null}

res3 == res2
True

"TreatRepeatedEntries" → 1 добавляет повторяющиеся позиции вверх.

Ответ 2

Я намерен переписать код ниже из-за соображений удобочитаемости Сабольча. До тех пор, знайте, что если ваши корзины являются регулярными, и вместо Nearest вы можете использовать Round, Floor или Ceiling (со вторым аргументом), код ниже будет намного быстрее. В моей системе он тестируется быстрее, чем решение GatherBy.


Предполагая, что я понимаю ваши требования, я предлагаю:

data = RandomReal[100, {75, 3}];

bins = {0, 20, 40, 60, 80, 100};

Reap[
  Sow[{#3, #2}, bins ~Nearest~ #] & @@@ data,
  bins,
  Reap[Sow[#, bins ~Nearest~ #2] & @@@ #2, bins, [email protected]#2 &][[2]] &
][[2]] ~Flatten~ 1 ~Total~ {3} // MatrixForm

Refactored:

f[bins_] := Reap[Sow[{##2}, bins ~Nearest~ #]& @@@ #, bins, #2][[2]] &

bin2D[data_, X_, Y_] := f[X][data, f[Y][#2, #2~Total~2 &] &] ~Flatten~ 1 ~Total~ {3}

Использование:

bin2D[data, xbins, ybins]

Ответ 3

Здесь мой подход:

data = RandomReal[5, {500000, 3}]; (* 500k values *)

zvalues = data[[All, 3]];

epsilon = 1*^-10; (* prevent 101 index *)

(* rescale and round (x,y) coordinates to index pairs in the 1..100 range *)    
indexes = 1 + Floor[(1 - epsilon) 100 Rescale[data[[All, {1, 2}]]]];

(* approach 1: create bin-matrix first, then fill up elements by adding  zvalues *)
res1 = Module[
    {result = ConstantArray[0, {100, 100}]},
    Do[
      AddTo[result[[##]], zvalues[[i]]] & @@ indexes[[i]], 
      {i, Length[indexes]}
    ];
    result
    ]; // Timing

(* approach 2: gather zvalues by indexes, add them up, convert them to a matrix *)
res2 = Module[{gb = GatherBy[Transpose[{indexes, zvalues}], First]},
    SparseArray[gb[[All, 1, 1]] -> (Total /@ gb[[All, All, 2]])]
    ]; // Timing

res1 == res2

Эти два подхода (res1 и res2) могут обрабатывать соответственно 100k и 200k элементов в секунду на этой машине. Это достаточно быстро или вам нужно запустить всю эту программу в цикле?

Ответ 4

Здесь мой подход, использующий функцию SelectEquivalents, определенную в Что входит в ваш пакет инструментов Mathematica?, который идеально подходит для такой проблемы.

data = RandomReal[100, {75, 3}];
bins = Range[0, 100, 20];
binMiddles = ([email protected] + [email protected])/2;
nearest = Nearest[binMiddles];

SelectEquivalents[
   data
   ,
   TagElement -> ({[email protected][#[[1]]], [email protected][#[[2]]]} &)
   ,
   TransformElement -> (#[[3]] &)
   ,
   TransformResults -> (Total[#2] &)
   ,
   TagPattern -> Flatten[Outer[List, binMiddles, binMiddles], 1]
   , 
   FinalFunction -> (Partition[Flatten[# /. {} -> 0], Length[binMiddles]] &)
]

Если вы хотите группировать в соответствии с более чем двумя измерениями, вы можете использовать в функции FinalFunction эту функцию, чтобы придать результатом списка желаемое измерение (я не помню, где я его нашел).

InverseFlatten[l_,dimensions_]:= Fold[Partition[#, #2] &, l, Most[Reverse[dimensions]]];