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