lsh
March 16, 2021, 10:03pm
1
Hi All,
As the title suggestions I want to group a table by several keys, then output a nested array of the data.
So far the strategy I’ve come up with is:
{
const df = aq.from(data).groupby(['key1', 'key2']);
const {keys} = df.groups();
const grouped = [];
for (const [k,v] of Object.entries(keys)) {
if (!grouped[v]) {
grouped[v] = [];
}
grouped[v].push(data[k]);
}
return grouped;
}
I was wondering if there’s a cleaner way to do this?
Thank you!
jrus
March 17, 2021, 12:15am
2
You could try to use something like Python’s defaultdict to slightly simplify the code.
A google search turns up GitHub - jsoendermann/defaultdict-proxy: A Proxy based JavaScript port of python's defaultdict.
Probably not worth importing a library if you only have to do this a couple times though.
1 Like
lsh
March 17, 2021, 2:19am
3
Thank you! Yeah I only need it a few times, so it should be fine. Was mostly curious if there was an “idiomatic” solution.
jrus
March 17, 2021, 3:43am
4
I wouldn’t mark this as ‘solved’ just yet. Maybe someone else has a better idea.
1 Like
jrus
March 17, 2021, 4:04am
5
We can make things at least a tiny bit shorter, though maybe less legible depending on your intended audience:
{
const df = aq.from(data).groupby(['key1', 'key2']);
const grouped = [];
[].forEach.call(df.groups().keys, (x, i) =>
(grouped[x] ||= []).push(data[i]));
return grouped;
}
1 Like
lsh:
{
const df = aq.from(data).groupby(['key1', 'key2']);
const {keys} = df.groups();
const grouped = [];
for (const [k,v] of Object.entries(keys)) {
if (!grouped[v]) {
grouped[v] = [];
}
grouped[v].push(data[k]);
}
return grouped;
}
Go directly against ‘data’ with d3.groups . If you need to do some other ‘data-wrangling’ with Arquero prior to needing the final grouping, reference table.objects() as the first parameter to the d3.groups method:
{
const d3 = await require(“d3-array”);
// Sample data:
const data = [{key1:10, key2: 20, val: 1}, {key1:10, key2: 20, val: 2}, {key1:10, key2: 30, val: 3}];
return d3.groups(data, d => d.key1, d => d.key2)[0][1].map(([_,d]) => d)
}
1 Like
lsh
March 18, 2021, 5:43pm
7
I thought some more about this issue and made a draft PR to add a grouped output feature. In the discussion, Jeff explains more idiomatic ways to use a combination of aq.create()
, aq.rollup()
, and op.array_agg()
to achieve a similar effect.
1 Like
lsh
March 22, 2021, 10:43pm
8
As of Arquero v4.1.0 , aq.objects()
accepts the { grouped } option , which allows grouped output. Therefore, the idiomatic code for my question above would be:
aq.from(data)
.groupby(["key1", "key2"])
.objects({grouped: "entries"});
1 Like