14
$\begingroup$

So I have 2 lists of 10000+ lists of 3 numbers, e.g.

{{1,2,3},{4,5,6},{7,8,9},...}
{{2,1,3},{4,5,6},{41,2,0},...}

Wanting a result like

{2,...}

Getting some sort of list of True/False is also probably enough, like this:

{False,True,False,...}

I guess I could use Position once I've done that.

I tried to use Thread, as below:

Thread[{{a, b}, {c, d}, {e, f}} == {{a, b}, {d, e}, {f, e}}]

which gives the True/False output

{True, {c, d} == {d, e}, {e, f} == {f, e}}

But as soon as there are actual numbers in place, it doesn't work:

Thread[{{1, 2}, {2, 3}, {4, 5}} == {{1, 3}, {2, 3}, {4, 5}}]

Returns

False

I'd really appreciate any help you could give.

Thanks,

H

$\endgroup$

4 Answers 4

10
$\begingroup$

One possible way of doing it:

a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {1, 2, 3}};
b = {{2, 1, 3}, {4, 5, 6}, {41, 2, 0}, {1, 2, 3}};
Position[MapThread[Equal, {a, b}], True]

The output reads: {{2}, {4}}

In my code, it compares the elements for each position, but it doesn't "cross check" them, i.e.it doesn't consider that the element n of list a could be equal to element m!=n of list b

EDIT: note that the solution provided by @Henrik Schumacher is much faster :)

a = RandomInteger[{0, 4}, {100000, 3}];
b = RandomInteger[{0, 4}, {100000, 3}];

RepeatedTiming@Position[MapThread[Equal, {a, b}], True][[1]]   
(*0.089*)

RepeatedTiming@
 Position[Unitize[Subtract[a, b]].ConstantArray[1, 
     Dimensions[a][[2]]], 0, 1][[1]]
(*0.0057*)
$\endgroup$
1
  • $\begingroup$ That's perfect and very simple. I tried to do a similar thing but I guess I messed up the syntax or something. Indeed I didn't want any cross-checking, comparing like-for-like position is an important part of my problem. Thanks so much! I'll mark as the answer as soon as I can $\endgroup$
    – H94
    Commented Jun 5, 2018 at 8:10
11
$\begingroup$
a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {1, 2, 3}};
b = {{2, 1, 3}, {4, 5, 6}, {41, 2, 0}, {1, 2, 3}};

Pick[Range@Length@a, Total /@ Unitize[Subtract[a, b]], 0]

{2,4}

Or using @Henrik's idea of using Dot in in the second argument of Pick:

Pick[Range @ Length @ a,  
  Unitize[Subtract[a, b]].ConstantArray[1, Dimensions[a][[2]]], 0]

{2, 4}

$\endgroup$
11
$\begingroup$

This should be rather fast for very long packed arrays of integers.

a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {1, 2, 3}};
b = {{2, 1, 3}, {4, 5, 6}, {41, 2, 0}, {1, 2, 3}};
f2[a_,b_] := Position[Unitize[Subtract[a, b]].ConstantArray[1, Dimensions[a][[2]]], 0, 1];

f2[a,b]

{{2}, {4}}

More complicated but twice as fast (thanks to Carl Woll for the idea to use 1. instead of 1 in the ConstantArray):

f3[a_, b_] := SparseArray[
    Unitize[
     Unitize[Subtract[a, b]].ConstantArray[1., Dimensions[a][[2]]]],
    {Length[a]},
    1
    ]["NonzeroPositions"];

RandomSeed[12345];
{a, b} = RandomInteger[{1, 9}, {2, 1000000, 3}];

r2 = f2[a, b]; // RepeatedTiming // First
r3 = f3[a, b]; // RepeatedTiming // First
r2 == r3

0.060

0.021

True

$\endgroup$
2
  • $\begingroup$ Using ConstantArray[1., Dimensions[a][[2]]] is a bit faster $\endgroup$
    – Carl Woll
    Commented Jun 5, 2018 at 18:46
  • $\begingroup$ Thanks, @CarlWoll, that's very good thing to keep in mind! $\endgroup$ Commented Jun 5, 2018 at 21:46
5
$\begingroup$

If you need speed, you could use ugly compiled function:

equalPosInt2 = Last@Compile[{{a, _Integer, 2}, {b, _Integer, 2}},
  Module[{result, dimA, dimB, n, m, eq},
    result = Internal`Bag@Most@{0};
    dimA = Dimensions@a;
    dimB = Dimensions@b;
    n = Min[Compile`GetElement[dimA, 1], Compile`GetElement[dimB, 1]];
    m = Min[Compile`GetElement[dimA, 2], Compile`GetElement[dimB, 2]];
    Do[
      eq = True;
      Do[
        If[Compile`GetElement[a, i, j] =!= Compile`GetElement[b, i, j],
          eq = False;
          Break[];
        ];
        ,
        {j, m}
      ];
      If[eq, Internal`StuffBag[result, i]];
      ,
      {i, n}
    ];
    Internal`BagPart[result, All]
  ],
  CompilationTarget -> "C", RuntimeOptions -> "Speed"
];

Basic test:

a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {1, 2, 3}};
b = {{2, 1, 3}, {4, 5, 6}, {41, 2, 0}, {1, 2, 3}};
equalPosInt2[a, b]
(* {2, 4} *)

Timings:

fraccalo[a_, b_] := Position[MapThread[Equal, {a, b}], True]
f2[a_, b_] := Position[Unitize[Subtract[a, b]].ConstantArray[1, Dimensions[a][[2]]], 0, 1]
f3[a_, b_] := SparseArray[Unitize[Unitize[Subtract[a, b]].ConstantArray[1, Dimensions[a][[2]]]], {Length[a]}, 1]["NonzeroPositions"]
kglr1[a_, b_] := Pick[Range@Length@a, Total /@ Unitize[Subtract[a, b]], 0]
kglr2[a_, b_] := Pick[Range@Length@a, Unitize[Subtract[a, b]].ConstantArray[1, Dimensions[a][[2]]], 0]

RandomSeed@12345;
{a, b} = RandomInteger[{1, 9}, {2, 1000000, 3}];

r1 = fraccalo[a, b]; // MaxMemoryUsed // RepeatedTiming     (*{0.691, 280000672}*)
r2 = f2[a, b]; // MaxMemoryUsed // RepeatedTiming           (*{0.0803, 64000928}*)
r3 = f3[a, b]; // MaxMemoryUsed // RepeatedTiming           (*{0.048,  64001120}*)
r4 = kglr1[a, b]; // MaxMemoryUsed // RepeatedTiming        (*{0.072,  56000904}*)
r5 = kglr2[a, b]; // MaxMemoryUsed // RepeatedTiming        (*{0.0495, 72001232}*)
r6 = equalPosInt2[a, b]; // MaxMemoryUsed // RepeatedTiming (*{0.0054,    29928}*)

r1[[All, 1]] === r2[[All, 1]] === r3[[All, 1]] === r4 === r5 === r6
(* True *)
$\endgroup$

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.