5
$\begingroup$

I have a nested list of the form:

test = {{"sample1", {"elema", "ppm", 238}, {"elemb", "ppm", 57}, 
                    {"elemc", "w%", 14.23}, {"elemd", "w%", 0.112}}, 
        {"sample2", {"elema", "ppm", 215}, {"elemb", "ppm", 13}, 
                    {"elemc", "w%", 12.13}, {"elemd", "w%", 0.095}}, 
        {"sample3", {"elema", "ppm", 219}, {"elemb", "ppm", 28}, 
                    {"elemc", "w%", 19.11}, {"elemd", "w%", 0.55}}};

I want to be able to select and order rows from this list based on another list:

samples = {"sample3", "sample1"};

and reorder elements based on their label,

order =  {"elemd","elemc","elemb","elema"};

to end up with something like:

{{0.55, 19.11, 28, 219}, {0.112, 14.23, 57, 238}}

The main aim is to have a robust re-ordering function of the form

trchem[data_, order_, sample_] := ...

I have played around with the following:

Intersection[sample, #] & /@ test

{{"sample1"}, {}, {"sample3"}}

Position[#, test[[All, 1]]] & /@ sample

{{}, {}}

Neither of which are quite right..

I'm sure this is really easy but I can't figure it out at the moment.

Any suggestions? Thanks.

$\endgroup$
0

4 Answers 4

4
$\begingroup$

Edit, I like this one more:

trchem[data_, order_, sample_] := Outer[
              Sequence @@ Cases[data, {#1, ___, {#2, _, x_}, ___} :> x] &, sample, order]

It is shorter but not faster since it scans whole list again and again.


Here is my first approach:

trchem[data_, order_, sample_] := Module[{temp},
       temp = Sequence @@ Cases[data, {#, y___} :> {y}] & /@ sample;

       Map[Function[x, (Sequence @@ Cases[x, {#, y_, z_} :> z] & /@ order)], temp]

      ]

trchem[test, order, samples]
{{0.55, 19.11, 28, 219}, {0.112, 14.23, 57, 238}}

I think the code is transparent, if there are quetions feel free to ask. I have choosen Map with Cases instead of Sort based solution. I think it can be faster but we will see.

There is Function[x, somemapping... which has to be there because there is double mapping, on order while on temp so twice "#&" will not work since mapping is entangled a little.


Remarks about OP's atempts:

  • Valid usage of Intersection would be:

    Intersection[test, Transpose@{samples}, SameTest -> (Equal @@ (First /@ {##}) &)]
    
    {{"sample1", {"elema", "ppm", 238}, <<3>>}, 
    {"sample3", {"elema", "ppm", 219},  <<3>>}}
    

But it does not preserve samples order. Also Intersection is deleting duplicates in its first argument so in, hypothetical, case where there are {"sample1", somedata1} and {"sample1", somedata2} you will loose one of them. It is also not efficient to do this if you don't have too.

  • Using Position may be interesting since it will preserve an order. (but I think Cases with Map will do the job better):

    Sequence @@ Position[test[[ ;; , 1]], #] & /@ samples
    Extract[test, %]
    
    {{"sample3", {"elema", "ppm", 219},  <<3>>}},
    {"sample1", {"elema", "ppm", 238}, <<3>>}}
    
$\endgroup$
5
  • $\begingroup$ I guess yours might be faster - I tried to stick closer to the OPs plan (maybe I wasn't so successful after all) :). Nice one! $\endgroup$ Commented Aug 22, 2013 at 7:28
  • $\begingroup$ @PinguinDirk It looks like second halfs of our ideas are quite similar. I used Map more because I feel comfortable with using it, also I do not have experience with sorting performance so this is brute force :) I will not be surprised if it is not optimal. $\endgroup$
    – Kuba
    Commented Aug 22, 2013 at 7:32
  • 1
    $\begingroup$ @geordie I've added couple of things about your approach. $\endgroup$
    – Kuba
    Commented Aug 22, 2013 at 9:46
  • $\begingroup$ I really like your edit. Thanks for the detailed explanations! $\endgroup$
    – geordie
    Commented Aug 28, 2013 at 13:09
  • $\begingroup$ @geordie That's great you like it. Thanks for good question that inspired me to play with Intersection :) $\endgroup$
    – Kuba
    Commented Aug 28, 2013 at 13:11
5
$\begingroup$

A bit long for a comment but can definitely be optimized:

trchem[data_, order_, sample_] := 
   Last /@ 
     Sort[#, Position[order, #1[[1]]][[1, 1]] < Position[order, #2[[1]]][[1, 1]] &]& /@ 
     Rest /@ Flatten[(Function[x, Select[data, #[[1]] == x &]] /@ sample), 1]

so

trchem[test,order,samples]

(using the params defined in the OP) gives the desired output.

What's going on?

I first select the entries we need using a Select (depending on the size of you list etc this could be optimized), by mapping it on the sample. After Flattening, we Map Rest on it, to get rid of the labels. Finally we Sort, using the Position in the order list. Mapping Last on that, we are done.

I tried to follow your logic/idea as much as possible. In regard to the two points you raise/ask at the end of your post, I can only refer you to the Documentation center (have a good look at the order of arguments in Position). I hope my approach gives you an idea... (I couldn't get Intersection in my answer :) )

$\endgroup$
1
  • $\begingroup$ This is good, thanks! Nice explanation. $\endgroup$
    – geordie
    Commented Aug 22, 2013 at 7:16
4
$\begingroup$

Here's a fairly simple rule-based approach.

First convert the input data to a list of rules:

testrules = #1 -> (#1 -> #3 &) @@@ {##2} & @@@ test

(* {"sample1" -> {"elema" -> 238, "elemb" -> 57, "elemc" -> 14.23, "elemd" -> 0.112}, 
 "sample2" -> {"elema" -> 215, "elemb" -> 13, "elemc" -> 12.13, "elemd" -> 0.095}, 
 "sample3" -> {"elema" -> 219, "elemb" -> 28, "elemc" -> 19.11, "elemd" -> 0.55}} *)

Then the required output is just:

order /. (samples /. testrules)

(* {{0.55, 19.11, 28, 219}, {0.112, 14.23, 57, 238}} *)
$\endgroup$
2
$\begingroup$

I know its not that pretty looking compared to other answers.but since i spent some time thinking about it i had to post it.

     trchem[data_, order_, sample_] := 
     Module[{}, testSample = data[[All, 1]];
     Partition[
      DeleteCases[Flatten[Table[If[sample[[#]] == testSample[[i]],
      DeleteCases[Flatten[Table[If[order[[#]] == data[[i, j, 1]],
           data[[i, j, 3]]], {j, 2, Length[data[[1]]]}] & /@ 
        Range[Length[order]]], Null]
         ], {i, 1, Length[data]}] & /@ Range[Length[sample]]], Null], 
      Length[order]]
      ]
$\endgroup$
3
  • $\begingroup$ @Kuba Yes u r right.I have edited it $\endgroup$
    – Hubble07
    Commented Aug 22, 2013 at 8:56
  • $\begingroup$ @Kuba Done.Actually im not able to understand that 3 underscore u have in ur code.it works even with 2.also whats the difference between 2 & 3 underscore $\endgroup$
    – Hubble07
    Commented Aug 22, 2013 at 10:07
  • $\begingroup$ You are right __ is enough, the ___ means there could be nothing :) like {"elema"}, but it is not this case so, as I said __ is enough. $\endgroup$
    – Kuba
    Commented Aug 22, 2013 at 10:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.