如何将这些数据分组为可过滤的
How to group this data to be filterable
我正在使用 dc.js 创建具有如下架构的 "changesets" 数组的可交叉筛选图表:
id,
first_name, last_name, user_id,
created_at,
num_changes,
hashtags: [str],
total_add, total_mod, total_del,
buildings_add, buildings_mod, buildings_del,
pois_add, pois_mod, pois_del,
roads_add, roads_mod, roads_del,
road_km_add, road_km_mod, road_km_del,
waterways_add, waterways_mod, waterways_del,
waterway_km_add, waterway_km_mod, waterway_km_del
}
问题
我试图创建可过滤的堆叠条形图,条形代表 add/mod/del
,堆叠代表数据类型发生变化 buildings/pois/roads/waterways/road_km/waterway_km
。
任何 1 个变更集可以具有这些字段的任意组合,因此您不能将每个变更集与 1 个修改类型配对。有没有更好的方法来完成此分组,我可以在其中对图表应用过滤器?
我试过的代码
我的图表显示使用了正确的数据,但我的设置方式使图表无法被过滤。最初我将维度写为 crossfilter.dimension(d => ['add', 'mod', 'del'], true)
以便每个变更集都显示在每个容器中。但是因为所有的变更集都会共享 add/mod/del,所以没有过滤任何内容。
然后我在这里看到了过滤器堆栈示例:https://github.com/dc-js/dc.js/blob/develop/web-src/examples/filter-stacks.html
并且我尝试运行编辑的多键维度
let editDim = ndx.dimension(d => {
let rt = []
stackKeys.forEach(key => {
editStacks.forEach(stack => {
if (d[sAcc(stack,key)]) {
rt.push(key + '.' + stack)
}
})
})
return rt
}, true)
此方法看起来非常接近,但过滤堆栈不会在其他图表上产生正确的结果。
似乎无论我选择从该图表中过滤掉什么,其他图表都会产生 0.
这是一个 jsfiddle,其中我有 1 个使用多键方法的堆叠条形图,1 个使用 ['add'、'mod'、[=51= 的堆叠条形图]] 关键方法和 1 个常规条形图与 results/filtering 进行比较。
为了将变更集分成几组,我使用了一个自定义缩减器,它将数据转换成看起来像
的东西
{
key: 'add',
value: {
add: {
buildings': 42,
pois: 12,
roads: 1,
waterway: 2,
waterway_km: 0.003,
road_km: 0
},
mod: {...}
del: {...}
}
}
维度分组为 ['add', 'mod', 'del']
并且使用
创建堆栈
const editStacks = ['buildings', 'pois', 'roads', 'waterways']
editStacks.forEach((stack, i) => {
// first is group, others are stacked
let action = i ? 'stack' : 'group'
chart[action](group, stack, d => d.value[d.key][stack])
})
在多键方法中,值被转换为
{key: 'add', value: {building, roads, pois, waterways}}
使用这个函数
all: function () {
var all = group.all()
var m = {}
all.forEach(kv => {
let [k,s] = kv.key.split('.')
m[k] = m[k] || {}
m[k][s] = kv.value[k][s]
})
return Object.keys(m).map(key => {
return {key, value: m[key]}
})
}
因此,为了让每个部分都可以过滤堆积条,我必须结合使用在中找到的答案
并在
要构造一个可过滤的维度,您必须使用第一个 link 中描述的方法在您的维度上创建一个自定义 filterHandler,该维度只是按元素分组
let dimension = ndx.dimension(d => d)
然后我通过使用 groupAll 并创建一个对象来构造我的组
{ key.stack: value }}
使用 reduce 函数。
然后我通过为组创建一个“全部”方法将它转换回标准组
如第二个 link.
中所述
function reduceAdd (p, v) {
keys.forEach(k => {
stacks.forEach(s => {
p[`${k}.${s}`] += v[accessor(k, s)] || 0
})
})
return p
}
function reduceRemove (p, v) {
keys.forEach(k => {
stacks.forEach(s => {
p[`${k}.${s}`] -= v[accessor(k, s)] || 0
})
})
return p
}
function reduceInit () {
let p = {}
keys.forEach(k => {
stacks.forEach(s => {
p[`${k}.${s}`] = 0
})
})
return p
}
function stackedGroup (group) {
return {
all: function () {
var all = Object.entries(group.value()).map(([key, value]) => ({ key, value }))
var m = {}
all.forEach(kv => {
let [k, s] = kv.key.split('.')
m[k] = m[k] || {}
m[k][s] = kv.value
})
return Object.keys(m).map(key => {
return { key, value: m[key] }
})
}
}
}
let group = dimension.groupAll().reduce(reduceAdd, reduceRemove, reduceInit)
group = stackedGroup(group)
最后,您必须重新定义第一个 link 中提到的过滤器处理程序。这个过滤器是我用来过滤的
“至少有 1 x 的变更集”,其中“x”是某种类型的编辑,例如“添加建筑物”或“修改道路”
chart.filterHandler((dim, filters) => {
if (filters && filters.length) {
dim.filterFunction((r) => {
return filters.some((c) => {
//the changeset must have a value in this field to be left in the chart
let [stack, field] = c[0].split('.')
return r[accessor(field, stack)] > 0
})
})
} else {
dim.filter(null)
}
return filters
})
在那之后,主题标签图表显示出意想不到的结果,所以我也不得不为主题标签图表做一个类似的维度和分组。
我相信这是因为主题标签图表是用数组维度定义的,但将其分组为 d => d
并定义自定义 filterHandler 产生了令我满意的结果。
两个图表都可以过滤以查找诸如“有多少带有#Kaart 标签的变更集有道路修改”等问题的结果。
再次感谢@Gordon!
我正在使用 dc.js 创建具有如下架构的 "changesets" 数组的可交叉筛选图表:
id,
first_name, last_name, user_id,
created_at,
num_changes,
hashtags: [str],
total_add, total_mod, total_del,
buildings_add, buildings_mod, buildings_del,
pois_add, pois_mod, pois_del,
roads_add, roads_mod, roads_del,
road_km_add, road_km_mod, road_km_del,
waterways_add, waterways_mod, waterways_del,
waterway_km_add, waterway_km_mod, waterway_km_del
}
问题
我试图创建可过滤的堆叠条形图,条形代表 add/mod/del
,堆叠代表数据类型发生变化 buildings/pois/roads/waterways/road_km/waterway_km
。
任何 1 个变更集可以具有这些字段的任意组合,因此您不能将每个变更集与 1 个修改类型配对。有没有更好的方法来完成此分组,我可以在其中对图表应用过滤器?
我试过的代码
我的图表显示使用了正确的数据,但我的设置方式使图表无法被过滤。最初我将维度写为 crossfilter.dimension(d => ['add', 'mod', 'del'], true)
以便每个变更集都显示在每个容器中。但是因为所有的变更集都会共享 add/mod/del,所以没有过滤任何内容。
然后我在这里看到了过滤器堆栈示例:https://github.com/dc-js/dc.js/blob/develop/web-src/examples/filter-stacks.html
并且我尝试运行编辑的多键维度
let editDim = ndx.dimension(d => {
let rt = []
stackKeys.forEach(key => {
editStacks.forEach(stack => {
if (d[sAcc(stack,key)]) {
rt.push(key + '.' + stack)
}
})
})
return rt
}, true)
此方法看起来非常接近,但过滤堆栈不会在其他图表上产生正确的结果。 似乎无论我选择从该图表中过滤掉什么,其他图表都会产生 0.
这是一个 jsfiddle,其中我有 1 个使用多键方法的堆叠条形图,1 个使用 ['add'、'mod'、[=51= 的堆叠条形图]] 关键方法和 1 个常规条形图与 results/filtering 进行比较。
为了将变更集分成几组,我使用了一个自定义缩减器,它将数据转换成看起来像
的东西{
key: 'add',
value: {
add: {
buildings': 42,
pois: 12,
roads: 1,
waterway: 2,
waterway_km: 0.003,
road_km: 0
},
mod: {...}
del: {...}
}
}
维度分组为 ['add', 'mod', 'del']
并且使用
const editStacks = ['buildings', 'pois', 'roads', 'waterways']
editStacks.forEach((stack, i) => {
// first is group, others are stacked
let action = i ? 'stack' : 'group'
chart[action](group, stack, d => d.value[d.key][stack])
})
在多键方法中,值被转换为
{key: 'add', value: {building, roads, pois, waterways}}
使用这个函数
all: function () {
var all = group.all()
var m = {}
all.forEach(kv => {
let [k,s] = kv.key.split('.')
m[k] = m[k] || {}
m[k][s] = kv.value[k][s]
})
return Object.keys(m).map(key => {
return {key, value: m[key]}
})
}
因此,为了让每个部分都可以过滤堆积条,我必须结合使用在中找到的答案
要构造一个可过滤的维度,您必须使用第一个 link 中描述的方法在您的维度上创建一个自定义 filterHandler,该维度只是按元素分组
let dimension = ndx.dimension(d => d)
然后我通过使用 groupAll 并创建一个对象来构造我的组
{ key.stack: value }}
使用 reduce 函数。
然后我通过为组创建一个“全部”方法将它转换回标准组
如第二个 link.
function reduceAdd (p, v) {
keys.forEach(k => {
stacks.forEach(s => {
p[`${k}.${s}`] += v[accessor(k, s)] || 0
})
})
return p
}
function reduceRemove (p, v) {
keys.forEach(k => {
stacks.forEach(s => {
p[`${k}.${s}`] -= v[accessor(k, s)] || 0
})
})
return p
}
function reduceInit () {
let p = {}
keys.forEach(k => {
stacks.forEach(s => {
p[`${k}.${s}`] = 0
})
})
return p
}
function stackedGroup (group) {
return {
all: function () {
var all = Object.entries(group.value()).map(([key, value]) => ({ key, value }))
var m = {}
all.forEach(kv => {
let [k, s] = kv.key.split('.')
m[k] = m[k] || {}
m[k][s] = kv.value
})
return Object.keys(m).map(key => {
return { key, value: m[key] }
})
}
}
}
let group = dimension.groupAll().reduce(reduceAdd, reduceRemove, reduceInit)
group = stackedGroup(group)
最后,您必须重新定义第一个 link 中提到的过滤器处理程序。这个过滤器是我用来过滤的 “至少有 1 x 的变更集”,其中“x”是某种类型的编辑,例如“添加建筑物”或“修改道路”
chart.filterHandler((dim, filters) => {
if (filters && filters.length) {
dim.filterFunction((r) => {
return filters.some((c) => {
//the changeset must have a value in this field to be left in the chart
let [stack, field] = c[0].split('.')
return r[accessor(field, stack)] > 0
})
})
} else {
dim.filter(null)
}
return filters
})
在那之后,主题标签图表显示出意想不到的结果,所以我也不得不为主题标签图表做一个类似的维度和分组。
我相信这是因为主题标签图表是用数组维度定义的,但将其分组为 d => d
并定义自定义 filterHandler 产生了令我满意的结果。
两个图表都可以过滤以查找诸如“有多少带有#Kaart 标签的变更集有道路修改”等问题的结果。
再次感谢@Gordon!