3
$\begingroup$

Here is the issue: I have a grid (which comes from a list of lists). What I want to do is to extract the rows of the grid (or the lists of the list) if all the elements of the row (or of the list) are positive and reals. I set an example of the list here:

list ={{1, 2, 2, {{0.45, -12}, {0.399896 - 0.547998 i, 7}}}, {1, 2, 2, {{0.45, -12}, {0.399896 - 0.547998 i, 7}}}, {1, 2, 2, {{0.45, -12}, {0.399896 - 0.547998 i, 7}}}, {1, 2, 2, {{0.45, -12}, {0.399896 - 0.547998 i, 7}}}, {1, 2, 2, {{0.45, 2}, {0.399896, 7}}}}

Grid[list]

So, in the list above, I would like to have a code which select only the last list. The list that I really have is way longer than this (if I print the pdf of the grid, it is like 45 pages long), so I need a kind of automation which gives me the desired output. To say it in other words, I need Mathematica to automatically select all the lists with positive and real elements only.

How can I do it?

$\endgroup$
3
  • 1
    $\begingroup$ If you mean the last row: list[[-1]] which gives {1, 2, 2, {{0.45, 2}, {0.399896, 7}}}. If you mean the bottom right cell: list[[-1, -1]] which gives {{0.45, 2}, {0.399896, 7}}. If you really mean the very last list: list[[-1, -1, -1]] which gives {0.399896, 7}. I recommend playing around with selecting different parts of lists using the indices, and looking up Part in the documentation system. The double square brackets are short-hand for Part. $\endgroup$
    – MassDefect
    Apr 21, 2020 at 21:21
  • $\begingroup$ as @MassDefect said you can use Part. Mathematica has short function for the first and last entries in a list also. So you can use Last@list in place of list[[-1]] and these give same thing. $\endgroup$
    – Nasser
    Apr 21, 2020 at 21:54
  • $\begingroup$ First of all, thank you a lot for your answer. Sorry guys, I have badly formulated the question, my fault. I was tired for the overload of work. I will edit it. $\endgroup$ Apr 21, 2020 at 22:05

2 Answers 2

5
$\begingroup$

If you want to check every element including the indices out front of each 2 x 2 matrix, you could use:

Select[list, AllTrue[Flatten[#], NonNegative] &]

Using NonNegative will catch cases where the value is zero since NonNegative[0] evaluates to True. If you don't want zero to be included, you can use Positive like in Omrie Ovdat's answer.

This will return all rows where the condition is met. If you just want the 2 x 2 matrices and not the values in front, there are a number of options including:

Select[list, AllTrue[Flatten[#], NonNegative] &][[All, 4]]

or

Select[list, AllTrue[Flatten[#[[4]]], NonNegative] &]

though this last one will only test the 2 x 2 matrix itself and won't test the numbers in front.

Also, keep in mind that while Grid is nice for display purposes, Grid, MatrixForm, TableForm, and friends don't behave the same as the actual list of lists. So if you have newlist = Grid[list], the functions above won't work on newlist.

Clarifications:

Select looks through each part of the top-most list, one-at-a-time. So to start with, it would extract the very first row of your grid. It then feeds that single row to the second part of the Select function (that would be AllTrue[Flatten[#], NonNegative] &).

The & tells Mathematica to feed that row into the placeholder (#) to its left. So the # is replaced by that entire first row.

Now we have something like Flatten[{1, 2, 2, {{0.45, -12}, {0.399896 - 0.547998*i, 7}}}]. Flatten takes a list of lists and turns it into a single list. The output of this part would be {1, 2, 2, 0.45, -12, 0.399896 - 0.547998*i, 7} in this case. The reason for doing this is because AllTrue in the next step gives a list of lists as the output if I don't use Flatten when I just want a single True or False as the output.

Then, AllTrue is designed to take each element of this list and test it with the function NonNegative. Essentially the same thing happens here as with the #/& pair earlier. Each element of the flattened list is tested in turn by being fed into NonNegative. I could have also wrote NonNegative[#]&, but it doesn't need the extra characters in this case so I didn't use them.

NonNegative checks any number to make sure it is non-negative. The definition in the documentation says:

NonNegative[x] gives False if x is manifestly a negative or complex numerical quantity. Otherwise, it remains unevaluated.

This means it will give True for non-negative real numbers. 0 is not negative, so it is included here. If you don't want to include 0, you can use Positive. Positive[0] gives False. Keep in mind that if you have something other than a number, nothing at all will happen. So if one of your elements happens to be a string ("Hello World") or a variable (x), you'll just get NonNegative["Hello World"] or NonNegative[x] as output since it cannot evaluate the input.

Note:

I just realized that you have lower case i in your numbers. In fact, NonNegative[list[[1, 4, 2, 1]]] gives NonNegative[0.399896 -0.547998 i]. This is because Mathematica does not recognize the "i" as indicating a complex number. In Mathematica, it should be a capital I. The code still works because Select chooses only those that evaluate to True. Anything that evaluates to False or in this case NonNegative[0.399896 -0.547998 i] is ignored. So it should still work, but you may want to think about making those into capital I's so that Mathematica recognizes them as complex numbers. Certainly if you want to do any math on those numbers or plot them or something, it won't work well.

You can fix this pretty easily with list = list/.i->I. This replaces any instance of "i" with I. /. is shorthand for ReplaceAll, and the -> indicates a rule for the replacement to follow.

EDIT to account for new cases:

It does become a bit trickier if you only want to return parts of rows based on certain conditions, so I've switched to Cases. There may be a more concise way to write this code, but I haven't been able to come up with it so far. Keep in mind, for this code to work properly, "i" needs to have been replaced with "I".

The first part of the pattern {a_?NonNegative, b_?NonNegative, c_?NonNegative, {d_, e_}} checks to make sure that a, b, c are not negative.

/; AllTrue[d, NonNegative] \[Or] AllTrue[e, NonNegative] tells it that a row is only valid if either the first 2 values of the matrix are non-negative OR the last 2 values are non-negative. If neither is non-negative, this row is skipped. /; is short for Condition (like a short If statement, essentially).

:> {a, b, c, Which[AllTrue[d~Join~e, NonNegative], {d, e}, AllTrue[d, NonNegative], d, AllTrue[e, NonNegative], e]} tells it what to return. :> is shorthand for RuleDelayed. If all the tests to the left of :> have been passed, then a, b, c will definitely be returned. The Which statement says that if both d and e are non-negative, return {d, e}. If only one of those is non-negative, then return whichever is non-negative.

Cases[
  list, 
  {a_?NonNegative, b_?NonNegative, c_?NonNegative, {d_, e_}} /; 
    AllTrue[d, NonNegative] \[Or] AllTrue[e, NonNegative] :> 
    {a, b, c, 
      Which[
        AllTrue[d~Join~e, NonNegative], {d, e}, 
        AllTrue[d, NonNegative], d, 
        AllTrue[e, NonNegative], e
      ]
    }
]
$\endgroup$
9
  • $\begingroup$ Thank you a lot, this is exactly what I was looking for!! $\endgroup$ Apr 22, 2020 at 0:08
  • $\begingroup$ I checked the documentation but still there are a couple of things I do not get. What is the meaning of Flatten[#], and the meaning of the &. Also, which part handles the selection of real numbers only? $\endgroup$ Apr 22, 2020 at 8:44
  • $\begingroup$ @PlasticMan I've added some ponters to help clarify how the function works. I also added a note at the bottom about your list using lower case "i" rather than upper case "I". $\endgroup$
    – MassDefect
    Apr 22, 2020 at 17:06
  • $\begingroup$ Wow, thanks a lot, that is perfect! $\endgroup$ Apr 22, 2020 at 19:05
  • $\begingroup$ @PlasticMan No problem, I’m glad it helped! $\endgroup$
    – MassDefect
    Apr 22, 2020 at 20:14
4
$\begingroup$

You can use Select as follows

list = {{1, 2, 2, {{0.45, -12}, {0.399896 - 0.547998*i, 7}}}, 
    {1, 2, 2, {{0.45, -12}, {0.399896 - 0.547998*i, 7}}}, 
    {1, 2, 2, {{0.45, -12}, {0.399896 - 0.547998*i, 7}}}, 
    {1, 2, 2, {{0.45, -12}, {0.399896 - 0.547998*i, 7}}}, 
    {1, 2, 2, {{0.45, 2}, {0.399896, 7}}}}; 
Select[Flatten[list , 1], Head[#1] == List && And@@(Positive /@ Flatten[{#1}]) & ]

which gives the desired result

{{{0.45, 2}, {0.399896, 7}}}
$\endgroup$
1
  • 1
    $\begingroup$ Thank you for your answer, that is pretty useful! $\endgroup$ Apr 22, 2020 at 0:08

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.