8
$\begingroup$

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.

$\endgroup$
1

6 Answers 6

11
$\begingroup$
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.

$\endgroup$
7
  • $\begingroup$ 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. $\endgroup$ Oct 2, 2014 at 20:54
  • $\begingroup$ I suppose you could do something like this then MapAt[#*0&,list1,{All,1;;3}] $\endgroup$
    – chuy
    Oct 2, 2014 at 20:55
  • 1
    $\begingroup$ You have my vote, but please note for readers that the final method ("without Module") modifies in-place. $\endgroup$
    – Mr.Wizard
    Oct 2, 2014 at 21:35
  • 1
    $\begingroup$ 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. $\endgroup$
    – ybeltukov
    Oct 2, 2014 at 21:53
  • $\begingroup$ @Mr.Wizard, thanks. Good point; will update. $\endgroup$
    – kglr
    Oct 2, 2014 at 21:55
10
$\begingroup$

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

$\endgroup$
1
  • $\begingroup$ +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]. $\endgroup$
    – WReach
    Oct 5, 2014 at 15:39
4
$\begingroup$

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.

$\endgroup$
8
  • 1
    $\begingroup$ It seems that @kguler's zeroColumns2c is considerably faster even with an additional copying of the matrix. $\endgroup$
    – ybeltukov
    Oct 4, 2014 at 14:32
  • $\begingroup$ @ybeltukov I didn't compare zeroColumns2c because it changes mat. I should have tested zeroColumns3, though. $\endgroup$
    – Michael E2
    Oct 4, 2014 at 16:57
  • $\begingroup$ @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." $\endgroup$
    – eldo
    Oct 4, 2014 at 18:59
  • $\begingroup$ @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. $\endgroup$
    – Michael E2
    Oct 4, 2014 at 21:37
  • 1
    $\begingroup$ @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. $\endgroup$
    – Michael E2
    Oct 13, 2014 at 10:49
3
$\begingroup$

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

$\endgroup$
3
$\begingroup$

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

$\endgroup$
1
$\begingroup$
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)$$

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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