15

To start, I have a situation where I have some matrix, for example

$$ A=\left[ \begin{matrix} 4&2&2&3&3\\ 2&3&1&2&3\\ 3&0&4&0&4\\ 1&4&1&1&2\\ 1&3&4&1&4\\ \end{matrix} \right] $$

and I would like to count how many adjacent elements there are. Adjacent elements can be up, down, left or right. For a pair to be valid the numbers have to have the same value. For example $\left(A_{1,2},A_{1,3}\right)$ is a valid pair because the are both $2$ and they are next to each other. I need a way to count the defined adjacent element pairs on a matrix of size $n$.

eg.

$$ \left[ \begin{matrix} 2&2&3\\ 3&2&3\\ 2&1&1 \end{matrix} \right] $$

There would be 4 pairs in this matrix.

I thought about converting the matrix into some sort of graph with "weighted vertices" however I had no clue on doing so. It would have then made it a matter of counting arcs. So how would one produce a function that takes in a matrix and spits out the number of pairs (by my definition) it contains?

I am unsure whether or not I have tagged this correctly.

user64494
  • 26,149
  • 4
  • 27
  • 56
Ali Caglayan
  • 563
  • 1
  • 5
  • 10
  • Question is not clear to me, e.g., would the 2-4 elements of which there are four pairings in your example count as 1 total? – ciao Jan 16 '14 at 21:03
  • @rasher Apologies about ambiguity I changed it. I also added an example – Ali Caglayan Jan 16 '14 at 21:05
  • 2
    If I understand correctly, this should accomplish the count you want. I treat a row, e.g, of x,x,x as two pairs. Try it and reply...

    (Count[{Differences /@ #}, 0, Infinity] + Count[{Differences /@ Transpose[#]}, 0, Infinity]) &[yourMatrixHere]

    – ciao Jan 16 '14 at 21:28
  • 5
    A one-liner: Count[Flatten@{Differences[a, {1,0}], Differences[a, {0,1}]}, 0], where the matrix is a. – Szabolcs Jan 16 '14 at 21:46
  • @Szabolcs Would it be possible to use this method to determine, in a matrix defined as mat = RandomChoice[{0, 1, 2}, {5, 5}], the number of 1s that have 0s adjacent to them (i.e. up, down, left, right)? – jbl Feb 11 '21 at 22:56

7 Answers7

11

This method generates all neighboring position-pairs (according to nontoroidal Neumann neighborhood) and then checks whether any of these position-pairs is a valid pair (i.e. identical) or not.

size= 5;
mat = RandomInteger[{1, 5}, {size, size}];
close = Cases[Tuples[Tuples[Range@size, {2}], {2}],
         {{a_, b_}, {c_, d_}} /; (a==c && b==d-1) || (a==c-1 && b==d)]
pairs = Cases[close, _?(SameQ @@ Extract[mat, #] &)]

Grid[mat, Background -> {None, None, Thread[# -> Hue[.66, .2]]}] & /@ pairs
{{{1, 2}, {1, 3}}, {{1, 4}, {1, 5}}, {{2, 2}, {2, 3}}, {{2, 4},
    {2, 5}}, {{2, 4}, {3, 4}}, {{3, 4}, {4, 4}}, {{5, 4}, {5, 5}}}

Mathematica graphics

István Zachar
  • 47,032
  • 20
  • 143
  • 291
8

This uses pattern-based rather than numeric methods and therefore will not be highly efficient, but I like the style.

f[a_?MatrixQ] :=
  Module[{h, pad},
    h[{{i_, x_}, {y_, _}}] := Count[{x, y}, i];
    Developer`PartitionMap[h, a, {2, 2}, 1, 1, pad] ~Total~ 2
  ]

Test:

a = {{4, 2, 2, 3, 3}, {2, 3, 1, 2, 3}, {3, 0, 4, 0, 4}, {1, 4, 1, 1, 2}, {1, 3, 4, 1, 4}};
m = {{2, 2, 3}, {3, 2, 3}, {2, 1, 1}};

f /@ {a, m}
{6, 4}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
7

Following @rasher 's idea, you could do

countAdjRows=Count[{Differences @ #}, 0, Infinity]&;

countAdj=countAdjRows@# + countAdjRows@Transpose@# &

Or, maybe speed it a little bit by counting with Unitize as @belsarius's superbly suggests

countAdj= -Total[Unitize@Differences@# - 1 & /@ {#, Transpose@#}, Infinity]&;

Or @Szabolcs magestic one-liner

countAdj= Count[Flatten@{Differences[#, {1,0}], Differences[#, {0,1}]}, 0]&

Some Code Golf:

Count[Differences /@ {#, #\[Transpose]}, 0, 3] &
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
Rojo
  • 42,601
  • 7
  • 96
  • 188
  • You don't need to map Differences, you can use it directly. E.g. Count[Flatten@Differences[a], 0]. – Szabolcs Jan 16 '14 at 21:45
  • @Szabolcs it just counts in the other direction, which is perfect anyway. Editing – Rojo Jan 16 '14 at 21:47
  • 1
    -Total[Unitize@Differences@# - 1 & /@ {m, Transpose@m}, 3] – Dr. belisarius Jan 16 '14 at 21:47
  • @belisarius I expected that subtracting one to a whole matrix would be slower but no. I don't deserve any credits for this – Rojo Jan 16 '14 at 21:49
  • I wonder if there isn't a ListConvolve kernel that can count both horiz and vert matches in one pass – Dr. belisarius Jan 16 '14 at 21:54
  • @belisarius I took a crack at that. I like it well enough but it's about two orders of magnitude slower than yours. -- Actually I just realized that my implementation would be a lot simpler merely using PartitionMap, so I'll rewrite it using that. – Mr.Wizard Jan 17 '14 at 13:25
7

Position of pairs:

posf[u_] := Module[{a, at, p1, p2},
  {a, at} = Position[Differences /@ #, 0] & /@ {u, Transpose@u};
  p1 = {#, # + {0, 1}} & /@ a;
  p2 = {#, # + {1, 0}} & /@ (Reverse /@ at);
  Join[p1, p2]]

Test matrix:

mat={{3, 5, 2, 2, 5}, {4, 2, 2, 1, 3}, {1, 2, 2, 1, 1}, {1, 1, 4, 1, 
  5}, {3, 2, 2, 3, 4}};

enter image description here

posf[mat]

yields:

{{{1, 3}, {1, 4}}, {{2, 2}, {2, 3}}, {{3, 2}, {3, 3}}, {{3, 4}, {3, 
   5}}, {{4, 1}, {4, 2}}, {{5, 2}, {5, 3}}, {{3, 1}, {4, 1}}, {{2, 
   2}, {3, 2}}, {{1, 3}, {2, 3}}, {{2, 3}, {3, 3}}, {{2, 4}, {3, 
   4}}, {{3, 4}, {4, 4}}}

Visualizing using:

Map[Function[x, 
  Grid[mat, Background -> {None, None, # -> LightRed & /@ x}]]
 , posf[mat]]

enter image description here

To obtain the count:

Length@posf[mat]

In this case yielding: 12.

ubpdqn
  • 60,617
  • 3
  • 59
  • 148
5

Revision notice

Thanks to E. Chan-López commets I added Overlaps -> True

a = {{2, 2, 3}, {3, 2, 3}, {2, 1, 1}};
b = {{4, 2, 2, 3, 3}, {2, 3, 1, 2, 3}, {3, 0, 4, 0, 4}, {1, 4, 1, 1, 2}, {1, 3, 4, 1, 4}};

CountAdjacentElements[m_?MatrixQ] := With[{c = SequenceCount[#, {x_, x_}, Overlaps->True] &}, Total[{c /@ m, c /@ Transpose[m]}, 2]]

CountAdjacentElements /@ {a,b}

{4, 6}

eldo
  • 67,911
  • 5
  • 60
  • 168
  • It seems that SequenceCount is not counting two sequences in the case of matrix c (@kglr) and I don't know what is happening, because if you check with SequencePosition it works: Length@Catenate[SequencePosition[#, {x_, x_ ..}] & /@ Join[#, Transpose@#] &@c] – E. Chan-López Dec 21 '23 at 06:45
  • I know what you missed, you're not counting the overlaps: Overlaps -> True – E. Chan-López Dec 21 '23 at 06:52
  • 1
    Thank you, I added a revision notice – eldo Dec 21 '23 at 08:30
  • Thanks to you too, @eldo. I've followed the use of functions for sequences because you used them in many answers. :-) – E. Chan-López Dec 21 '23 at 08:37
5
adjPairsCount = 
  Total @
  Map[Split /* Map[Length @ # - 1 &] /* Splice] @ 
  Join[#, Transpose @ #] &;

Examples:

Using input examples a and b from eldo's answer:

a = {{2, 2, 3}, {3, 2, 3}, {2, 1, 1}};

b = {{4, 2, 2, 3, 3}, {2, 3, 1, 2, 3}, {3, 0, 4, 0, 4}, {1, 4, 1, 1, 2}, {1, 3, 4, 1, 4}};

c = {{4, 2, 2, 2, 3, 3}, {2, 3, 1, 2, 2, 3}, {3, 0, 4, 2, 0, 4}, {1, 4, 1, 0, 1, 2}, {1, 3, 4, 2, 1, 4}};

adjPairsCount /@ {a, b, c}

{4, 6, 9}

Visualization:

adjPairs = Map[
  ReplaceAll[{{_?NumericQ} -> -1, a : {_?NumericQ, __} :> Splice[a]}] @*
  Split];

markAdjPairs = MapThread[Max, {Transpose @ adjPairs @ Transpose @ # , adjPairs @ #}, 2] &;

labeledMatrixPlot = MatrixPlot[markAdjPairs @ #, PlotLabel -> Style[PromptForm["adjacent Pairs Count", adjPairsCount@#], 16], Mesh -> All, ColorRules -> {_?Negative -> White}, Epilog -> MapIndexed[Text[Style[#, 20], #2 - 1/2] &, Transpose @ Reverse @ #, {2}], ##2] &;

Row[labeledMatrixPlot[#, ImageSize -> 300, Frame -> False, MeshStyle -> Directive[LightGray, Thick]] & /@ {a, b,c}, Spacer[20]]

enter image description here

kglr
  • 394,356
  • 18
  • 477
  • 896
  • 1
    A very nice and useful illustration, which tells us much more than the dull {4, 6} – eldo Nov 16 '23 at 14:14
2

Thanks to @eldo for inspiring the following answer using SequenceCases:

a = {{2, 2, 3}, {3, 2, 3}, {2, 1, 1}};

b = {{4, 2, 2, 3, 3}, {2, 3, 1, 2, 3}, {3, 0, 4, 0, 4}, {1, 4, 1, 1, 2}, {1, 3, 4, 1, 4}};

c = {{4, 2, 2, 2, 3, 3}, {2, 3, 1, 2, 2, 3}, {3, 0, 4, 2, 0, 4}, {1, 4, 1, 0, 1, 2}, {1, 3, 4, 2, 1, 4}};

CountAdjacentElements[m_?MatrixQ] := Total[ Flatten[ Function[ Map[ Function[

    SequenceCases[#, s : {x_, Repeated[x_]} :> (Length[s] - 1)]
                    ],
                Join[#, Transpose @ #]
            ]
        ][m]
    ]

];

CountAdjacentElements /@ {a, b, c}

({4, 6, 9})

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44