开发者

how to extract rows from matrix based on value in first entry?

This is another simple 'matrix' question in Mathematica. I want to show how I did this, and ask if there is a better answer.

I want to select all 'rows' from matrix based on value in the first column (or any column, I used first column here just as an example).

Say, find all rows where the entry in the first position is <=4 in this example:

    list = {{1, 2, 3},
           {4, 5, 8},
           {7 , 8, 9}}

So, the result should be

            {{1,2,3},
             {4,5,8}}

Well, the problem is I need to use Position, since the result returned by Position can be used directly by Extract. (but can't be used by Part or [[ ]], so that is why I am just looking at Position[] ).

But I do not know how to tell Position to please restrict the 'search' pattern to only the 'first' column so I can do this in one line.

When I type

pos = Position[list, _?(# <= 4 &)]

it returns position of ALL entries which are <=4.

{{1, 1}, {1, 2}, {1, 3}, {2, 1}}

If I first get the first column, then apply Position on it, it works ofcourse

  list = {{1, 2, 3},
          {4, 5, 8},
          {7 , 8, 9}};
   pos = Position[list[[All, 1]], _?(# <= 4 &)]
   Extract[list, pos]
   -->       {{1, 2, 3}, {4, 5, 8}}

Also I tried this:

pos = Position[list, _?(# <= 4 &)];
pos = Select[pos, #[[2]] == 1 &]  (*only look at ones in the 'first' column*)

{{1, 1}, {2, 1}}---> 

and this gives me the correct positions in the first column. To use that to find all rows, I did

pos = pos[[All, 1]] (* to get list of row positions*)
---> {1, 2}

list[[ pos[[1]] ;; pos[[-1]], All]]
{{1, 2, 3}, 
 {4, 5, 8}}

So, to summarize, putting it all together, this is what I did:

method 1

 list = {{1, 2, 3},
         {4, 5, 8},
         {7 , 8, 9}};
 pos = Position[list[[All, 1]], _?(# <= 4 &)]
 Extract[list, pos]
  -->       {{1, 2, 3}, {4, 5, 8}}

method 2

list = {{1, 2, 3},
      {4, 5, 8},
      {7 , 8, 9}}

pos = Position[list, _?(# <= 4 &)];
pos = Select[pos, #[[2]] == 1 &];
pos = pos[[All, 1]];
list[[ pos[[1]] ;; pos[[-1]], All]]

{{1, 2, 3}, 
{4, 5, 8}}

 The above clearly is not too good. 

Is method 1 above the 'correct' functional way to do this?

For reference, this is how I do the above in Matlab:

EDU>> A=[1 2 3;4 5 8;7 8 9]
A =
     1     2     3
     4     5     8
     7     8     9

EDU>> A( A(:,1)<=4 , :)

     1     2     3
     4     5     8

I am trying to improve my 'functional' handling of working with matrices in Mathematica commands, this is an area I feel I am not good at working with lists. I find working with matrices easier for me.

The question is: Is there is a shorter/more functional开发者_Python百科 way to do this in Mathematica?

thanks


You could use Pick[] as follows:

Pick[list, list[[All, 1]], _?(# <= 4 &)]


How about the following?

In[1]:= list = {{1, 2, 3}, {4, 5, 8}, {7, 8, 9}};

In[2]:= Select[list, First[#] <= 4 &]

Out[2]= {{1, 2, 3}, {4, 5, 8}}

Here's a loose translation of your matlab code:

list[[Flatten[Position[Thread[list[[All, 1]] <= 4], True]]]]

(of course, the Flatten would not be needed if I used Extract instead of Part).


There is a faster method than those already presented, using SparseArray. It is:

list ~Extract~
  SparseArray[UnitStep[4 - list[[All, 1]]]]["NonzeroPositions"]

Here are speed comparisons with the other methods. I had to modify WReach's method to handle other position specifications.

f1[list_, x_] := Cases[list, {Sequence @@ Table[_, {x - 1}], n_, ___} /; n <= 4]
f2[list_, x_] := Select[list, #[[x]] <= 4 &]
f3[list_, x_] := Pick[list, (#[[x]] <= 4 &) /@ list]
f4[list_, x_] := Pick[list, UnitStep[4 - list[[All, x]]], 1]
f5[list_, x_] := Pick[list, Thread[list[[All, x]] <= 4]]
f6[list_, x_] := list ~Extract~
                   SparseArray[UnitStep[4 - list[[All, x]]]]["NonzeroPositions"]

For a table with few rows and many columns (comparing position 7):

a = RandomInteger[99, {250, 150000}];
timeAvg[#[a, 7]] & /@ {f1, f2, f3, f4, f5, f6} // Column
0.02248
0.0262
0.312
0.312
0.2808
0.0009728

For a table with few columns and many rows (comparing position 7):

a = RandomInteger[99, {150000, 12}];
timeAvg[#[a, 7]] & /@ {f1, f2, f3, f4, f5, f6} // Column
0.0968
0.1434
0.184
0.0474
0.103
0.002872


If you want the rows that meet the criteria, use Cases:

Cases[list, {n_, __} /; n <= 4]

(* {{1, 2, 3}, {4, 5, 8}} *)

If you want the positions within the list rather than the rows themselves, use Position instead of Cases (restricted to the first level only):

Position[list, {n_, __} /; n <= 4, {1}]

(* {{1}, {2}} *)


If you want to be very clever:

Pick[list, UnitStep[4 - list[[All, 1]]], 1]

This also avoids unpacking, which means it'll be faster and use less memory.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜