Преобразуйте координатную систему в Матрицу в Mathematica - PullRequest
6 голосов
/ 20 августа 2011

В связи с вопросом о программировании, я считаю, что мне нужно немного рассказать о том, что я делаю, чтобы облегчить понимание моей проблемы:

Я записываю движения глаз при отображении некоторых рисунков для объекта съемки. В ходе эксперимента я позже продемонстрировал симметричное преобразование этих паттернов.

enter image description here

Я получаю списки координат и продолжительности фиксаций:

{{fix1X, fix1Y, fix1Dn}, {fix2X, fix2Y, fix2Dn}, ... {fixNX, fixNY, fixNDn}}

Где:

- fix1X - это координата X для первой фиксации.

- fix1Y - это координата Y для первой фиксации.

- fix1D - продолжительность в миллисекундах фиксаций

Пожалуйста, примите во внимание:

FrameWidth  = 31.36;
scrHeightCM = 30;
scrWidthCM  = 40;
FrameXYs    = {{4.32, 3.23}, {35.68, 26.75}};  (* {{Xmin,Ymin},{Xmax,Ymax}} *)

Ниже приведены фиксации для 1 дисплея (тематические фиксации во время представления стимулов 3s на экране)

fix ={{20.14, 15.22, 774.}, {20.26, 15.37, 518.}, {25.65, 16.22, 200.}, 
      {28.15, 11.06, 176.}, {25.25, 13.38, 154.}, {24.78, 15.74, 161.}, 
      {24.23, 16.58, 121.}, {20.06, 13.22, 124.}, {24.91, 15.8, 273.}, 
      {24.32, 12.83, 119.}, {20.06, 12.14, 366.}, {25.64, 18.22, 236.}, 
      {24.37, 19.2, 177.}, {21.02, 16.4, 217.}, {20.63, 15.75,406.}}

Graphics[{
          Gray, EdgeForm[Thick],
          Rectangle @@ {{0, 0}, {scrWidthCM, scrHeightCM}},
          White,
          Rectangle @@ StimuliFrameCoordinates,
          Dashed, Black,
         Line[
             {{(scrWidthCM/2), FrameXYs[[1, 2]]},
             {(scrWidthCM/2), FrameXYs[[2, 2]]}}],
         Line[
             {{FrameXYs[[1, 1]], (scrHeightCM/2)},
             {(FrameXYs[[2, 1]]), (scrHeightCM/2)}}],

         Thickness[0.005], Pink,
         Disk[{#[[1]], #[[2]]}, 9 N[#[[3]]/Total[fix[[All, 3]]]]] & /@ fix
         }, ImageSize -> 500]

enter image description here

Что я хочу сделать:

Я бы хотел "дискретизировать" пространство кадра стимулов на кластеры:

Ниже приведено визуальное представление (выполнено в PPT) с различными кластерами (2,4,16,64).

Цветная часть, представляющая кластеры, в которых произошли фиксации:

enter image description here

С этим я хочу

-Счет количества фиксаций в каждом кластере.

- Вычислить наличие / количество или продолжительность, наблюдаемую в каждом кластере.

Матричная форма легко позволила бы мне сравнивать различные фиксации дисплея по вычитанию.

Итак, вопрос (ы)

-Как я могу создать гибкий механизм для разделения системы стимулов на кластеры.

-Попишите фиксации на эти кластеры, получив прямоугольную матрицу, заполненную нулями или счетчиками фиксаций или общей продолжительностью фиксаций для каждой ячейки матрицы.

Мне кажется, этот вопрос может быть неясным, и я отредактирую его, чтобы уточнить что-либо необходимое. Кроме того, вы думаете, что это нужно задать в 2-х отдельных вопросах, я рад это сделать.

Большое спасибо заранее за любую помощь, которую вы можете оказать.

Ответы [ 3 ]

8 голосов
/ 20 августа 2011

Вы можете сделать что-то вроде:

createMatrix[list_, frameXYs_, partitX_, partitY_, fun_] :=
 Module[{matrix},
  (*init return matrix*)
  matrix = Array[0 &, {partitX, partitY}];
  (matrix[[
    IntegerPart@Rescale[#[[1]], {frameXYs[[1, 1]], frameXYs[[2, 1]]}, {1,partitX}],
    IntegerPart@Rescale[#[[2]], {frameXYs[[1, 2]], frameXYs[[2, 2]]}, {1,partitY}]
         ]] += fun[#[[3]]]) & /@ list;

  Return@(matrix[[1 ;; -2, 1 ;; -2]]);]

Где fun - функция подсчета в третьем измерении вашего списка.

Итак, если вы хотите посчитать вхождения:

fix = {{20.14, 15.22, 774.}, {20.26, 15.37, 518.}, {25.65, 16.22, 200.}, 
       {28.15, 11.06, 176.}, {25.25, 13.38, 154.}, {24.78, 15.74, 161.}, 
       {24.23, 16.58, 121.}, {20.06, 13.22, 124.}, {24.91, 15.8,  273.}, 
       {24.32, 12.83, 119.}, {20.06, 12.14, 366.}, {25.64, 18.22, 236.}, 
       {24.37, 19.2, 177.},  {21.02, 16.4, 217.},  {20.63, 15.75, 406.}};
FrameXYs = {{4.32, 3.23}, {35.68, 26.75}};

cm = createMatrix[fix, FrameXYs, 10, 10, 1 &]
MatrixPlot@cm
MatrixForm@cm

enter image description here

А если вы хотите добавить время фиксации

cm = createMatrix[fix, FrameXYs, 10, 10, # &]
MatrixPlot@cm
MatrixForm@cm

enter image description here

Редактировать

Внесение некоторых изменений в индексы, немного приукрашивания кода, более понятный пример:

createMatrix[list_, frameXYs_, partit : {partitX_, partitY_}, fun_] :=
 Module[{matrix, g},
  (*Define rescale function*)
  g[i_, l_] := IntegerPart@
                   Rescale[l[[i]], (Transpose@frameXYs)[[i]], {1, partit[[i]]}];
  (*Init return matrix*)
  matrix = Array[0 &, {partitX + 1, partitY + 1}];
  (matrix[[g[1, #], g[2, #]]] += fun[#[[3]]]) & /@ list;
  Return@(matrix[[1 ;; -2, 1 ;; -2]]);]

.

fix = {{1, 1, 1}, {1, 3, 2}, {3, 1, 3}, {3, 3, 4}, {2, 2, 10}};
FrameXYs = {{1, 1}, {3, 3}};
cm = createMatrix[fix, FrameXYs, {7, 7}, # &];
MatrixPlot@cm
Print[MatrixForm@SparseArray[(#[[1 ;; 2]] -> #[[3]]) & /@ fix], MatrixForm@cm]

enter image description here

4 голосов
/ 20 августа 2011

Есть пара вещей, которые нужно сделать, чтобы достичь желаемого. Во-первых, учитывая количество делений, мы должны разделить 2-мерное пространство. Во-вторых, используя счетчик делений, нам нужен гибкий метод группировки фиксаций в соответствующие места. Наконец, мы генерируем любую статистику, которая вам нужна.

Что касается подсчета делений, встроенная функция FactorInteger почти делает то, что вам нужно. Например,

(* The second parameter is the upper limit for the number of factors returned *)
FactorInteger[35,2] == {{5,1}, {7,1}}
FactorInteger[64,2] == {{2,6}}

К сожалению, вы можете указать только верхний предел для числа возвращаемых факторов, поэтому мы должны немного изменить вывод

Clear[divisionCount]
divisionCount[c_Integer?(Positive[#] && (# == 2 || ! PrimeQ[#]) &)] :=
With[{res = FactorInteger[c, 2]},
 Power @@@ If[ 
     Length[res] == 2, 
     res // Reverse,
     With[
       {q = Quotient[res[[1, 2]], 2], r = Mod[res[[1, 2]], 2], 
        b = res[[1, 1]]},
       {{b, q + r}, {b, q}}
     ]
 ]
] 

Это делает две вещи, заменяет {{b,m}} на {{b, m / 2 + m mod 2}, {b, m / 2}}, где / представляет целочисленное деление (т.е. есть остатки) и преобразует {{b, m} ..} в {b^m ..} через Power @@@. Это дает

divisionCount[32] == {8, 4}
divisionCount[64] == {8, 8}.

Оказывается, мы можем получить количество фиксаций в этот момент с минимальной дополнительной работой через BinCounts следующим образом

BinCounts[fix[[All,;;2]], (* stripping duration from tuples *)
  {xmin, xmax, (xmax - xmin)/#1,
  {ymin, ymax, (ymax - ymin)/#2]& @@ divisionCount[ divs ]

, где вам нужно указать диапазоны для x и y и количество делений. Однако это не так гибко, как могло бы быть. Вместо этого мы будем использовать SelectEquivalents.

Ключом к эффективному использованию SelectEquivalents является создание хорошей функции категоризации. Для этого нам нужно самим определить деления следующим образом

Clear[makeDivisions]
makeDivisions[
 {xmin_, xmax_, xdivs_Integer?Positive}, {ymin_, ymax_, ydivs_Integer?Positive}] :=
   Partition[#,2,1]& /@ {
     (xmax - xmin)*Range[0, xdivs]/xdivs + xmin,
     (ymax - ymin)*Range[0, ydivs]/ydivs + ymin
   }

makeDivisions[
       {xmin_, xmax_}, {ymin_, ymax_}, 
       divs_Integer?(Positive[#] && (# == 2 || ! PrimeQ[#]) &)] :=
 makeDivisions[{xmin, xmax, #1}, {ymin, ymax, #2}] & @@ divisionCount[divs]

, где

makeDivisions[{0, 1}, {0, 1}, 6] == 
 {{{0, 1/3}, {1/3, 2/3}, {2/3, 1}}, {{0, 1/2}, {1/2, 1}}}.

(я бы использовал FindDivisions, но он не всегда возвращает количество запрошенных вами делений.) makeDivisions возвращает два списка, где каждый термин в каждом списке является минимальным-максимальным пара, которую мы можем использовать, чтобы определить, попадает ли точка в корзину.

Поскольку мы находимся на квадратной решетке, нам нужно проверить все пары пределов, которые мы только что определили. Я бы использовал следующее

Clear[inBinQ, categorize]
inBinQ[{xmin_,xmax_}, {ymin_, ymax_}, {x_,y_}]:= 
   (xmin <= x < xmax) && (ymin <= y < ymax)

categorize[{xmin_, xmax_}, {ymin_, ymax_}, divs_][val : {x_, y_, t_}] := 
 With[{bins = makeDivisions[{xmin, xmax}, {ymin, ymax}, divs]}, 
  Outer[inBinQ[#1, #2, {x, y}] &, bins[[1]], bins[[2]], 1]] //Transpose

, который возвращает

categorize[{0,1},{0,1},6][{0.1, 0.2, 5}] ==
 {{True, False, False}, {False, False, False}}.

Обратите внимание, координата y обращена по сравнению с графиком, низкие значения находятся в начале массива. Чтобы «исправить» это, Reverse bins[[2]] в categorize. Кроме того, вы захотите удалить Transpose до предоставления результатов в MatrixPlot, так как он ожидает результаты в нетранспонированной форме.

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

SelectEquivalents[ 
 fix, 
 (categorize[{xmin, xmax}, {ymin, ymax}, 6][#] /. {True -> 1, False -> 0} &), 
 #[[3]] &, (* strip off all but the timing data *)
{#1, #2} &],

получаем

{{
  {{0, 0, 0}, {0, 1, 0}}, {774., 518., 161., 121., 273., 177., 217., 406.}
 }, 
 {
  {{0, 0, 0}, {0, 0, 1}}, {200., 236.}
 }, 
 {
  {{0, 0, 1}, {0, 0, 0}}, {176., 154.}
 }, 
 {
  {{0, 1, 0}, {0, 0, 0}}, {124., 119., 366.}
 }}}

где первый член в каждом подсписке является матричным представлением ячейки, а второй член - это список точек, попадающих в эту ячейку. Чтобы определить, сколько в каждой корзине,

Plus @@ (Times @@@ ({#1, Length[#2]} & @@@ %)) ==
 {{0, 3, 2}, {0, 8, 2}}

Или время

Plus @@ (Times @@@ ({#1, Total[#2]} & @@@ %)) ==
 {{0, 609., 330.}, {0, 2647., 436.}}

Редактировать : Как вы можете видеть, чтобы получить любую статистику, необходимую вам для фиксаций, вам нужно просто заменить либо Length или Total. Например, вам может потребоваться среднее (Mean) потраченное время, а не только общее.

3 голосов
/ 21 августа 2011

В зависимости от вычислений, которые вы выполняете, и точности данных, вам может потребоваться более мягкий подход.Рассмотрите возможность использования передискретизации изображения для этого.Вот один из возможных подходов.Здесь есть очевидная неясность, но, опять же, это может быть желательно.Вот попытка быть конструктивной.Это мой взгляд на прекрасное решение Велисария.Я чувствую, что это немного чище.

createMatrix[list_, {{x1_, y1_}, {x2_, y2_}}, par:{pX_, pY_}, fun_] :=
 Module[{matrix, quant},
    matrix = 0 ~ConstantArray~ par;
    quant = IntegerPart@Rescale@## &;
    (matrix[[
         quant[#1, {x1, x2}, {1, pX}],
         quant[#2, {y1, y2}, {1, pY}]
           ]] += fun[#3] &) @@@ list;
    Drop[matrix, -1, -1]
 ]

Обратите внимание, что синтаксис немного отличается: квантовать разделы x и y в списке.Я чувствую, что это больше согласуется с другими функциями, такими как Array.

...