# Replacing columns of a matrix with zeros

I am to create a function that replaces columns 'm' through till 'n' with zeros. Here is what I have so far:

zeroColumns[mat_, m_ ;; n_] := ReplacePart[mat, {_, m | n} -> 0]
list1 = {{1, 2, 3, 4, 4}, {4, 5, 6, 9, 5}, {7, 3, 8, 9, 5}, {14, 3, 1,5, 6}}
zeroColumns[list1, 1 ;; 3]


which returns

{{1, 2, 3, 4, 4}, {4, 5, 6, 9, 5}, {7, 3, 8, 9, 5}, {14, 3, 1, 5, 6}}

{{0, 2, 0, 4, 4}, {0, 5, 0, 9, 5}, {0, 3, 0, 9, 5}, {0, 3, 0, 5, 6}}


and that's not a surprise to me because in the part ReplacePart[list1, {_, m | n} -> 0] I'm not going from 'm' to 'n'; instead, I'm choosing column 'm' and column 'n'. I tried replacing | with ;; but it does nothing. How can I iterate from 'm' to 'n' instead of just picking both? I cannot use loops. I'm not really interested in revamping my code. I'm sure there's just a simple way of altering the | so that it goes through EVERY column instead of just THOSE two.

• Related: (3069), (52385). Possible duplicate: (56494) Oct 2, 2014 at 21:13

ClearAll[zeroColumns2,zeroColumns2b,zeroColumns3,zeroColumns3b];
zeroColumns2[mat_, m_ ;; n_] :=  ReplacePart[mat, {_, Alternatives @@ Range[m, n]} -> 0];
(* or Alternatives @@ Range @@ (m ;; n) if you want to use Span *)

list1 = {{1, 2, 3, 4, 4}, {4, 5, 6, 9, 5}, {7, 3, 8, 9, 5}, {14, 3, 1, 5, 6}};
zeroColumns2[list1, 1 ;; 3]
(* {{0,0,0,4,4},{0,0,0,9,5},{0,0,0,9,5},{0,0,0,5,6}} *)


A way to use Span instead of Alternatives:

zeroColumns2b[mat_, m_ ;; n_] := ReplacePart[mat, Thread[{_, Range @@ (m ;; n)}] -> 0];


Just in case you don't have to use ReplacePart, you might consider

zeroColumns3[mat_, m_ ;; n_] := Module[{t = mat}, t[[All, m ;; n]] = 0; t]
zeroColumns3[list1, 1 ;; 3]
(* {{0,0,0,4,4},{0,0,0,9,5},{0,0,0,9,5},{0,0,0,5,6}}  *)


or, without Module,

ClearAll[zeroColumns2c];
SetAttributes[zeroColumns2c, HoldFirst];
zeroColumns2c[mat_, m_ ;; n_] := (mat[[All, m ;; n]] = 0; mat);
zeroColumns2c[list1, 1 ;; 3]
(*  {{0,0,0,4,4},{0,0,0,9,5},{0,0,0,9,5},{0,0,0,5,6}}  *)


Note: This last one modifies the input matrix in-place.

• Thanks very much, although I wish I could somehow use ;; in place of |. We're supposed to be using pure functions right now, and not modules. Oct 2, 2014 at 20:54
• I suppose you could do something like this then MapAt[#*0&,list1,{All,1;;3}]
– chuy
Oct 2, 2014 at 20:55
• You have my vote, but please note for readers that the final method ("without Module") modifies in-place. Oct 2, 2014 at 21:35
• It seems to me that Range @@ (m ;; n) is simply Range[m,n]. So may be s_Span instead of m_ ;; n_? You will handle m ;; n ;; d at the same time. Oct 2, 2014 at 21:53
• @Mr.Wizard, thanks. Good point; will update.
– kglr
Oct 2, 2014 at 21:55

An alternative approach

m = RandomInteger[9, {5, 5}];
m // MatrixForm


MapAt[0 &, m, {;; , 2 ;; 4}] // MatrixForm


There is a pattern-based solution, but it is considerably more diffiluct

zeroColumns4[mat_, s_Span] :=
ReplacePart[mat, {_, j_ /; s[[1]] <= j <= (s[[2]] /. All -> ∞) &&
(Length@s < 3 || Mod[j - s[[1]], s[[3]]] == 0)} -> 0];

m = RandomInteger[9, {10, 10}];
m // MatrixForm


zeroColumns4[m, 4 ;; ;; 2] // MatrixForm


• +1 If the OP's goal is to leave everything in the original code unchanged except m | n, then I suggest zeroColumns[mat_, m_ ;; n_] := ReplacePart[mat, {_, i_ /; m <= i <= n} -> 0]. Oct 5, 2014 at 15:39

I understand from a comment that the use of pure functions is desired, but I think the broader question will have a broader interest. Here's a way that has a little start-up time, but whose efficiency advantage increases with size.

zeroColumnsM[mat_?MatrixQ, m_ ;; n_] :=
With[{ncol = Last@Dimensions@mat},
mat . SparseArray[Delete[Table[{i, i} -> 1, {i, ncol}], List /@ Range[m, n]], {ncol,ncol}]
]

zeroColumnsM[list1, 2 ;; 3]
(* {{1, 0, 0, 4, 4}, {4, 0, 0, 9, 5}, {7, 0, 0, 9, 5}, {14, 0, 0, 5, 6}} *)


Timings: Originally, I omitted kguler's zeroColumns3, which is probably as fast as one can get because of comments on the use of Module. Later I realized that if I'm going to consider the "broader question," then it definitely should be included.

Needs["GeneralUtilities"];

zeroColumns2[mat_, m_ ;; n_] :=                 (* kguler's faster function *)
ReplacePart[mat, {_, Alternatives @@ Range[m, n]} -> 0];
zeroColumns3[mat_, m_ ;; n_] :=
Module[{t = mat}, t[[All, m ;; n]] = 0; t];   (* kguler's really faster function *)
zeroColumnsY[mat_?MatrixQ, m_ ;; n_] :=         (* ybeltukob *)
MapAt[0 &, mat, Thread[{All, Range[m, n]}]]


Small example:

list = RandomInteger[10, {10, 10}];
res2 = zeroColumns2[list, 2 ;; 5]; // AccurateTiming
res3 = zeroColumns3[list, 2 ;; 5]; // AccurateTiming
resY = zeroColumnsY[list, 2 ;; 5]; // AccurateTiming
resM = zeroColumnsM[list, 2 ;; 5]; // AccurateTiming


0.0000805732
7.9209*10^-6 *
0.0000251982
0.000071834

Larger example:

list = RandomInteger[10, {100, 200}];
res2 = zeroColumns2[list, 20 ;; 50]; // AccurateTiming
res3 = zeroColumns3[list, 20 ;; 50]; // AccurateTiming
resY = zeroColumnsY[list, 20 ;; 50]; // AccurateTiming
resM = zeroColumnsM[list, 20 ;; 50]; // AccurateTiming


0.0176221
0.000024208 *
0.00363939
0.000537734

res2 == resY == resM
(* True *)


The SparseArray approach gains an advantage the more columns there are to be zeroed, but I doubt there's a way to beat kguler's zeroColumns3.

• It seems that @kguler's zeroColumns2c is considerably faster even with an additional copying of the matrix. Oct 4, 2014 at 14:32
• @ybeltukov I didn't compare zeroColumns2c because it changes mat. I should have tested zeroColumns3, though. Oct 4, 2014 at 16:57
• @MichaelE2 - Interesting, that you don't consider the other answers for your your timing table. What you got is, to quote yourself, "a lack of time."
– eldo
Oct 4, 2014 at 18:59
• @eldo The other answers weren't there when I did the analysis. But it's interesting that no one else has done it at all. In part there's just so much time. And we had to go harvest. It's going to be cold tonight. Oct 4, 2014 at 21:37
• @Mr.Wizard A sleeping Mac turns itself on every few hours to get mail etc. It caused the open MSE page to register me as present. I turned that feature off - I took over a year to discover it - then I got a new Mac which turned the feature back on (I assumed it would inherit the old settings). Your noticing is a coincidence - for the last 5 days I've been on family trip with the computer off. If you look now, it says 667/1. Now sometimes I would log in only at breakfast just to keep the streak going. I'm glad you saw the 666. Breaking the streak at that number was a coincidence, too. Oct 13, 2014 at 10:49

For rendering columns m through n step s:

sa[mat_, m_, n_, s_: 1] :=
(1-SparseArray[{i_, j_} /; MemberQ[Range[m, n, s], j] :> 1,Dimensions@mat]) mat


For removing list of columns:

sasc[mat_,list_] :=
(1-SparseArray[{i_, j_} /; MemberQ[list, j] :> 1,Dimensions@mat]) mat


Testing:

test={{9, 6, 2, 1, 1, 4, 8, 0, 8, 8}, {0, 1, 7, 8, 8, 9, 4, 3, 6, 8}, {1,
5, 4, 2, 8, 9, 4, 1, 9, 3}, {5, 6, 4, 3, 0, 9, 7, 7, 3, 0}, {4, 0,
7, 5, 3, 0, 8, 8, 1, 6}, {3, 4, 8, 8, 7, 6, 9, 6, 2, 0}, {0, 7, 7,
0, 8, 9, 5, 3, 0, 3}, {8, 0, 1, 7, 7, 9, 2, 3, 9, 9}, {1, 5, 6, 2,
7, 3, 8, 0, 7, 6}, {2, 0, 9, 8, 4, 4, 0, 1, 6, 6}}


sa[test, 2, 10, 2] // MatrixForm


sasc[test, {2, 3, 7}] // MatrixForm


A solution using Set

replace[m_?MatrixQ, p_?VectorQ] := Module[{q = m}, q[[All, p]] = 0; q]


Replace 1 column

replace[RandomInteger[9, {5, 5}], {3}] // MatrixForm


Replace contigious columns

replace[RandomInteger[9, {7, 7}], Range[2, 6]] // MatrixForm


Replace at arbitrary positions

replace[RandomInteger[9, {5, 5}], {1, 3, 4}] // MatrixForm


A variant "directly" changing your matrix

Clear @ replaceInplace
SetAttributes[replaceInplace, HoldFirst]

replaceInplace[m_?MatrixQ, p_?VectorQ] := (m[[All, p]] = 0; m)

mat = RandomInteger[9, {5, 5}];
replaceInplace[mat, {1, 3, 4}]
mat // MatrixForm


list1.DiagonalMatrix[{1, 0, 0, 0, 1}] // MatrixForm


$$\left( \begin{array}{ccccc} 1 & 0 & 0 & 0 & 4 \\ 4 & 0 & 0 & 0 & 5 \\ 7 & 0 & 0 & 0 & 5 \\ 14 & 0 & 0 & 0 & 6 \\ \end{array} \right)$$

Or, maybe a more versatile variation:

list1 // #.SparseArray[{ {2, 2} -> 0, {3, 3} -> 0, {4, 4} -> 0,
Band[{1, 1}] -> 1}, Dimensions[#][[2]]] & // MatrixForm
`

$$\left( \begin{array}{ccccc} 1 & 0 & 0 & 0 & 4 \\ 4 & 0 & 0 & 0 & 5 \\ 7 & 0 & 0 & 0 & 5 \\ 14 & 0 & 0 & 0 & 6 \\ \end{array} \right)$$