I am trying to use MapIndexed to manipulate a matrix of data but I am having trouble getting the levels parameter to work the way I would like. Suppose that my data looks like this, with every odd column being a count and the even columns being some piece of data:

a = {{11, d11, 21, d21, 31, d31}, 
    {12, d12, 22, d22, 32, d32}, 
    {13, d13, 23, d23, 33, d33}, 
    {14, d14, 24, d24, 34, d34}};

b = {{11, d11, 0, Null, 31, d31}, 
    {12, d12, 0, Null, 32, d32}, 
    {13, d13, 0, Null, 33, d33}, 
    {14, d14, 0, Null, 34, d34}};

c = {{0, Null, 0, Null, 31, d31}, 
    {0, Null, 0, Null, 32, d32}, 
    {0, Null, 0, Null, 0, Null}, 
    {0, Null, 0, Null, 34, d34}};

As you can see, some odd columns will have a zero count and the corresponding data column will be NULL. Edit: I have changed c to reflect some new bits of information about the data. There may be some rows that contain the 0, NULL pattern, but I only want to eliminate columns in which the first row is like this. The first row is a sum of the following rows, and I only want to eliminate a column if it has no data at all. This is why I structured the For loop in the way I did. Based on the responses, I felt I needed to clarify. I'd like to eliminate both the count and data columns any time this is the case. For example, after some operation I'd like to have:

a0 = {{11, d11, 21, d21, 31, d31}, 
     {12, d12, 22, d22, 32, d32}, 
     {13, d13, 23, d23, 33, d33}, 
     {14, d14, 24, d24, 34, d34}};

b0 = {{11, d11, 31, d31}, 
     {12, d12, 32, d32}, 
     {13, d13, 33, d33}, 
     {14, d14, 34, d34}};

c0 = {{31, d31}, 
     {32, d32}, 
     {0, NULL}, 
     {34, d34}};

I accomplished this using a hack with a For loop but this is obviously not the proper way to use Mathematica and I was directed to MapIndexed:

a0 = For[i = 1, i < Length @ a[[1]], i = i + 2, 
         If[a[[1, i]] == 0,
             a = Drop[a, None, {i, i + 1}];
             i = i - 2

How can I get a0 with something of this form?:

a0 = MapIndexed[f, a, list];
 Transpose[c] //. {before___, {0..}, {Null..}, after___} :> {before, after} // Transpose
 {31,  d31}, 
 {32,  d32}, 
 { 0, Null}, 
 {34,  d34}


In case of column headers as in the comment below, the patterns in the rule need to modified slightly:

Transpose[c] //. {before___, {_, 0..}, {_, Null..}, after___} :> {before, after} // Transpose
 {"h3", "d3"}, 
 {  31,  d31}, 
 {  32,  d32}, 
 {   0, Null}, 
 {  34,  d34}
  • $\begingroup$ This line works correctly for the given data, but it does not work if there are column headers like head = {"h1", "d1", "h2", "d2", "h3", "d3"} that I would like to keep. Is there a simple way to work around this to produce f[PrependTo[c, head]] = {{"h3","d3"},{31, d31},{32, d32},{0, Null},{34, d34}} $\endgroup$ – Nick Jul 18 '14 at 17:28
  • $\begingroup$ The update works beautifully on my data set, thank you. $\endgroup$ – Nick Jul 18 '14 at 18:37
  • $\begingroup$ Suppose that instead of only wanting to eliminate columns whose first (non-header) entry is 0, I also would like to eliminate columns whose first (non-header) entry is below a certain threshold - say 1% of the first value in the farthest right column. Can this be done using a similar RuleDelayed? $\endgroup$ – Nick Jul 22 '14 at 21:36
  • $\begingroup$ @Nick I'm not sure what you mean exactly. Why don't you post this as a new question with one or two examples? $\endgroup$ – Teake Nutma Jul 23 '14 at 12:26

Here's another way typical for Mathematica:

c //. {a___, _, Null, b___} :> {a, b}
  • $\begingroup$ Could you explain what that command does? I'm a new user. $\endgroup$ – Nick Jul 14 '14 at 16:11
  • $\begingroup$ I'm hesitant to try to replace the documentation because I would not be as eloquent. //. is short for ReplaceRepeated and it will apply the replacement rule to the right until several times until nothing changes (which is necessary if several columns have to be removed). :> is short for RuleDelayed, the distinction between RuleDelayed and Rule is the same as that between Set and SetDelayed. The patterns: ___ matches any sequence of elements, even an empty one. _ matches a single arbitrary element. Reading about these things in the docs is the best way to learn. $\endgroup$ – C. E. Jul 14 '14 at 16:17
  • $\begingroup$ Thank you. Even though this is a good answer, I'd still like to see it done with MapIndexed so I can see an example with that function. $\endgroup$ – Nick Jul 14 '14 at 16:26
  • $\begingroup$ Given OP's example, why not c /. 0 | Null -> Sequence[]? $\endgroup$ – rm -rf Jul 14 '14 at 16:35
  • 2
    $\begingroup$ @Nick I don't think any of the old solution will work in this case. I won't update this answer, but Teake Nutma's answer is in the same spirit as what I wrote but works for your data. $\endgroup$ – C. E. Jul 14 '14 at 20:32
c /. (0 | Null) -> Sequence[] // MatrixForm

enter image description here

DeleteCases[MapIndexed[pos, c, {2}, Heads -> False], pos[0 | Null, _], 2] // MatrixForm

enter image description here

  • 1
    $\begingroup$ Given that the OP asked for an explanation of my rule, I think you should at least outline what you're doing in your second piece of (non-trivial) code. $\endgroup$ – C. E. Jul 14 '14 at 18:12
  • $\begingroup$ @eldo It is not clear to me that this provides the desired answer to the OP's question. In particular, for the c matrix, the third row is transformed to {} rather than the desired {0, Null}. $\endgroup$ – ecoxlinux Jul 14 '14 at 20:01
  • 1
    $\begingroup$ @ecoxlinux The question was changed / appended 2 hours after my answer. I think that Pickett's solution handles this case. $\endgroup$ – eldo Jul 14 '14 at 20:05

Here is one that seems fast (and only checks the first row, as the OP suggested in the question):

    Partition[Range@Length@First@c, 2] , 
    c[[1, #]] != {0, Null} &]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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