Is there a faster way to Map an Association?

7

2

Consider mapping an existing Association in a manner such as this:

asc = AssociationThread[Range @ 26, CharacterRange["a", "z"]];

Map[asc, {{11, 13, 2}, {19, 23, 16}}, {2}]
{{"k", "m", "b"}, {"s", "w", "p"}}

Is there a more efficient way perform this generic operation?

Mr.Wizard

Posted 2014-07-27T14:30:11.173

Reputation: 259 163

I guess I don't understand why I would want to Map an Association like that when the natural thing to use here is Replace or ReplaceAll. – RunnyKine – 2014-07-27T14:53:06.853

@RunnyKine You may already have an Association object to work with and converting to Rules and then Dispatch is slow (and pointless). In fact my experimentation indicates Association is faster than `Dispatch in this application. – Mr.Wizard – 2014-07-27T15:02:02.557

@RunnyKine I updated my answer to highlight the fact that use of Association is not included in the documentation for Replace that I can see. – Mr.Wizard – 2014-07-27T15:03:22.530

Ah I see, I already knew they worked with Association objects, I thought I saw somewhere that almost all functions were updated to work with Associations so I just used them, didn't know this was not documented. Thanks. – RunnyKine – 2014-07-27T15:07:08.693

I see you're shooting for the Socratic badge ;)

– rm -rf – 2014-07-27T15:17:49.923

@rm-rf What? Szabolcs beat me to it?! NOOOO!!!! ;-) Actually, unlike Legendary which I really did pursue I haven't thought about that badge. One I am looking forward to is Generalist, but I just have to wait for that. – Mr.Wizard – 2014-07-27T15:44:36.490

@RunnyKine I'm still getting my toes wet regarding Associations. I just learned that Lookup can be faster still. Do you know of any other method I can test? – Mr.Wizard – 2014-07-27T15:56:11.833

Yeah, I was gonna suggest that but remembered I learned it from @rm-rf in this answer way before the commercial release of V10

– RunnyKine – 2014-07-27T15:59:00.873

@RunnyKine I forgot about that answer. +1 on it now that I can run the code. – Mr.Wizard – 2014-07-27T16:04:37.697

Answers

11

Although announced for 10.0.2 the functionality below works from 10.0.0 onward.


Although apparently undocumented Replace and ReplaceAll work with Association and this combination is considerably faster than Map. Further it appears to be somewhat faster than using a Dispatch table as well.

Update: it seems Lookup is faster still. See additional timing result.

Setup:

rules = Thread[Range @ 26 -> CharacterRange["a", "z"]];
asc   = <|rules|>;
d1    = Dispatch @ rules;
d2    = Dispatch @ asc;

Note another undocumented functionality: you can Dispatch an Association.

Test:

{{11, 13, 2}, {19, 23, 16}} /. asc

Replace[{{11, 13, 2}, {19, 23, 16}}, asc, {2}]
{{"k", "m", "b"}, {"s", "w", "p"}}

{{"k", "m", "b"}, {"s", "w", "p"}}

Timings:

time = Function[x, NumberForm[x // Timing // First // AbsoluteTiming, {4, 3}], HoldAll]

m = RandomInteger[{1, 26}, {2500, 2500}];

Map[asc, m, {2}]      // time
m /. asc              // time
Replace[m, asc, {2}]  // time
Replace[m, d1, {2}]   // time
Replace[m, d2, {2}]   // time

Lookup[asc, #] & /@ m // time
{1.318, 1.248}

{0.843, 0.827}

{0.477, 0.468}

{0.576, 0.562}

{0.576, 0.562}

{0.380, 0.359}

Notes:

  1. Replace at levelspec {2} is almost three times faster than the equivalent Map

  2. ReplaceAll is not as fast but still faster than Map

  3. The origin of the Dispatch table appears to have no effect on performance

  4. Although not included in the example relative timings hold with few or many rules

Mr.Wizard

Posted 2014-07-27T14:30:11.173

Reputation: 259 163