RE: Request for help: working with multi-level lists
- To: mathgroup at smc.vnet.net
- Subject: [mg13667] RE: [mg13665] Request for help: working with multi-level lists
- From: "Ersek, Ted R" <ErsekTR at navair.navy.mil>
- Date: Sat, 15 Aug 1998 04:39:03 -0400
- Sender: owner-wri-mathgroup at wolfram.com
kcconnolly at aol.com wrote:
_____________________________
I have a list of 10 elements, each of which is a list of three elements
(let's say in each case an integer, a real number, and a string). I am
looking for the most elegant way to select those first-level elements
(i.e., the lists) whose integer element is equal to a particular value
(let's say "1"), and then to obtain the mean of the real number
elements of the lists selected. This seems as if it should be simple,
but everything I try leads to Part specification errors. Any help
would be greatly appreciated.
______________________________
You want to avoid programs that take elements out of a list or matrix
and use those elements to make a new list. This is often the first
thing new users think of, but it's inefficient to do this (with a long
list) using Mathematica. Consider (lst) below.
In[1]:=
lst={{1,0.84787,"text"},{1,0.4739,"text"},
{1,0.4108,"text"},{1,0.0771,"text"},
{2,0.7565,"text"},{0,0.0842,"text"},
{1,0.2260,"text"},{0,0.2148,"text"},
{4,0.8246,"text"},{1,0.6548,"text"},
{3,0.7183,"text"},{1,0.7181,"text"},
{1,0.9862,"text"},{0,0.5956,"text"}, {1,0.4053,"text"}};
Either of the lines below work very well for finding all the elements
that have the integer (1) as the first element. I happen to think the
line that uses Cases is much more readable. An important point is that
with Select you get a list of elements that make a test come out true.
With Cases you get a list of elements that match a pattern.
In[2]:=
selected=Select[lst, Part[#,1]===1&];
In[3]:=
selected=Cases[lst,{1,_,_}];
Now that you have a list of all elements with the first part being the
integer (1), you need to make a list of the real numbers in the second
part. A few choices are given below.
In[4]:=
reals=Part[Transpose[selected],2];
In[5]:=
reals=Map[Part[#,2]&, selected];
If you find the code for In[5] cryptic you could use the code in the
next line. The only down side is that you have to use a global
variable (ie. element2) that may get in your way at sometime. also the
less cryptic version isn't as concise.
In[6]:=
element2[expr_]:=Part[expr,2]
reals=Map[element2, selected];
In[8]:=
reals=selected/.{int_,rel_,str_}:>rel;
I like the code at In[8] best. It's very readable, and pretty fast too.
Once you have a list of real numbers it's there is one way to compute
the mean that stands out as most elegant. I give two versions of it.
First the longhand version, then the equivalent shorthand version.
In[9]:=
avr=Apply[Plus,reals]/Length[reals]
Out[9]=
0.533341
In[10]:=
avr=Plus@@reals/Length at reals
Out[10]=
0.533341
Warning:
I caution you against using {int_,rel_,str_}->rel at In[8] above. Why?
Suppose you give (rel) a value early during your session and then use
it in a pattern for (lhs->rhs) as below. Then you will get the wrong
result as I did below.
In[11]:=
rel=23;
....
....
....
In[87]:=
reals=p/.{int_,rel_,str_}->rel
Out[87]=
{23,23,23,23,23,23,23,23,23}
If you are thinking about using (lhs->rhs) with a pattern in (lhs) it's
much safer to use
(lhs:>rhs).
Ted Ersek