Перерисовывание гистограмм с помощью Crossfilter и D3
Я адаптирую библиотеку Crossfilter, чтобы визуализировать некоторые твиты, которые я собирал из Олимпиада. Я пытаюсь существенно расширить исходный пример двумя способами:
- Вместо отображения списков полетов на основе исходного набора данных я хочу отображать списки элементов в другом наборе данных, набранном элементами, выбранными в настоящее время перекрестным фильтром.
- Переключение между различными источниками данных и перезагрузка гистограмм и таблиц.
У меня есть часть (1), работающая как и планировалось. Однако часть (2) дает мне некоторую проблему. В настоящее время я изменяю набор данных, выбирая новый "спорт" для отображения или выбора нового сводного алгоритма. При переключении любого из них я считаю, что сначала должен удалить фильтры, диаграммы и списки, которые были созданы и отображены ранее, а затем перезагрузить новые данные.
Однако, будучи немного новым для визуализации переднего конца, особенно D3 и Crossfilter, я не понял, как это сделать, и я не уверен, как лучше всего сформулировать вопрос.
У меня есть рабочий пример моей проблемы здесь. Выбрав диапазон по дате, а затем переключитесь с Archery на Fencing, то выбор reset показывает хороший пример того, что неправильно: не все новые данные построены.
![Screen image of Fencing data after switching sports while filtering on Date]()
Как сказано, большая часть кода вытягивается из примера Crossfilter и Учебник по созданию радиальных визуализаций. Вот некоторые из key code фрагментов, которые, по моему мнению, актуальны:
Выбор нового источника данных:
d3.selectAll("#sports a").on("click", function (d) {
var newSport = d3.select(this).attr("id");
activate("sports", newSport);
reloadData(activeLabel("sports"), activeLabel("methods"));
});
d3.selectAll("#methods a").on("click", function (d) {
var newMethod = d3.select(this).attr("id");
activate("methods", newMethod);
reloadData(activeLabel("sports"), activeLabel("methods"));
});
Перезагрузка данных:
function reloadData(sportName, methodName) {
var filebase = "/tweetolympics/data/tweet." + sportName + "." + methodName + ".all.";
var summaryList, tweetList, remaining = 2;
d3.csv(filebase + "summary.csv", function(summaries) {
summaries.forEach(function(d, i) {
d.index = i;
d.group = parseInt(d.Group);
d.startTime = parseTime(d.Start);
d.meanTime = parseTime(d.Mean);
});
summaryList = summaries;
if (!--remaining)
plotSportData(summaryList, tweetList);
});
d3.csv(filebase + "groups.csv", function(tweets) {
tweets.forEach(function(d, i) {
d.index = i;
d.group = parseInt(d.Group);
d.date = parseTime(d.Time);
});
tweetList = tweets;
if (!--remaining)
plotSportData(summaryList, tweetList);
});
}
И загрузите перекрестный фильтр, используя данные:
function plotSportData(summaries, tweets) {
// Create the crossfilter for the relevant dimensions and groups.
var tweet = crossfilter(tweets),
all = tweet.groupAll(),
date = tweet.dimension(function(d) { return d3.time.day(d.date); }),
dates = date.group(),
hour = tweet.dimension(function(d) { return d.date.getHours() + d.date.getMinutes() / 60; }),
hours = hour.group(Math.floor),
cluster = tweet.dimension(function(d) { return d.group; }),
clusters = cluster.group();
var charts = [
// The first chart tracks the hours of each tweet. It has the
// standard 24 hour time range and uses a 24 hour clock.
barChart().dimension(hour)
.group(hours)
.x(d3.scale.linear()
.domain([0, 24])
.rangeRound([0, 10 * 24])),
// more charts added here similarly...
];
// Given our array of charts, which we assume are in the same order as the
// .chart elements in the DOM, bind the charts to the DOM and render them.
// We also listen to the chart brush events to update the display.
var chart = d3.selectAll(".chart")
.data(charts)
.each(function(chart) { chart.on("brush", renderAll)
.on("brushend", renderAll); });
// Render the initial lists.
var list = d3.selectAll(".list")
.data([summaryList]);
// Print the total number of tweets.
d3.selectAll("#total").text(formatNumber(all.value()));
// Render everything..
renderAll();
Я предполагаю, что я должен начать plotSportData
с чего-то, что очищает старый набор данных, но я не уверен, что это должно выглядеть. Любые предложения или мысли будут в высшей степени оценены.
Ответы
Ответ 1
После ночного сна решение пришло ко мне.
Мне просто нужно позвонить
d3.selectAll(".chart").selectAll("svg").remove();
в начале plotSportData
, который будет захватывать любые гистограммы, вложенные в div .chart
, и удалять их. И если нет элементов для удаления, это будет no-op.