Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast extraction of elements from nested lists

This is a basic question on list manipulation in Mathematica. I have a large list where each element has the following schematic form: {List1, List2,Number}. For e.g.,

a = {{{1,2,3},{1,3,2},5},{{1,4,5},{1,0,2},10},{{4,5,3},{8,3,4},15}}}.

I want to make a new lists which only has some parts from each sublist. Eg., pick out the third element from each sublist to give {5,10,15} from the above. Or drop the third element to return {{{1,2,3},{1,3,2}},{{1,4,5},{1,0,2}},{{4,5,3},{8,3,4}}}.

I can do this by using the table command to construct new lists, e.g.,

Table[a[[i]][[3]],{i,1,Length[a]}

but I was wondering if there was a must faster way which would work on large lists.

like image 876
bartimaeus Avatar asked Oct 25 '12 05:10

bartimaeus


3 Answers

As the authors suggest, the approaches based on Part need well-formed data, but Cases is built for robust separation of Lists:

Using your a,

a = {{{1, 2, 3}, {1, 3, 2}, 5}, {{1, 4, 5}, {1, 0, 2}, 
10}, {{4, 5, 3}, {8, 3, 4}, 15}};

Cases[a,{_List,_List,n_}:>n,Infinity]

{5, 10, 15}

The other pieces of a record can be extracted by similar forms.

Part-based approaches will gag on ill-formed data like:

badA = {{{1, 2, 3}, {1, 3, 2}, 5}, {{1, 4, 5}, {1, 0, 2}, 
10}, {{4, 5, 3}, {8, 3, 4}, 15}, {baddata}, {{1, 2, 3}, 4}};

badA[[All,3]]

{{{1, 2, 3}, {1, 3, 2}, 5}, {{1, 4, 5}, {1, 0, 2},
10}, {{4, 5, 3}, {8, 3, 4}, 15}, {baddata}, {{1, 2, 3},
4}}[[All, 3]]

,but Cases will skip over garbage, operating only on conforming data

Cases[badA, {_List, _List, s_} :> s, Infinity]

{5, 10, 15}

hth,

Fred Klingener

like image 38
Fred Klingener Avatar answered Nov 04 '22 16:11

Fred Klingener


In Mathematica version 5 and higher, you can use the keyword All in multiple ways to specify a list traversal.

For instance, instead of your Table, you can write

a[[All,3]]

Here Mathematica converts All into all acceptable indices for the first dimension then takes the 3rd one of the next dimension.

It is usually more efficient to do this than to make a loop with the Mathematica programming language. It is really fine for homogenous lists where the things you want to pick or scan through always exist.

Another efficient notation and shortcut is the ;; syntax:

a[[ All, 1 ;; 2]]

will scan the first level of a and take everything from the 1st to the 2st element of each sublist, exactly like your second case.

In fact All and ;; can be combined to any number of levels. ;; can even be used in a way similar to any iterator in Mathematica:

a[[ start;;end;;step ]]

will do the same things as

Table[ a[[i]], {i,start,end,step}]

and you can omit one of start, end or step, it is filled with its default of 1, Length[(of the implicit list)], and 1.

Another thing you might want to lookup in Mathematica's Help are ReplacePart and MapAt that allow programmatic replacement of structured expressions. The key thing to use this efficiently is that in ReplacePart you can use patterns to specify the coordinates of the things to be replaced, and you can define functions to apply to them.

Example with your data

ReplacePart[a, {_, 3} -> 0]

will replace every 3rd part of every sublist with 0.

ReplacePart[a, {i : _, 3} :> 2*a[[i, 3]]]

will double every 3rd part of every sublist.

like image 58
ogerard Avatar answered Nov 04 '22 16:11

ogerard


You can use Part (shorthand [[...]]) for this :

a[[All, 3]]

a[[All, {1, 2}]]
like image 1
b.gatessucks Avatar answered Nov 04 '22 14:11

b.gatessucks