I want to join associations in a dataset which share identical keys specified by the list sameAt
and merge them by taking a Mean
of keys specified by meanAt
and taking First
of the rest of keys. Below is a minimum example, not even doing what I really want (see later).
ds = {
<|"a" -> 0, "b" -> "b1", "c" -> "c1", "x" -> 1, "y" -> 7|>,
<|"a" -> 1, "b" -> "b1", "c" -> "c2", "x" -> 2, "y" -> 8|>,
<|"a" -> 0, "b" -> "b2", "c" -> "c3", "x" -> 3, "y" -> 9|>,
<|"a" -> 1, "b" -> "b2", "c" -> "c4", "x" -> 4, "y" -> 10|>,
<|"a" -> 0, "b" -> "b1", "c" -> "c5", "x" -> 2, "y" -> 10|>,
<|"a" -> 1, "b" -> "b1", "c" -> "c6", "x" -> 3, "y" -> 11|>,
<|"a" -> 0, "b" -> "b2", "c" -> "c7", "x" -> 4, "y" -> 12|>,
<|"a" -> 1, "b" -> "b2", "c" -> "c8", "x" -> 5, "y" -> 13|>
};
sameAt = {"a", "b"}; (* associations with identical values at these keys should be merged *)
meanAt = {"x", "y"}; (* merging should be Mean at these keys, First at rest *)
Query[N @* Mean] /@ GatherBy[ds, (#a && #b) &]
{<|"a" -> 0., "b" -> "b1", "c" -> 0.5 ("c1" + "c5"), "x" -> 1.5, "y" -> 8.5|>, <|"a" -> 1., "b" -> "b1", "c" -> 0.5 ("c2" + "c6"), "x" -> 2.5, "y" -> 9.5|>, <|"a" -> 0., "b" -> "b2", "c" -> 0.5 ("c3" + "c7"), "x" -> 3.5, "y" -> 10.5|>, <|"a" -> 1., "b" -> "b2", "c" -> 0.5 ("c4" + "c8"), "x" -> 4.5, "y" -> 11.5|>}
As can be seen, my method is not general enough: had to manually specify grouping criterion #a && #b &
; and averaging is not restricted for the keys "x"
and "y"
. In reality, I would like to make this work with:
- any number of keys in
sameAt
andmeanAt
(but see last point); - any number of mergable "copies", not just two (so
JoinAcross
is no good); - possibly specifying a different merging operator for each key, not just
First
andMean
;
A general approach, where each key has a specific aggregator defined:
sameAt = {"a", "b"};
op = <|"a" -> First, "b" -> First, "c" -> Last, "x" -> N@*Mean, "y" -> Mean|>;
MapThread[Apply, {op, Merge[#, List]}] & /@ GatherBy[ds, Query[sameAt]]
{<|"a" -> 0, "b" -> "b1", "c" -> "c5", "x" -> 1.5, "y" -> 17/2|>, <|"a" -> 1, "b" -> "b1", "c" -> "c6", "x" -> 2.5, "y" -> 19/2|>, <|"a" -> 0, "b" -> "b2", "c" -> "c7", "x" -> 3.5, "y" -> 21/2|>, <|"a" -> 1, "b" -> "b2", "c" -> "c8", "x" -> 4.5, "y" -> 23/2|>}
QUESTION
Can this be solved elegantly (being simpler than my above example) within the Dataset
-framework, with a single Query
? (If not, what is missing from the SQL-like Wolfram language that could make this work easily? Perhaps we can expect new functionality to be added, as it is hinted e.g. here.)