Transforming a coordinate system into a matrix in Mathematica

In anticipation of the programming issue, I believe that I need to give some information on what I am doing to facilitate an understanding of my problem:

I record eye movements showing some patterns to subjects. As a result of the experiment, I later discovered some symmetrical transformation of these patterns.

enter image description here

What I get are lists of commit coordinates and duration:

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

Where:

- fix1X is the X coordinate for the first fix.

- fix1Y - Y coordinate for the first fix.

- fix1D is the duration in milliseconds of commits

Note:

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

Below are the fixations for 1 display (Fixing the theme during the presentation of 3s stimuli on the screen)

 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

What I want to do:

I would like to “discretize” the stimulus frame space into clusters:

Below is a visual representation (made in PPT) with various clusters (2,4,16,64).

The colored part representing the clusters in which the fixation occurred:

enter image description here

With this i want

-Take into account the number of commits within each cluster.

-Check for the presence / quantity or duration observed in each cluster.

The matrix form will easily allow me to compare different display fixes by subtraction.

So the question (s)

-How can I create a flexible mechanism for dividing the stimulus framework into clusters.

-Configure fixations on these clusters, getting a rectangular matrix filled with 0s or fix counts or the duration of a complete fix for each of the matrix cells.

I feel this question may be unclear and will edit it to clarify everything that is needed. Besides, what do you think should be asked in two separate questions, I am glad to do it.

Thanks a lot for any help you could provide.

+6
source share
3 answers

You can do something like:

 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]]);] 

Where fun is the count function in the third dimension of your list.

So, if you want to count the occurrences:

 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

And if you want to add commit time

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

enter image description here

Edit

Making some changes to indexes, slightly embellishing the code, a clearer example:

 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

+8
source

There are a few things you need to do to accomplish what you want. First, given the division count, we must separate the two-dimensional space. Secondly, using the division count, we need a flexible method of grouping fixations into their corresponding points. Finally, we generate any statistics you need.

As for the number of divisions, the built-in FactorInteger function almost does what you need. For instance,

 (* 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}} 

Unfortunately, you can specify only the upper limit of the number of returned factors, so we need to slightly change the result

 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}} ] ] ] 

This does two things, replaces {{b,m}} with {{b, m / 2 + m mod 2}, {b, m / 2}} , where / represents integer division (that is, there are residues) and converts {{b, m} ..} to {b^m ..} via Power @@@ . This gives

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

It turns out that we can get a fixation account at this point with minimal additional work through BinCounts as follows

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

where you need to specify ranges for x and y and the number of divisions. However, this is not as flexible as it could be. Instead, we will use SelectEquivalents .

The key to using SelectEquivalents effectively is to create a good categorization function. To do this, we need to determine the divisions themselves, as follows

 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] 

Where

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

(I would use FindDivisions , but it does not always return the number of queries you request.) makeDivisions returns two lists, where each member in each list is a min-max pair, which we can use to determine if a point falls into the cell .

Since we are on a square grid, we need to check all the pairs of limits that we just defined. I would use the following

 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 

which returns

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

Please note that the y-coordinate is inverted compared to the graph, low values ​​are at the beginning of the array. To “fix” this, Reverse bins[[2]] in categorize . In addition, you will want to remove Transpose before submitting the results to MatrixPlot , as it expects results in untransformed form.

Using

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

we get

 {{ {{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.} }}} 

where the first term in each sublist is the matrix representation of the hopper, and the second term is a list of points that fall into this bit. To determine how many are in each box,

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

Or timings

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

Change As you can see, to get any statistics needed for commits, you just need to replace either Length or Total . For example, you may need Mean Time, not just General.

+4
source

Depending on the calculations performed and the accuracy of the data, you may want to take a softer approach. Consider re-sampling the image for this. Here is one possible approach. There is a clear fuzziness here, but again, which may be desirable.

 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}}; Graphics[{ AbsolutePointSize@Sqrt @#3, Point[{#, #2}]} & @@@ fix, BaseStyle -> Opacity[0.3], PlotRange -> Transpose@FrameXYs , PlotRangePadding -> None, ImageSize -> {400, 400}] ImageResize[%, {16, 16}]; Show[ ImageAdjust@ %, ImageSize -> {400, 400}] 

scaled dots

resampled raster


As can be seen from the above, it is useless, here is an attempt to be constructive. This is my approach to the great solution of the great man. I feel it is a little cleaner.

 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] ] 

Note that the syntax is slightly different: quantize sections x and y are given in the list. I feel this is more consistent with other functions like Array .

+3
source

Source: https://habr.com/ru/post/895490/


All Articles