8

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.

kglr
  • 394,356
  • 18
  • 477
  • 896
Sultan of Swing
  • 913
  • 1
  • 6
  • 19

6 Answers6

11
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.

kglr
  • 394,356
  • 18
  • 477
  • 896
10

An alternative approach

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

enter image description here

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

enter image description here


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

enter image description here

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

enter image description here

ybeltukov
  • 43,673
  • 5
  • 108
  • 212
  • +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]. – WReach Oct 05 '14 at 15:39
4

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.

Michael E2
  • 235,386
  • 17
  • 334
  • 747
  • 1
    It seems that @kguler's zeroColumns2c is considerably faster even with an additional copying of the matrix. – ybeltukov Oct 04 '14 at 14:32
  • @ybeltukov I didn't compare zeroColumns2c because it changes mat. I should have tested zeroColumns3, though. – Michael E2 Oct 04 '14 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 04 '14 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. – Michael E2 Oct 04 '14 at 21:37
  • @MichaelE2 :))) – eldo Oct 04 '14 at 21:58
  • @Michael I happened to look at your profile and saw: "visited 666 days, 666 consecutive" -- besides the slightly interesting number I was shocked to see that you've been on the site every day since you joined!? I must admit I am sort of hoping you have a script that refreshes the page. Otherwise I think you are over due for some time away from a computer! (And SE itself for that matter.) – Mr.Wizard Oct 13 '14 at 05:43
  • 1
    @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. – Michael E2 Oct 13 '14 at 10:49
  • @Michael I hope you enjoyed the vacation. Amazing coincidence; glad I saw it! – Mr.Wizard Oct 13 '14 at 17:00
3

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}}

enter image description here

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

enter image description here

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

enter image description here

ubpdqn
  • 60,617
  • 3
  • 59
  • 148
3

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

enter image description here

Replace contigious columns

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

enter image description here

Replace at arbitrary positions

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

enter image description here

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

enter image description here

eldo
  • 67,911
  • 5
  • 60
  • 168
1
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)$$

user1066
  • 17,923
  • 3
  • 31
  • 49