5
$\begingroup$

I have optical spectra in the following format:

spectra = {{x1,y1},{x2,y2},...,{xn,yn}}

Where x are wavenumbers and y is intensity. I want to modify my intensity, e.g. divide by 3. So I use the following code for that:

modyfiedspectra = Transpose[{spectra[[All, 1]], spectra[[All, 2]]/3}]

But when I have a list of different spectra, I don't know how to loop over it. For example I have a list of 2 spectra:

listofspectra = {{{x1,y1},{x2,y2},...,{xn,yn}},{{x1,y1},{y2,y2},...,{xn,yn}}}

And now I want to divide intensity of each spectra by different numbers. E.g:

divideby = {3,5}

So, output should be something like that:

(*{{{x1,y1/3},{x2,y2/3},...,{xn,yn/3}},{{x1,y1/5},{x2,y2/5},...,{xn,yn/5}}}*)

So the question: how to loop over a list of lists and apply a function with different arguments for each list?

$\endgroup$
6
$\begingroup$

Very similar to @march answer:

Given for example

listofspectra = {{{x1, y1}, {x2, y2}}, {{x11, y11}, {y22, y22}}};
divideby = {3, 5};

you start defining the function that will modify one spectra :

modifyspectra[spectra_, factor_] := 
 Transpose[{spectra[[All, 1]], spectra[[All, 2]]/factor}]

then you apply it to the list of spectras and corresponding factors :

modifyspectra @@@ Thread@{listofspectra, divideby}
(* Apply[modifyspectra, Thread[{listofspectra, divideby}], 1] *)

{{{x1, y1/3}, {x2, y2/3}}, {{x11, y11/5}, {y22, y22/5}}}

You can also use this very similar form (the arguments of the function is now a list containing the same former arguments):

modifyspectra2[{spectra_, factor_}] := 
     Transpose[{spectra[[All, 1]], spectra[[All, 2]]/factor}]

modifyspectra2 /@ Thread@{listofspectra, divideby}
(* Map[modifyspectra2, Thread@{listofspectra, divideby} ] *)

{{{x1, y1/3}, {x2, y2/3}}, {{x11, y11/5}, {y22, y22/5}}}

By the way, Thread@{listofspectra, divideby} can also be replaced with Transpose@{listofspectra, divideby}

$\endgroup$
  • $\begingroup$ I was right this moment trying to come up with a solution using Thread instead of MapThread in order to avoid a bunch of nested pure functions. +1. $\endgroup$ – march Oct 16 '15 at 20:24
  • $\begingroup$ Thanks, that works fine. One small question: what i should do if i want not just divide by any factor but want apply function with different arguments (list of arguments) to different spectra? E.g. i have function for bose-correction of my spectra, nba[wavenuber_,temperature_]:=... and i want use it as argument in your modifyspectra function. $\endgroup$ – Mr.Eight Oct 19 '15 at 12:25
  • $\begingroup$ I'm so sorry, i made a mistake. I realize how to did it! Thank you again! $\endgroup$ – Mr.Eight Oct 19 '15 at 13:15
6
$\begingroup$

Here's some versions. The first generalizes your method:

MapThread[Transpose[{#2[[All, 1]], #2[[All, 2]]/#1}] &, {divideby, listofspectra}]
MapThread[Function[{x}, {1, 1/#1} x] /@ #2 &, {divideby, listofspectra}]

Another slight variation:

f[num_, list_] := MapAt[#/num &, list, {All, 2}]
MapThread[f, {divideby, listofspectra}]

Significantly different:

{1, 1/#} & /@ divideby # & /@ Transpose@listofspectra // Transpose
$\endgroup$
5
$\begingroup$
lst = {{{"a", 1}, {"b", 2}, {"c", 3}}, {{"d", 10}, {"e", 20}, {"f", 30}, {"g", 40}}};

div[li_, di_] := li /. {a_, b_} :> {a, b/di}

MapThread[div, {lst, {3, 5}}]

{{{"a", 1/3}, {"b", 2/3}, {"c", 1}}, {{"d", 2}, {"e", 4}, {"f",
6}, {"g", 8}}}

$\endgroup$
5
$\begingroup$

If it's a one off kind of thing then I find just rewriting the values in the list easier.

listofspectra = {{{x1,y1},{x2,y2},{x3,y3}},{{a1,b1},{a2,b2},{a3,b3}}};

listofspectra[[1, All, 2]] = listofspectra[[1, All, 2]]/3

{{{x1, y1/3}, {x2, y2/3}, {x3, y3/3}}, {{a1, b1}, {a2, b2}, {a3, b3}}}

listofspectra[[2, All, 2]] = listofspectra[[2, All, 2]]/5

{{{x1, y1/3}, {x2, y2/3}, {x3, y3/3}}, {{a1, b1/5}, {a2, b2/5}, {a3, b3/5}}}

But the MapThread method in eldo's answer is more general.

$\endgroup$
  • $\begingroup$ @JackLaVigne yes I did. Thanks for pointing it out. I've edited my answer $\endgroup$ – rhuairahrighairidh Oct 16 '15 at 23:22
4
$\begingroup$

Generally, I do this with MapIndexed as the position information it passes to the function can be used to look up the values you need. For instance,

lst = {{{a1, b1}, {a2, b2}, {a3, b3}}, {{c1, d1}, {c2, d2}, {c3, d3}}};
factors = <|{1, 2} -> 3, {2, 2} -> 5|>;
MapIndexed[#1/Lookup[factors, Key@#2[[{1, 3}]], 1]&, lst, {-1}]
(* 
{{{a1, b1/3}, {a2, b2/3}, {a3, b3/3}}, 
 {{c1, d1/5}, {c2, d2/5}, {c3, d3/5}}}
*)
$\endgroup$

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.