27
$\begingroup$

Prompted by a comments conversation here, here's an interesting (and often performance enhancing) use of Extract:

target = Join[Range[100], {{1, 2, 3, Range[20]}}];

Rest@Extract[target, {{{}}, {2 ;; 10 ;; 2}, {-1, -1, 1 ;; -1 ;; 2}}]

(* {{2, 4, 6, 8, 10}, {1, 3, 5, 7, 9, 11, 13, 15, 17, 19}} *)

Prepending the {{}} to the extraction list appears to suppress the error message one would otherwise get attempting to use Span, and it appears that most any valid position/span construct can be used in the extraction list.

The extracted list has an empty list as first element (hence the use of Rest). Prepending just {} makes the first element the original list.

I've often found this useful instead of mapping over a list of spans (and faster).

This seems similar to the undocumented extension to MapAt

As noted by Mr. Wizard, this does not appear to work in V7, I'm curious what versions it works in.

$\endgroup$
6
  • $\begingroup$ Verified to work in Mathematica V9.0.1 on MS Windows. $\endgroup$
    – ciao
    Commented Apr 16, 2014 at 22:04
  • $\begingroup$ Sorry, I missed your comment until after you posted this. This structure is OK, but personally I would have used something like: "Is it possible to use Span in Extract?" -- "Yes, since at least v9 there is an undocumented syntax for this, and it performs very well..." You can rewrite your Q&A like that if you wish, or leave it as it is; either way it gets the information out to people. +1 :-) $\endgroup$
    – Mr.Wizard
    Commented Apr 16, 2014 at 22:13
  • $\begingroup$ Great find (+1). It looks like extraction list could be prepended with anything that does not trigger an error message: {{}}, or {0} or {{101,4,3}} ... $\endgroup$
    – kglr
    Commented Apr 16, 2014 at 23:13
  • 6
    $\begingroup$ New in 3 | Last modified in 8 | Documentation last modified in 5 :-) $\endgroup$ Commented Apr 17, 2014 at 14:32
  • 1
    $\begingroup$ The question example does not work in either 10.0.2 or 10.1.0 under Windows. In both versions the function is returned unevaluated and issues a message. $\endgroup$
    – Mr.Wizard
    Commented May 12, 2015 at 10:23

1 Answer 1

11
$\begingroup$

Update

I think this behaviour was unintentional. It seems to have been fixed in V10.0.1.

Evidence that it is unintentional

The following command crashed the kernel in V9 (and I think V10.0.0)

Extract[1, {{}, All}]

and printed a funky message too (in V9). Anyway, it makes sense that we should put another pair of brackets around All. Then, this was also weird:

Extract[1, {{}, {All}}] 

which used to give Integer[]

Old answer

One thing to point out is that we can still use the third argument of Extract if we use Span. Indeed Extract is now more general/flexible than Part, except that you cannot use Extract with Set. Extract appears to work quickly on packedarrays.

Timings

Here are some timing comparisons. Extract can indeed beat Part with Map (or like in this answer, Apply), as mentioned by rasher in comments. The result here will be a ragged array, so that we cannot suffice with a single call to Part. Let

max=5*^6;
kk=1000;
mm=max/kk;
target = ConstantArray[Range[kk], mm]; ;

We have

PackedArrayQ[target]
True

If we also let

partSpans = Table[{ii, 1 ;; Mod[ii, kk, 1]}, {ii, 1, mm}]; ;
spans = Prepend[properSpans, {{}}];

Then

(out = Rest@Extract[target, spans]) // Timing // First
(pOut = Part[target, ##] & @@@ partSpans) // Timing // First
pOut === out
0.016991  
0.020719
True

More and smaller sublists

For

max = 5*^6;
kk = 5;
mm = max/kk;

The timings become

0.673268 (*Extract*)
1.636892 (*Part*)

Extract reduces overhead

It turns out that the difference in timing between the solutions with Extract and Part is mainly due to overhead that is caused by Apply. Another solution shows that the time taken by Part is about the same as that taken by Extract, even in this last case. For the timings below, I use the same parameters as in the last case above.

First we do all the overhead

(pCode =
    Apply[
     Part,
     Hold@Evaluate@Array[Join[{target}, partSpans[[#]]] &, mm],
     {2}
     ]) // Timing // First
5.176928

Then Part is quite quick

(p2Out = ReleaseHold[pCode]) // Timing // First
p2Out === out === pOut
0.902468 (*Extract took 0.673268 *)
True

Possibly temporary note

This is a cleaner way to define pCode (without the wrapper Hold, but using a "delayed definition" instead). Syntax smyntax!

ReleaseHold@
   Hold[SetDelayed][
    Hold@pCode,
    Apply[
     Part,
     Hold@Evaluate@Array[Join[{target}, partSpans[[#]]] &, mm],
     {2}
     ]
    ] // Timing // First
$\endgroup$
6
  • $\begingroup$ Now I will feel bad using Part for pure extraction :( but I probably will do that still because of the syntax ;) $\endgroup$
    – Kuba
    Commented Apr 17, 2014 at 15:36
  • $\begingroup$ @Kuba Extract is more general and can be faster, but if you can use one call to Part, Part must be faster :). Hm I don't know about syntax arguments :P. I hope in version 10 we get even more control over such things (and we can decide for ourselves whether we like the notation for Composition or not :P) :). $\endgroup$ Commented Apr 17, 2014 at 15:46
  • $\begingroup$ @Kuba see the new last section :P $\endgroup$ Commented Apr 17, 2014 at 15:48
  • $\begingroup$ Part has [[ ]] :) and you can e.g. matrix[[;;,1]]=1. :) Composition can be tricky if you need to extract only tiny part but put that operation inside instead of directly on argument. $\endgroup$
    – Kuba
    Commented Apr 17, 2014 at 15:52
  • $\begingroup$ Neat analysis! +1 $\endgroup$
    – ciao
    Commented Apr 17, 2014 at 21:18

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.