8
$\begingroup$

I have 2 lists, that have the following form:

List1 = {{0.0, val10}, {0.1, val11}, {0.2, val12},...}
List2 = {{0.0, val20}, {0.1, val21}, {0.2, val22},...}

I would like to make a normalized ListDiff defined in such a way that it has the form:

ListDiff = {{0.0, (val10 - val20)/val10}, {0.1, (val11 - val21)/val11}, {0.2, (val12 - val22)/val12},...}

Is there any procedure in Mathematica to perform this operation?

$\endgroup$

11 Answers 11

4
$\begingroup$
ListDiff = Transpose[{
   List1[[All, 1]],
   Subtract[1., List2[[All, 2]]/List1[[All, 2]]]
   }]

is slightly faster than Bob Hanlon's second proposal...

$\endgroup$
8
$\begingroup$

Another approach,

List1 = {{0.0, val10}, {0.1, val11}, {0.2, val12}};
List2 = {{0.0, val20}, {0.1, val21}, {0.2, val22}};

{#[[1, 1]], (#[[1, 2]] - #[[2, 2]])/#[[1, 2]]} & /@ 
 Transpose[{List1, List2}]

(* {{0., (val10 - val20)/val10}, {0.1, (val11 - val21)/val11}, {0.2, (
  val12 - val22)/val12}} *)

Or

Transpose@{List1[[All, 1]], (List1[[All, 2]] - List2[[All, 2]])/
   List1[[All, 2]]}
$\endgroup$
6
$\begingroup$

Another way:

ListDiff = List1;
ListDiff[[All, 2]] -= List2[[All, 2]];
ListDiff[[All, 2]] /= List1[[All, 2]];

ListDiff

{{0., (val10 - val20)/val10}, {0.1, (val11 - val21)/val11}, {0.2, (val12 - val22)/val12}}

$\endgroup$
1
  • $\begingroup$ Looks nice, thank you! $\endgroup$
    – newt
    Dec 8, 2017 at 20:05
5
$\begingroup$

You might consider using Association objects instead. For example:

List1 = {{0.0, val10}, {0.1, val11}, {0.2, val12}};
List2 = {{0.0, val20}, {0.1, val21}, {0.2, val22}};

assoc1 = Association[Rule @@@ List1]
assoc2 = Association[Rule @@@ List2]

<|0. -> val10, 0.1 -> val11, 0.2 -> val12|>

<|0. -> val20, 0.1 -> val21, 0.2 -> val22|>

Arithmetic with associations acts on the values. So:

(assoc1 - assoc2)/assoc1

<|0. -> (val10 - val20)/val10, 0.1 -> (val11 - val21)/val11, 0.2 -> (val12 - val22)/val12|>

$\endgroup$
4
$\begingroup$
f1 = {#[[1, 1]], (#2 - #1)/#1 & @@ #[[All, 2]]} & /@ GatherBy[Join[##], First] &;
f2 = {#[[1, 1]], (#2 - #1)/#1 & @@ #[[All, 2]]} & /@ SplitBy[Sort@Join[##], First] &;

f1[List1, List2]

{{0., (-val10 + val20)/val10}, {0.1, (-val11 + val21)/ val11}, {0.2, (-val12 + val22)/val12}}

f1[List1, List2] == f2[List1, List2]

True

$\endgroup$
4
$\begingroup$

You can use $\texttt{Cases}$ and pattern-matching:

Cases[
  Transpose[{List1, List2}],
  {{n_, v1_}, {n_, v2_}} :> {n, (v2 - v1)/v1}
]
$\endgroup$
1
  • $\begingroup$ Thanks for this! $\endgroup$
    – newt
    Dec 8, 2017 at 20:05
3
$\begingroup$

You have a low reputation, so I'm assuming you are fairly new to Mathematica. As such, here is an inelegant (but easily understood) approach:

Table[
   {List1[[i, 1]], (List1[[i, 2]] - List2[[i, 2]])/List1[[i, 2]]}, 
  {i, Length[List1]}]

And a word of advice: Never use upper-case to start the name of a variable, as it may conflict with an internal function in Mathematica. This is especially concerning if your variable is List1--so close to the function List. And ListDiff might be similar to the 30 or so internal functions that begin with "List".

$\endgroup$
1
  • $\begingroup$ Thank you very much. I thought I would have to write a cycle. Never get used to Tables... And, I'm assuming you forgot division symbol. $\endgroup$
    – newt
    Dec 7, 2017 at 18:03
3
$\begingroup$

You can use MapThread.

MapThread[
 {First[#1], First[1 - Rest[#2] / Rest[#1]]}&, {List1, List2}]

Evaluating the code above, returns

{ { 0. , 1 - val20/val10 }, 
  { 0.1, 1 - val21/val11 },
  { 0.2, 1 - val22/val12 } }

(input lists are concatenated so as to allow evaluation).

MapThread applies a function on tuples, taken in a successive manner from the input lists (see code above).

The first tuple, contains the first element from every input list, the second tuple consists of the second element in each input list and so on.

Since, in this example, we are dealing with only two input lists (List1 and List2), the function that is applied on successive input tuples, receives two arguments ie #1 and #2.

These, are numbered slots (see Slot). They stand for the successive inputs, fed into the function argument of MapThread.

In this particular case we are dealing with, both of them are lists, with two entries eg in the case of the first tuple, #1 stands for {0.0, val10} and #2 stands for {0.0, val20}.

Continuing with the example of the first input tuple, evaluating First[#1] returns 0. (this is the First item in {0.0, val10}) while Rest[#1] returns {val01} (the second entry of {0.0,val10}-notice the braces).

Similarly, Rest[#2] returns {val20} (because #2 is equal to {0.0, val20}) and if we combine all the parts of the function together we get {0.,1-val20/val10}.

The rest of the output is constructed along similar lines.

$\endgroup$
1
  • $\begingroup$ Thank you for the detailed explanation! $\endgroup$
    – newt
    Dec 8, 2017 at 20:06
2
$\begingroup$

Using Merge:

alist = {{0.0, val10}, {0.1, val11}, {0.2, val12}};
blist = {{0.0, val20}, {0.1, val21}, {0.2, val22}};

Merge[{Rule @@@ alist, 
     Rule @@@ blist}, (First@# - Last@#)/(First@#) &] // Normal // 
  ReplaceAll[Rule -> List] // Column

Result:

enter image description here

$\endgroup$
2
$\begingroup$
l1 = {{0.0, val10}, {0.1, val11}, {0.2, val12}};
l2 = {{0.0, val20}, {0.1, val21}, {0.2, val22}};

Using Thread and MapApply:

{#1[[1]], 1 - #2[[2]]/#1[[2]]} & @@@ Thread[{l1, l2}]

Result:

enter image description here

Or using GroupBy:

l3 = Catenate[{l1, l2}];

gby = GroupBy[l3, #[[1]] &, 1 - #2/#3 & @@ Union@## & @@ Transpose@# &]

Thread[{Keys@#, Values@#}] &@gby

Result:

enter image description here

$\endgroup$
1
$\begingroup$
a = {{0.0, val10}, {0.1, val11}, {0.2, val12}};
b = {{0.0, val20}, {0.1, val21}, {0.2, val22}};

Using MapApply (new in 13.1)

{c, d} = Association @* MapApply[Rule] /@ {a, b};

Display as Dataset

(c - d) / c // Dataset

enter image description here

Convert to List

(c - d) / c // KeyValueMap[List] // TableForm

enter image description here

If we want to change a and b inline, we can use ApplyTo (new in 12.2)

{a, b} //= Map[Association @* MapApply[Rule]];

(a - b) / a // Dataset

(* output like above *)

$\endgroup$

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.