Здравствуйте, это Эйдан Вольф, вице-президент по продукту в Datazar. Сегодня я покажу вам, как создать модуль NotebookJS за 9 минут.
Если вы не знакомы с NotebookJS, это облачная система научных вычислений, полностью построенная на Javascript. Что делает NotebookJS еще более мощным, так это добавление модулей, которые создаются и поддерживаются нашим замечательным сообществом Datazar.
К концу этого руководства ваш модуль будет мгновенно доступен для включения в любую записную книжку и, возможно, внесет свой вклад в новый исследовательский проект, который изменит мир! Разве это не захватывающе?
Начиная
Используя ваш любимый редактор кода, создайте новый файл JavaScript. Сделав это, скопируйте и вставьте этот стартовый код.
//nb.js Tutorial Module nb.tutorial = function () {}; nb.tutorial.test=function() {return “this module is working!”;};
Если вы хотите, чтобы переменная или функция были доступны за пределами вашего модуля, вы должны сначала включить «nb.[moduleName]». перед ссылкой на объект. В этом примере имя моего модуля — nb.tutorial, и я включил свою первую функцию «nb.tutorial.test». Я мог бы загрузить этот модуль прямо сейчас и вызвать nb.tutorial.test() из своей записной книжки, и он отобразит мое сообщение!
Модули Notebook могут в полной мере использовать D3.js, поэтому для этого руководства мы собираемся создать визуализацию D3, готовую к NotebookJS.
Ради скорости позаимствуем замечательную визуализацию аккордовой диаграммы Стюарта Нойса. http://bl.ocks.org/StewartNoyce/9532973
1. Сначала создайте новую функцию внутри вашего файла.
nb.tutorial.chord = function (data) { //where we'll put our D3 code! }
2. Затем скопируйте и вставьте код из ‹script›‹/script› из примера диаграммы аккордов Стюарта Нойса в свой код.
(Для этого урока нам нужна только функция Chord)
function Chord(container, options, matrix) {
// initialize the chord configuration variables
var config = {
width: 640,
height: 560,
rotation: 0,
textgap: 26,
colors: ["#7fc97f", "#beaed4", "#fdc086", "#ffff99", "#386cb0", "#f0027f", "#bf5b17", "#666666"]
};
// add options to the chord configuration object
if (options) {
extend(config, options);
}
// set chord visualization variables from the configuration object
var offset = Math.PI * config.rotation,
width = config.width,
height = config.height,
textgap = config.textgap
colors = config.colors;
// set viewBox and aspect ratio to enable a resize of the visual dimensions
var viewBoxDimensions = "0 0 " + width + " " + height,
aspect = width / height;
if (config.gnames) {
gnames = config.gnames;
} else {
// make a list of names
gnames = [];
for (var i=97; i<matrix.length; i++) {
gnames.push(String.fromCharCode(i));
}
}
// start the d3 magic
var chord = d3.layout.chord()
.padding(.05)
.sortSubgroups(d3.descending)
.matrix(matrix);
var innerRadius = Math.min(width, height) * .31,
outerRadius = innerRadius * 1.1;
var fill = d3.scale.ordinal()
.domain(d3.range(matrix.length-1))
.range(colors);
var svg = d3.select("body").append("svg")
.attr("id", "visual")
.attr("viewBox", viewBoxDimensions)
.attr("preserveAspectRatio", "xMinYMid") // add viewBox and preserveAspectRatio
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll("g.group")
.data(chord.groups)
.enter().append("svg:g")
.attr("class", "group");
g.append("svg:path")
.style("fill", function(d) { return fill(d.index); })
.style("stroke", function(d) { return fill(d.index); })
.attr("id", function(d, i) { return "group" + d.index; })
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius).startAngle(startAngle).endAngle(endAngle))
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
g.append("svg:text")
.each(function(d) {d.angle = ((d.startAngle + d.endAngle) / 2) + offset; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + (outerRadius + textgap) + ")"
+ (d.angle > Math.PI ? "rotate(180)" : "");
})
.text(function(d) { return gnames[d.index]; });
svg.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chord.chords)
.enter().append("path")
.attr("d", d3.svg.chord().radius(innerRadius).startAngle(startAngle).endAngle(endAngle))
.style("fill", function(d) { return fill(d.source.index); })
.style("opacity", 1)
.append("svg:title")
.text(function(d) {
return d.source.value + " people from " + gnames[d.source.index] + " commute to " + gnames[d.target.index];
});
// helper functions start here
function startAngle(d) {
return d.startAngle + offset;
}
function endAngle(d) {
return d.endAngle + offset;
}
function extend(a, b) {
for( var i in b ) {
a[ i ] = b[ i ];
}
}
// Returns an event handler for fading a given chord group.
function fade(opacity) {
return function(g, i) {
svg.selectAll(".chord path")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("opacity", opacity);
};
}
window.onresize = function() {
var targetWidth = (window.innerWidth < width)? window.innerWidth : width;
var svg = d3.select("#visual")
.attr("width", targetWidth)
.attr("height", targetWidth / aspect);
}
}
3. Чтобы этот модуль работал с ноутбуком, мы должны изменить несколько вещей, касающихся размера и стилей.
var config = {
width: 640,
height: 560,
};
//changes to
var config = {
width: nb.boxWidth,
height: nb.boxHeight,
};
Далее нам нужно добавить .attr(‘class’,’mainBox’) в список атрибутов svg.
var svg = d3.select("body").append("svg") //changes to var svg = d3.select("body").append("svg").attr(‘class’,’mainBox’)
Эти три изменения гарантируют, что визуализатор отображается в поле правильного размера с правильными стилями.
4. Теперь нам нужно подготовить модуль для пользовательского набора данных.
Удалите параметры из Chord() и добавьте «опции» и «матрицу».
Chord = function (options, matrix) {
- «options» — это массив настраиваемых параметров для таких вещей, как цвет
- «матрица» — это данные, которые мы хотим визуализировать
Если вы еще этого не сделали, объедините Chord() внутри функции nb.tutorial.chord(), которую мы создали ранее.
nb.tutorial.chord = function (data) { Chord(options, matrix) { ... ... } }
Наш набор данных настроен как файл CSV с рядом заголовков и множеством строк под заголовками. Вот пример набора данных, представляющий количество людей, перемещающихся между Марин, Сонома, Напа и Сан-Франциско.
[ "Marin", "Sonoma", "Napa", "San Francisco"], [ 198, 11, 3, 330 ], [ 11, 89, 8, 33 ], [ 0, 1, 51, 29 ], [ 0, 0, 0, 0 ], ]
Чтобы заставить этот набор данных работать, мы сначала должны вытащить заголовки, оставив только матрицу чисел. Мы сохраним названия в chord_options для последующего использования.
nb.tutorial.chord = function (data) { var chord_options = { "gnames": data[0], //captures titles "rotation": -0.7, "colors": ["#034e7b","#feb24c","#b10026","#238443"] }; data.splice( 0, 1 ); //removes titles from data Chord(options, matrix) { ...
'chord_options' в этом примере является статическим, но вы можете сделать его параметром самой функции модуля.
Наконец, мы должны вызвать функцию Chord() из nb.tutorial.chord().
... Chord(options, matrix) { ... } Chord(chord_options, data); }
Вот готовый код:
//nb.js Tutorial Module nb.tutorial = function () {}; nb.tutorial.test=function() {return "this module is working!";}; nb.tutorial.chord = function (data) { var chord_options = { "gnames": data[0], "rotation": -0.7, "colors": ["#034e7b","#feb24c","#b10026","#238443","#fdbb84","#ffffb2","#fed976"] }; data.splice( 0, 1 ); function Chord(options, matrix) { // initialize the chord configuration variables var config = { width: nb.boxWidth, height: nb.boxHeight, rotation: 0, textgap: 26, colors: ["#7fc97f", "#beaed4", "#fdc086", "#ffff99", "#386cb0", "#f0027f", "#bf5b17", "#666666"] }; // add options to the chord configuration object if (options) { extend(config, options); } // set chord visualization variables from the configuration object var offset = Math.PI * config.rotation, width = config.width, height = config.height, textgap = config.textgap, colors = config.colors; // set viewBox and aspect ratio to enable a resize of the visual dimensions var viewBoxDimensions = "0 0 " + width + " " + height, aspect = width / height; if (config.gnames) { gnames = config.gnames; } else { // make a list of names gnames = []; for (var i=97; i<matrix.length; i++) { gnames.push(String.fromCharCode(i)); } } // start the d3 magic var chord = d3.layout.chord() .padding(0.05) .sortSubgroups(d3.descending) .matrix(matrix); var innerRadius = Math.min(width, height) * 0.31, outerRadius = innerRadius * 1.1; var fill = d3.scale.ordinal() .domain(d3.range(matrix.length-1)) .range(colors); var svg = d3.select("body").append("svg") .attr("id", "visual") .attr("viewBox", viewBoxDimensions) .attr("preserveAspectRatio", "xMinYMid") // add viewBox and preserveAspectRatio .attr("width", width) .attr("height", height) .attr('class','mainBox') .append("g") .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); var g = svg.selectAll("g.group") .data(chord.groups) .enter().append("svg:g") .attr("class", "group"); g.append("svg:path") .style("fill", function(d) { return fill(d.index); }) .style("stroke", function(d) { return fill(d.index); }) .attr("id", function(d, i) { return "group" + d.index; }) .attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius).startAngle(startAngle).endAngle(endAngle)) .on("mouseover", fade(0.1)) .on("mouseout", fade(1)); g.append("svg:text") .each(function(d) {d.angle = ((d.startAngle + d.endAngle) / 2) + offset; }) .attr("dy", ".35em") .attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; }) .attr("transform", function(d) { return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" + "translate(" + (outerRadius + textgap) + ")" + (d.angle > Math.PI ? "rotate(180)" : ""); }) .text(function(d) { return gnames[d.index]; }) .on("mouseover", fade(0.1)) .on("mouseout", fade(1)); svg.append("g") .attr("class", "chord") .selectAll("path") .data(chord.chords) .enter().append("path") .attr("d", d3.svg.chord().radius(innerRadius).startAngle(startAngle).endAngle(endAngle)) .style("fill", function(d) { return fill(d.source.index); }) .style("opacity", 1) .append("svg:title") .text(function(d) { return d.source.value + " people from " + gnames[d.source.index] + " commute to " + gnames[d.target.index]; }); // helper functions start here function startAngle(d) { return d.startAngle + offset; } function endAngle(d) { return d.endAngle + offset; } function extend(a, b) { for( var i in b ) { a[ i ] = b[ i ]; } } // Returns an event handler for fading a given chord group. function fade(opacity) { return function(g, i) { svg.selectAll(".chord path") .filter(function(d) { return d.source.index != i && d.target.index != i; }) .transition() .style("opacity", opacity); }; } } Chord(chord_options, data); }; //Test chord with test data /* nb.tutorial.chord(matrix = [ [ "Marin", "Sonoma", "Napa", "San Francisco", "East Bay", "Silicon Valley", "Remote Locations" ], [ 198, 11, 3, 330, 32, 35, 59 ], [ 11, 89, 8, 33, 15, 0, 28 ], [ 0, 1, 51, 29, 17, 0, 26 ], [ 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0 ] ]); */
5. Теперь пришло время загрузить ваш модуль в Datazar.
Переименуйте файл .js в .nb.js. Этот шаг необязателен, но рекомендуется и может оказаться важным в будущих версиях Notebook.
Затем перейдите на https://www.datazar.com/notebook/modules/ и нажмите Создать модуль, чтобы начать процесс загрузки.
После заполнения основной информации во всплывающем окне вы должны быть перенаправлены на этот экран. В окне вы можете:
- Предоставить документацию (в Markdown)
- Сохраняйте исходный код
- Загрузите пользовательский значок (нажав на темно-серый квадрат)
- Редактируйте исходный код прямо в редакторе кода Datazar.
Вот моя страница модуля с пользовательским значком и документацией.
Последний шаг — загрузить фактический код, поэтому нажмите кнопку загрузки в правом верхнем углу и выберите файл nb.js. После загрузки модуль готов к включению в любой сеанс Notebook.
Ура! Ваш самый первый модуль! Поздравляем!
Чтобы использовать свой модуль в Notebook, просто нажмите «Добавить» и введите имя, которое вы дали своему модулю ранее. Я назвал свой модуль «tutorialModule», поэтому все, что мне нужно сделать, это ввести его и нажать «Включить модули».
Теперь просто вызовите функцию аккорда в редакторе слева и наблюдайте, как диаграмма аккорда волшебным образом появляется в окне справа.
Если вам понравился этот контент, сообщите нам об этом, нажав кнопку «Рекомендовать» и оставив ответ, сообщив нам, что вы думаете.
Удачи со всеми вашими будущими модулями ноутбука :)