4
$\begingroup$

Let's assume that we have the following three lists

data1 = {{1, 2}, {2, 0.1}, {3, -3.2}, {4, 4.1}, {5, 0.2}};
data2 = {{1, -3}, {2, -2.1}, {3, 1.2}, {4, -3.1}, {5, -3.1}};
data3 = {{1, 0}, {2, 3.3}, {3, 0.003}, {4, 5.2}, {5, 3.1}};

As we can see, in all three lists the first elements of each sublist are the same. Now I want to create a new list data containing the first elements of each sublist and the sums of the second elements, i.e.,

data = {{1, 2 - 3 + 0}, {2, 0.1 - 2.1 + 3.3}, {3, -3.2 + 1.2 + 0.003}, {4, 4.1 - 3.1 + 5.2}, {5, 0.2 - 3.1 + 3.1}}

Any suggestions?

$\endgroup$
6
$\begingroup$

I suggest you use associations, not lists of pairs.

{asc1, asc2, asc3} = AssociationThread @@ Transpose[#] & /@ {data1, data2, data3};

asc1 + asc2 + asc3
(* <|1 -> -1, 2 -> 1.3, 3 -> -1.997, 4 -> 6.2, 5 -> 0.2|> *)

Look up KeyUnion and KeyIntersection for dealing with the cases when the set of keys are not exactly the same.


You may also use TimeSeries, though I am personally not very experienced with this construct, so I will only show the simplest example:

{ts1, ts2, ts3} = TimeSeries /@ {data1, data2, data3};

Now we can do

tsSum = ts1 + ts2 + ts3

Convert back to a list:

Normal[tsSum]
(* {{1, -1}, {2, 1.3}, {3, -1.997}, {4, 6.2}, {5, 0.2}} *)

Most operations that one might want to do on time series data, such as ListPlot or LinearModelFit, will work without needing to convert the TimeSeries back to a list.

$\endgroup$
  • $\begingroup$ No, this is not what I want. I want a new list as it is described in my post. $\endgroup$ – Vaggelis_Z Aug 25 at 10:59
  • $\begingroup$ You can convert the resulting Association to a list by doing List @@@ Normal@myassoc $\endgroup$ – Carl Lange Aug 25 at 11:14
  • 3
    $\begingroup$ @Vaggelis_Z Why do you want a new list? My point is that it is usually much more convenient to use associations for this type of data. What's the next thing you want to do with that list? Can it not be done with associations? Often, not only can it be done, it's also easier to do. And as Carl said, converting back to a list is trivial. (See also KeyValueMap.) $\endgroup$ – Szabolcs Aug 25 at 11:52
4
$\begingroup$
Values @ GroupBy[Join[data1, data2, data3], First, {#[[1, 1]], Total[#[[All, 2]]]} &]

{{1, -1}, {2, 1.3}, {3, -1.997}, {4, 6.2}, {5, 0.2}}

Also (thanks WReach):

KeyValueMap[List] @ GroupBy[Join[data1, data2, data3], First -> Last,Total]

{{1, -1}, {2, 1.3}, {3, -1.997}, {4, 6.2}, {5, 0.2}}

More generally, if the input lists are ragged lists:

KeyValueMap[List] @ GroupBy[Join[data1, data2, data3], First -> Rest, First@*Total]
$\endgroup$
  • $\begingroup$ +1. In the second case, we could write GroupBy[..., First -> Last, Total]. $\endgroup$ – WReach Aug 26 at 4:36
  • $\begingroup$ Thank you @WReach. Updated with the suggested form. $\endgroup$ – kglr Aug 26 at 4:47
3
$\begingroup$
{#[[1, 1]], #[[All, 2]] // Total} & /@ GatherBy[Join[data1, data2, data3], First]

{{1, -1}, {2, 1.3}, {3, -1.997}, {4, 6.2}, {5, 0.2}}

$\endgroup$
3
$\begingroup$

You can use Query:

{data1, data2, data3} // Transpose // Query[ All, Transpose /* {1 /* First, 2 /* Total} ]

{{1, -1}, {2, 1.3}, {3, -1.997}, {4, 6.2}, {5, 0.2}}

Alternatively we can initiate the Query using GatherBy as suggested by C. E.'s answer:

{data1, data2, data3} // RightComposition[
    Apply@Join,
    GatherBy[ #, First ]&,
    Query[ All, Transpose /* {1 /* First, 2 /* Total} ]
]
$\endgroup$
  • 1
    $\begingroup$ +1. In the first example, we can combine the transpositions: Transpose[{data1, data2, data3}, {3, 1, 2}] // Query[All, {1 /* First, 2 /* Total}] $\endgroup$ – WReach Aug 26 at 4:20
3
$\begingroup$

You can use MapThread to do this,

combiner[patt : ({x_, _} ..)] := {x, Total[{patt}[[All, 2]]]}

MapThread[combiner, {data1, data2, data3}]
(* {{1, -1}, {2, 1.3}, {3, -1.997}, {4, 6.2}, {5, 0.2}} *)
$\endgroup$
3
$\begingroup$
data1 = {{1, 2}, {2, 0.1}, {3, -3.2}, {4, 4.1}, {5, 0.2}};
data2 = {{1, -3}, {2, -2.1}, {3, 1.2}, {4, -3.1}, {5, -3.1}};
data3 = {{1, 0}, {2, 3.3}, {3, 0.003}, {4, 5.2}, {5, 3.1}};

data = {#[[1, 1]], 
    Inactive[Plus] @@ #[[All, 2]]} & /@
  (Join[data1, data2, data3] //
     GatherBy[#, First] &)

enter image description here

data // Activate

(* {{1, -1}, {2, 1.3}, {3, -1.997}, {4, 6.2}, {5, 0.2}} *)
$\endgroup$
2
$\begingroup$

Another short solution:

data = Rule @@@ Join[data1, data2, data3] // Merge[Total] // KeyValueMap[List]
$\endgroup$
1
$\begingroup$

I would also suggest using associations, but I would use Merge to total them:

{ass1, ass2, ass3} = Apply[Rule, {data1, data2, data3}, {2}];

Merge[{ass1, ass2, ass3}, Total]

<|1 -> -1, 2 -> 1.3, 3 -> -1.997, 4 -> 6.2, 5 -> 0.2|>

$\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.