1
$\begingroup$

How to pick elements of a matrix that are inside a circle centered at the center of the matrix?

$\endgroup$
2
  • 2
    $\begingroup$ Please add the code you already have tried and the results, if any, that you already have obtained. $\endgroup$
    – bbgodfrey
    Mar 22, 2016 at 12:00
  • $\begingroup$ Welcome to Mathematica.SE! I hope you will become a regular contributor. To get started, 1) take the introductory tour now, 2) when you see good questions and answers, vote them up by clicking the gray triangles, because the credibility of the system is based on the reputation gained by users sharing their knowledge, 3) remember to accept the answer, if any, that solves your problem, by clicking the checkmark sign, and 4) give help too, by answering questions in your areas of expertise. $\endgroup$
    – bbgodfrey
    Mar 22, 2016 at 12:01

4 Answers 4

5
$\begingroup$

You can use Select to choose the elements which are within a circular distance from the center. Let's assume you have a square matrix,

nElements = 12;
radius = 4;
matrix = RandomReal[1, {nElements, nElements}];
center = (Dimensions@matrix + 1)/2;

Then you can generate a list of the matrix elements with

Flatten[Array[{#1, #2} &, {nElements, nElements}], 1]
(* {{1,1},{1,2},{1,3},{1,4},{1,5},{1,6},{1,7},{1,8},{1,9},{1,10},{1,11},{1,12},{2,1},{2,2},{2,3},{2,4},{2,5},{2,6},{2,7},{2,8},{2,9},{2,10},{2,11},{2,12},{3,1},{3,2},{3,3},{3,4},{3,5},{3,6},{3,7},{3,8},{3,9},{3,10},{3,11},{3,12},{4,1},{4,2},{4,3},{4,4},{4,5},{4,6},{4,7},{4,8},{4,9},{4,10},{4,11},{4,12},{5,1},{5,2},{5,3},{5,4},{5,5},{5,6},{5,7},{5,8},{5,9},{5,10},{5,11},{5,12},{6,1},{6,2},{6,3},{6,4},{6,5},{6,6},{6,7},{6,8},{6,9},{6,10},{6,11},{6,12},{7,1},{7,2},{7,3},{7,4},{7,5},{7,6},{7,7},{7,8},{7,9},{7,10},{7,11},{7,12},{8,1},{8,2},{8,3},{8,4},{8,5},{8,6},{8,7},{8,8},{8,9},{8,10},{8,11},{8,12},{9,1},{9,2},{9,3},{9,4},{9,5},{9,6},{9,7},{9,8},{9,9},{9,10},{9,11},{9,12},{10,1},{10,2},{10,3},{10,4},{10,5},{10,6},{10,7},{10,8},{10,9},{10,10},{10,11},{10,12},{11,1},{11,2},{11,3},{11,4},{11,5},{11,6},{11,7},{11,8},{11,9},{11,10},{11,11},{11,12},{12,1},{12,2},{12,3},{12,4},{12,5},{12,6},{12,7},{12,8},{12,9},{12,10},{12,11},{12,12}} *)

And then choose those elements which are within a certain radius of the center, by using Norm to compute the distance and Select to choose the elements,

circleElements = 
 Select[Flatten[Array[{#1, #2} &, {nElements, nElements}], 1], 
  Norm[# - center] <= radius &]

(some of you may be thinking it would be faster to use a circular region and then apply RegionFunction or Element to it, and you would be wrong).

You can use Extract to grab those elements which are within the circular area,

Extract[matrix, circleElements]
(*{0.0992023,0.495438,0.414766,0.120624,0.359152,0.886304,0.852727,0.456035,0.870987,0.818563,0.339816,0.809685,0.776808,0.307547,0.831416,0.19712,0.200391,0.505536,0.879347,0.155195,0.876139,0.703026,0.335968,0.912485,0.458875,0.220671,0.723213,0.565869,0.601153,0.737624,0.990036,0.428771,0.931505,0.905728,0.303853,0.612953,0.00655347,0.929123,0.837107,0.786894,0.308131,0.377017,0.17553,0.669582,0.914713,0.802992,0.702451,0.304244,0.104606,0.726116,0.352679,0.288495} *)

Finally you can use ReplacePart to change those elements to some other value,

{MatrixPlot[matrix],
 MatrixPlot[ReplacePart[matrix, circleElements -> 0]]}

enter image description here

Or, if you want to keep only those elements inside the circle, I can think of two ways to do it. First, you could do like above but change the $\leq$ to a $>$,

outsideCircleElements = 
  Select[Flatten[Array[{#1, #2} &, {nElements, nElements}], 1], 
   Norm[# - center] > radius &];
MatrixPlot[ReplacePart[matrix, outsideCircleElements -> 0]]

enter image description here

Or you could create an array of zeroes, and then change the center elements to the values for the original matrix,

newmatrix = ConstantArray[0, Dimensions@matrix];
Set[newmatrix[[#1, #2]], matrix[[#1, #2]]] & @@@ circleElements;
MatrixPlot[newmatrix]

Both give the same result.

You can apply this to larger matrices,

![enter image description here

$\endgroup$
7
  • $\begingroup$ Thanks for the answer. I actually want to extract those values which are in the circle instead of assigning them as zeros. How can I do that? I used EXTRACT by the way. $\endgroup$
    – faf
    Mar 22, 2016 at 12:34
  • $\begingroup$ @faf - see the edit, you want to use Extract $\endgroup$
    – Jason B.
    Mar 22, 2016 at 12:37
  • $\begingroup$ D'oh, didn't fully read your comment I reckon - so my point was that you can use the code above to create a list of the element positions which are within a circular area of the center. So the line Extract[matrix, circleElements] gives you these in a list. Is that what you were going for? $\endgroup$
    – Jason B.
    Mar 22, 2016 at 12:44
  • $\begingroup$ How can I assign these new extracted values to a new matrix with dimenssion of the first matrix and assign the non-circleElement to zero?(So one sees the position of circleElements in a matrix) $\endgroup$
    – faf
    Mar 22, 2016 at 12:45
  • $\begingroup$ @faf - see the edit, I think this should do what you are asking. $\endgroup$
    – Jason B.
    Mar 22, 2016 at 13:25
8
$\begingroup$

Why reinvent the wheel? The built-in function DiskMatrix returns a matrix whose values are 1s within a particular radius of its center:

diskmat = DiskMatrix[3, {10, 20}];
diskmat // MatrixForm

enter image description here

You can then use Pick to select the appropriate elements from the input matrix, and Flatten to turn them into a simple list. Packaging it all up:

selectdiskmatrix[mat_, r_] := Flatten[Pick[mat, DiskMatrix[r, Dimensions[mat]], 1]]

testmat = Table[i + j, {i, 1, 10}, {j, 1, 20}];
selectdiskmatrix[testmat, 3]

(* {12, 13, 14, 15, 12, 13, 14, 15, 16, 17, 13, 14, 15, 16, 17, 18, 14, 
    15, 16, 17, 18, 19, 15, 16, 17, 18, 19, 20, 17, 18, 19, 20} *)

This works for both square and non-square matrices. If you wanted an off-center disk, you would have to create a disk matrix and then pad it with zeroes appropriately; but other than that, the same technique should work.

$\endgroup$
4
  • $\begingroup$ Didn't know about DiskMatrix! Also Pick should be very fast here. $\endgroup$ Mar 22, 2016 at 15:06
  • $\begingroup$ @MariusLadegårdMeyer: Yeah, the primary use of DiskMatrix is (I think) in image processing, which makes its intended use somewhat specialized. But that doesn't stop us from using it here. :-) $\endgroup$ Mar 22, 2016 at 15:10
  • $\begingroup$ @MichaelSeifert - I really like this, better than my answer, but I don't know that Pick is doing what the OP is looking for (it wasn't clear in the original question). I think that would be achieved by selectdiskmatrix[mat_, r_] := ReplacePart[mat, Position[DiskMatrix[r, Dimensions@mat], 0] -> 0] $\endgroup$
    – Jason B.
    Mar 23, 2016 at 8:48
  • 1
    $\begingroup$ @JasonB: Actually, the easier way to get that output would be to just use element-by-element multiplication: mat * DiskMatrix[r, Dimensions@mat] . $\endgroup$ Mar 23, 2016 at 19:04
2
$\begingroup$

Here is an approach that is definitely less readable and intuitive than JasonB's answer, but a good deal faster, especially if the radius is small. Of course speed may not be important in this case, but I'm posting it anyway :)

The idea is to create a ragged Table where, for each row i, the included columns range from jmin to jmax given by the solutions to the disk edge equation

(j - jc)^2 == radius^2 - (i - ic)^2

where ic, jc are the center positions. The solutions will be

jmin = Ceiling[- Sqrt[radius^2 - (i - ic)^2] + jc]
jmax = Floor[- Sqrt[radius^2 - (i - ic)^2] + jc]

Ceiling and Floor makes sure we are inside the disk and that the indices are in fact integers.

The matrix elements are found by the function

elements[mat_, center_, radius_] := 
Block[{ic, jc},
  {ic, jc} = center;
  Table[mat[[i, Ceiling[-# + jc] ;; Floor[# + jc]]] & @
    Sqrt[radius^2 - (i - ic)^2]
  , {i, Ceiling[-radius + ic], Floor[radius + ic]}
  ]
]

and the positions by

indices[mat_, center_, radius_] :=
Block[{ic, jc, row},
  {ic, jc} = center;
  Flatten[
    Table[(row = Range[Ceiling[-# + jc], Floor[# + jc]]; 
      Transpose[{ConstantArray[i, Length@row], row}]) &[
      Sqrt[radius^2 - (i - ic)^2]],
    {i, Ceiling[-radius + ic], Floor[radius + ic]}
    ]
  , 1]
]

The reason it is faster is that the index i only runs over those rows that are actually inside the disk, so especially for small values of radius, this is faster than running through all rows. Also we do not need to perform any testing with this algorithm.

As with JasonB's answer, this also works fine for non-square matrices:

radius = 30;
matrix = RandomReal[1, {200, 400}];
center = {150, 250}

First@AbsoluteTiming[ind = indices[matrix, center, radius]];
(* 0.003351 *)

First@AbsoluteTiming[circleElements = 
  Select[Flatten[Array[{#1, #2} &, {200, 400}], 1], 
  Norm[# - center] <= radius &];
(* 1.641141 *)

The plots produced are of course identical: enter image description here

$\endgroup$
0
$\begingroup$

You can access an entry (n,m) of a Matrix by

M[[n,m]]

So you could get the circle around the coordinates "center" of radius r by

{M[[center+r,center-r]],M[[center+r,center]],M[[center+r,center+r]]}, 

and so on.

Or as a function

circle[M_, center_, radius_] :=Table[M[[n, m]], {n, center - radius,center + radius}, {m,center - radius, center + radius}]
$\endgroup$
1
  • $\begingroup$ This produces a new square, with corners located at the limits of your Table iterator specs... $\endgroup$ Mar 22, 2016 at 13:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.