7
$\begingroup$

I have the following lists Y and EY with actual and estimated outcomes.

Y = {-1., -1., 1., -1., -1., -1., 1., 1., -1., -1., -1., -1., -1., -1., \
-1., -1., -1., -1., -1., -1., 1., 1., -1., -1., -1., -1., 1., -1., \
1., -1., -1., -1., -1., -1., -1., -1., 1., -1., -1., -1., -1., -1., \
-1., -1., -1., -1., 1., -1., -1., 1., 1., -1., 1., 1., 1., -1., 1., \
-1., 1., -1., -1., -1., -1., 1., -1., 1., -1., -1., -1., 1., -1., \
-1., -1., 1., 1., 1., 1., 1., -1., 1., -1., 1., -1., -1., 1., 1., \
-1., -1., 1., 1., -1., -1., 1., 1., 1., -1., -1., -1., 1., -1., 1., \
-1., -1., 1., 1., 1., -1., -1., -1., -1., -1., -1., -1., -1., -1., \
-1., -1., 1., -1., 1., 1., -1., 1., -1., 1., -1., -1., 1., -1., -1., \
-1., -1., -1., -1., -1., -1., -1.}

EY = {-1., -1., -1., -1., -1., -1., 1., 1., -1., -1., -1., -1., -1., -1., \
-1., -1., -1., -1., -1., 1., 1., 1., -1., -1., -1., -1., 1., -1., 1., \
-1., -1., -1., -1., -1., 1., 1., 1., -1., -1., -1., -1., 1., -1., 1., \
-1., 1., -1., 1., -1., -1., 1., -1., 1., 1., 1., -1., 1., -1., 1., \
1., -1., -1., -1., 1., 1., 1., 1., -1., -1., -1., -1., -1., -1., 1., \
1., 1., 1., -1., -1., 1., -1., 1., 1., -1., 1., 1., -1., 1., 1., 1., \
-1., -1., 1., 1., 1., -1., -1., 1., 1., -1., 1., -1., -1., 1., 1., \
-1., -1., -1., 1., -1., -1., 1., 1., 1., -1., -1., -1., 1., -1., 1., \
1., -1., 1., -1., 1., -1., -1., 1., -1., -1., -1., -1., -1., -1., 1., \
1., -1.}

I want to calculate the recall metric. To do so I intuitively try this (for the shake of simplicity it is not the actual recall formula, just its numerator to make the question easier to follow):

recall = (Count[Y == 1.0 && EY == 1.0, True])
0

But when I do this, I can get the actual count:

Count[Thread[Thread[Y == 1.0] && Thread[EY == 1.0]], True]
38

Why the first expression is not applying == to each element?

$\endgroup$
4
  • 2
    $\begingroup$ == is the short form for Equal. Equal does not have the attribute Listable as seen from Attributes[Equal]. To see what the attribute listable means compare the outputs from the following two examples: $\endgroup$ Dec 27, 2022 at 20:54
  • $\begingroup$ Array[a, 4]~Function[{a, b}, a == b]~Array[b, 4] $\endgroup$ Dec 27, 2022 at 20:54
  • $\begingroup$ Array[a, 4]~Function[{a, b}, a == b, Listable]~Array[b, 4] $\endgroup$ Dec 27, 2022 at 20:54
  • 3
    $\begingroup$ I would discourage equality checking on floating-point numbers because it can be quite brittle in practice. From the documentation, "Approximate numbers with machine precision or higher are considered equal if they differ in at most their last seven binary digits (roughly their last two decimal digits)." So there is some room for numerical error, but not much. Better to round your numbers to the nearest integer before testing, or testing with a larger error range. $\endgroup$
    – Roman
    Dec 28, 2022 at 12:02

7 Answers 7

8
$\begingroup$

You could create a listable version of Equal:

ClearAll[listEqual]
SetAttributes[listEqual, Listable]
listEqual[seq__] := Equal[seq]

listEqual[{1, 2, 3}, {1, 2, 4}]
(* Out: {True, True, False} *)

This could also be used inline with the same results:

{1, 2, 3} ~ listEqual ~ {1, 2, 4}

With that in hand:

Thread[Y ~ listEqual ~ 1.0 && EY ~ listEqual ~ 1.0];
Count[%, True]

(* Out: 38 *)
$\endgroup$
1
  • $\begingroup$ Sometimes I've done things like this listEqual = Function[, Equal[##], Listable]. $\endgroup$
    – lericr
    Dec 27, 2022 at 21:58
7
$\begingroup$

Here is the way to fix origin code:

recall1 = Y // Map[EqualTo[1.0]]
recall2 = EY // Map[EqualTo[1.0]]
{recall1, recall2} // MapThread[And] // Count[True]

38

$\endgroup$
0
7
$\begingroup$

Another simple solution could be:

Count[Transpose[{Y, EY}], {1., 1.}]

(* Out: 38 *)

Since we are matching patterns, applying N to data is recommended, if you're not sure they are Real or Integer.

$\endgroup$
1
  • 2
    $\begingroup$ Or Counts[Transpose[{Y, EY}]] to get all combinations as an association: <|{-1., -1.} -> 74, {1., -1.} -> 6, {1., 1.} -> 38, {-1., 1.} -> 19|> $\endgroup$
    – Roman
    Dec 28, 2022 at 11:55
7
$\begingroup$

(1) Ramp and Dot

How about using Ramp and the Dot product?

Ramp[Y].Ramp[EY]
(* 38. *)

(I have interpreted the question as @Ben Izd and @Syed have done)

(2) MapThread

Count[MapThread[##==1.0&,{Y,EY}],True]
(* 38 *) 

(The above method is almost identical to that given below by @lericr, which was posted well before this answer)

(3) Thread

From the documentation for Thread, under "possible issues", it is pointed out that "Thread evaluates the whole expression before threading" but that MapThread "takes the function and its arguments separately".

It is also pointed out that suppressing evaluation can make Thread behave like MapThread.

Thread[Unevaluated[##==1.0]]&[Y,EY]//Count[True]
(* 38 *) 

(4) Count

Count[2.][Y+EY]
(* 38 *) 

(5) Inner

Inner[##==1.&,Y,EY,Count[True]@*List]
(* 38 *) 

This is likely to be much slower than the Dot product method as "Matrix multiplication using Dot is heavily optimized" (see this post by Carl Woll)

There is also:

 Inner[Times, Ramp@Y, Ramp@EY]
 (* 38. *) 
$\endgroup$
0
6
$\begingroup$

This expression,

Y == 1.0 && EY == 1.0

Is applying And to two expressions that aren't True/False. Look at what you get if you evaluate Y == 1.0. And is a logical operator, so it won't resolve if the the values aren't True/False (in general).

You might try something like this:

Count[MapThread[#1 == #2 == 1.0 &, {Y, EY}], True]

Or this:

Count[Pick[Y, EY, 1.], 1.]

Or maybe even this:

Tally[Pick[Y, EY, 1.]]
$\endgroup$
1
  • 1
    $\begingroup$ == is not Listable, so Y==1.0 just keep the form. $\endgroup$ Dec 27, 2022 at 20:54
4
$\begingroup$

As @Roman pointed out, the data are floats and a float-appropriate solution should be used (unless it is guaranteed that the data are integer floats, but this is not a condition stated in the problem).

Total[1 - Unitize[Threshold@Abs[ey - 1] + Threshold@Abs[y - 1]]]
(*  38  *)

From a comment by the OP:

The intended calculation is the numerator of the Recall metric, which focuses on how many positives the system detects out of the total number of actual positives...

Then use (if I interpreted the implication on the data correctly):

Total[UnitStep[EY] UnitStep[Y]]
(*  38  *)
$\endgroup$
3
$\begingroup$

Using Position:

Position[Y, 1.0] ⋂ Position[EY, 1.0] // Length

Other variants:

Thread[(# == 1.0 & /@ Y) && (# == 1.0 & /@ EY)] // Count[True]

Count[AllTrue[#, EqualTo[1.0]] & /@ Transpose[{Y, EY}], True]

Result

38

$\endgroup$

Your Answer

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

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