14
$\begingroup$

On trying to write this answer I reached the frustrating realization that I didn't have an efficient way to delete a list of columns or deeper level components in a simple way as Part gives.

Given

MatrixForm[m = Partition[Partition[Range[4 4 3], 3], 4]]

Mathematica graphics

I can Delete rows {2,3} by

Delete[m, List /@ {2, 3}] // MatrixForm

Mathematica graphics

But to delete the columns or deeper levels I would need to Transpose twice. For instance using something like this

rDelete[m_, row_, col_] := Delete[
  Transpose[
   Delete[
    Transpose[m]
    , List /@ col
    ]
   ], List /@ row
  ]

Mathematica graphics

On the other hand to get a Part at any level I can easily use

Part[m, All, {1, 4}, {2, 3}] // MatrixForm

Mathematica graphics

Unfortunately, All and Span are not available for Delete.

Question: How can we delete columns or whole higher levels elegantly and efficiently, as we do with Part?

$\endgroup$
3
  • 3
    $\begingroup$ This removes the second "column" of m: Drop[m, None, {2}, None]. Drop[m, None, None, {2}] gives the complement of your Part[] example. You can judiciously combine this with Map[]/MapAt[] if you need to do several positions, since unlike Delete[], Drop[] can only remove from either a single dimension or a range of dimensions. $\endgroup$ Feb 25, 2016 at 16:12
  • $\begingroup$ @J.M. I guess the problem with Drop is the fact that you need to Map over the list of indexes to delete, as you say. $\endgroup$
    – rhermans
    Feb 25, 2016 at 16:55
  • $\begingroup$ Also: (43785) $\endgroup$
    – Mr.Wizard
    Feb 26, 2016 at 23:28

2 Answers 2

13
$\begingroup$

You can actually use Part for that:

ClearAll[delete];
delete[expr_, specs___] :=
  Module[{copy = expr},
    copy[[specs]] = Sequence[];
    copy
  ];

So that for example

delete[m, All, All, 2]

(* 
   {
     {{1, 3}, {4, 6}, {7, 9}, {10, 12}}, 
     {{13, 15}, {16, 18}, {19, 21}, {22, 24}}, 
     {{25, 27}, {28, 30}, {31, 33}, {34, 36}}, 
     {{37, 39}, {40, 42}, {43, 45}, {46, 48}}
   }
*)

Note that this is not exactly equivalent to Delete in all cases, since sequence splicing is an evaluation-time effect, so the results will be different if you delete inside held expressions - in which case the method I suggested may not work.

Here is a version that would probably be free of the mentioned flaw, but will be slower:

ClearAll[deleteAlt];
deleteAlt[expr_, specs___] :=
  Module[{copy = Hold[expr], tag},
    copy[[1, specs]] = tag;
    ReleaseHold@Delete[copy, Position[copy, tag]]
  ];

You can test both on say, Hold[Evaluate[m]], with the spec 1, All, All, 2, to see the difference.

$\endgroup$
8
  • $\begingroup$ This opens for me a whole a new understanding of Sequence. Very clever! +1 $\endgroup$
    – rhermans
    Feb 25, 2016 at 16:11
  • 7
    $\begingroup$ The Sequence[] is replaceable with Nothing in version 10.2, I suppose. Speaking of which: MapAt[Nothing, m, {All, All, 2}]. $\endgroup$ Feb 25, 2016 at 16:15
  • $\begingroup$ @J.M. I am aware of Nothing, but haven't yet gained extensive experience with it, unlike with Sequence. As to MapAt, this is a possibility, but it was known to have efficiency issues in the past, for large number of positions mapped. I didn't have the time to check it now, so I didn't go that way - for in-place assignments with Part, it has been known that it has excellent efficiency in all cases. $\endgroup$ Feb 25, 2016 at 16:24
  • 1
    $\begingroup$ I don't want to move the goalpost, I'm grateful for your answer and the code is unambiguous. What I was expecting (naively?) is deleted columns, rows and higher orders all across the array, like in my example with rDelete, where all the elements have the same length. But this already solves most of what I wanted nicely. $\endgroup$
    – rhermans
    Feb 25, 2016 at 18:41
  • 1
    $\begingroup$ @rhermans This is then different semantics. Here is a version that works that way: ClearAll[delete2];delete2[expr_, part_, rest___] :=Module[{copy = expr}, copy[[part]] = Sequence[];Map[delete2[#, rest] &, copy]]; delete2[expr_] := expr;. But it won't be as efficient as the ones I posted before. $\endgroup$ Feb 25, 2016 at 19:04
9
$\begingroup$

You may use ReplacePart and Nothing with Blank (_) for All.

Delete second entry of all submatrices

ReplacePart[m, {_, _, 2} -> Nothing] // MatrixForm

enter image description here

Delete last column

ReplacePart[m, {_, -1} -> Nothing] // MatrixForm

enter image description here

and so on.

Hope this helps.

$\endgroup$
2
  • $\begingroup$ Thanks, it does partially. How do you deal with list of columns to delete? $\endgroup$
    – rhermans
    Feb 25, 2016 at 17:08
  • 2
    $\begingroup$ @rhermans Alternatives : ReplacePart[m, {_, 2 | 3} -> Nothing] // MatrixForm. $\endgroup$
    – Edmund
    Feb 25, 2016 at 17:21

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