4
$\begingroup$

I have a list of points in 3D and I need to count how many times each point is repeated. For a similar problem with a list of characters many different solutions have been proposed in this post: solutions. Among those, I have here select this solution which is faster:

stringTally = Last @ Reap[Sow[1, #], _, {#, Tr@#2} &] &;
(* to be used as in this example to catch 4 repetitions *)
(* Cases[ stringTally @ list, {x_, 4}:>x] *)

Unfortunately I cannot understand how Reap works in stringTally. I tried to decompose it but the use of two pure functions make the expression a little bit complicated.

$\endgroup$
  • $\begingroup$ blog.wolfram.com/2011/04/20/… $\endgroup$ – Patrick Stevens Sep 30 '15 at 18:09
  • $\begingroup$ @PatrickStevens, I know how does it work and I have already read this blog. $\endgroup$ – BetterEnglish Sep 30 '15 at 18:18
  • $\begingroup$ If you use Trace on your list of character example (e.g. Cases[stringTally@list, {x_, 4} :> x] and at the same time carefully read the documentation on a separate window you should see how it works. $\endgroup$ – gwr Sep 30 '15 at 18:40
  • $\begingroup$ You might, I believe, also write stringTally a bit differently: stringTally = Last@Reap[Sow[1, ##], _, {#1, Tr@#2} &] &. So the ## will take the list as it is (with each element being a tag for Sow and the #1,#2 in the pure function at the end work on each collection as part or the Reap. Tr@#2 will simply give the total for a vector and each collection will have a number of 1s for each instance as its second part. Hope that helps -- sometimes Mma is very compact and fast but not quite readable for humans... $\endgroup$ – gwr Sep 30 '15 at 18:47
  • $\begingroup$ @gwr, there is only one argument (to stringTally) so # vs ## makes no difference. $\endgroup$ – george2079 Sep 30 '15 at 19:20
4
$\begingroup$

We can see from this expression

Reap[Sow[1, #] & /@ {1, 1, 2, 3}, _, f]
(* Out: {{1, 1, 1, 1}, {f[1, {1, 1}], f[2, {1}], f[3, {1}]}} *)

how Sow and Reap interact in general. In this example f is {#, Tr@#2} &. Tr in this context works just like Total but it can be faster. But the sum of the second part is just the sum of 1s, the number of times that the particular tag has been sown (because 1 is sown each time).

Please ask questions if you want a more thorough explanation.

$\endgroup$
  • 3
    $\begingroup$ The only thing I'd mention is that Reap is of the form Reap[expr, pattern, function] and here the pattern is _ which will match anything. Sow then organizes by tag and Reap applies the final function. $\endgroup$ – kale Sep 30 '15 at 18:25
1
$\begingroup$

The answer in the link you supplied indicates that the use of the Sow and Reap approach performs better than Tally when strings are involved.

However, to count the number of occurrences in your list of 3D points, Tally is actually better.

list = Partition[RandomInteger[15000, 300000], 3];

Tally@list // Timing // First

0.078 seconds on my machine while

Last@Reap[Sow[1, list], _, {#, Tr@#2} &] // Timing // First

takes 1.59 seconds.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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