Как интерпретировать простые алгоритмы ObservableHq как многократно используемые фрагменты кода?

Основным источником решений D3js является observableHq.com, но кажется невозможным (?) повторно использовать алгоритмы путем копирования/вставки... Так ли это? Даже при проверке руководств подобных этому не существует простых способ (с меньшим количеством подключаемых модулей или затрат времени программиста!) для проверки и повторного использования.

Пример: мне нужен свежий алгоритм D3js v5 2020 года для визуализации дерева с отступами, и есть хорошее решение: observableHq.com/@d3/indented-tree.
Загрузка бесполезна, поскольку основана на сложном классе времени выполнения...

Но, кажется, простой алгоритм построения диаграмм,

chart = {  // the indented-tree algorithm
  const nodes = root.descendants();
  const svg = d3.create("svg")// ...
  // ...
  return svg.node();
}

Могу ли я простым человеческим шагом преобразовать его в простой HTML без сложных адаптаций, который начинается с <script src="https://d3js.org/d3.v5.min.js"></script> и заканчивается без использования класса среды выполнения?


Подробнее как пример

Представляя пошаговые инструкции для цитируемого алгоритма дерева с отступами, я не могу Finesh и нужна ваша помощь:

Предположим, вы начинаете с чистого шаблона HTML5. Например:

<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <title>Indented Tree</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script>
    function onLOAD() {
        console.log("Hello onLoad!")
        // all copy-paste and adaptations HERE.
        console.log("!Bye!")
    } // \onLOAD
    </script>
</head>
<body onload="onLOAD()">
  <script>
    console.log("Hello!")
    // global INITIALIZATIONS HERE.
  </script>
</body>
</html>
  1. Подготовьте глобальные переменные, кажется root, nodeSize=17 и width

  2. Подготовьте данные... Данные JSON находятся в уродливом ./files/e6537420..., я переместил в корень проекта с его настоящим именем, flare-2.json.

  3. Простой и классический способ D3js для чтения данных JSON: d3.json("./flare-2.json").then( data=> console.log(data) );
    Необходимо протестировать и проверить отсутствие ошибок CORS и т. д.

  4. Подготовьте данные как переменную root. Все в блок data => {}, чтобы избежать проблем с синхронизацией...
    Похоже, что root основан на function(d3,data) { let i = 0; return d3.hierarchy(data).eachBefore(d => d.index = i++); }.

  5. Скопируйте-вставьте chart = указанное выше, после root инициализации с данными.

  6. ...


Часто задаваемые вопросы

Вопросы и ответы в комментариях:

@Mehdi - Не могли бы вы объяснить, в чем проблема с включением тега скрипта D3 и использованием библиотеки времени выполнения в коде?

Когда исходный алгоритм ObservableHq прост, мне нужен другой способ, простой способ его повторного использования путем копирования/вставки и минимальной адаптации.

@Mehdi - Прочитали ли вы руководство по загрузке и внедрению блокнотов?

Да, никаких новостей: никаких «человеческих инструкций» о том, как повторно использовать код... Только «установить это» и «установить это». Никаких инструкций про "копировать/вставить и минимальные адаптации", которые я объяснил выше.

(@nobody) - Что вам нужно в качестве ответа?

Как я показал выше, простая удобочитаемая пошаговая процедура преобразования... В идеале конечный результат может быть протестирован, доказательство того, что он работает, например, на JSFiddle, с кодом копирования/вставки и некоторыми другими адаптационные линии, чтобы показать свою точку зрения.


person Peter Krauss    schedule 20.03.2020    source источник
comment
Вы читали Скачивание и встраивание блокнотов руководство?   -  person Mehdi    schedule 20.03.2020
comment
Не могли бы вы объяснить, в чем проблема с включением тега сценария d3 и использованием библиотеки времени выполнения в коде?   -  person Mehdi    schedule 20.03.2020
comment
Привет @Mehdi, спасибо. Я отредактировал заголовок и добавил часто задаваемые вопросы... Пожалуйста, объясните или измените свой закрытый голос.   -  person Peter Krauss    schedule 20.03.2020
comment
Спасибо за разъяснения. Я все еще не понимаю. Почему бы тогда не скопировать и вставить каждую ячейку отдельно?   -  person Mehdi    schedule 22.03.2020
comment
Привет @Mehdi, я снова отредактировал, см. Раздел «Подробнее» в качестве примера.   -  person Peter Krauss    schedule 22.03.2020


Ответы (2)


Ноябрь 2020 изменить

Теперь у Observable есть функция embed, подробности на этой странице .

Исходный пост

Вот пошаговый процесс переноса связанной наблюдаемой диаграммы на собственную веб-страницу путем копирования и вставки кода без использования наблюдаемая runtime библиотека.

Начиная с HTML-страницы и файла JavaScript, на который ссылается HTML-страница. Предположим, что веб-сервер работает и настроен соответствующим образом.

  1. Получите данные.
  • Если вы хотите использовать свои собственные данные вместо тех, которые используются в записной книжке, сделайте файлы данных доступными в каталоге на вашем веб-сервере.
  • в противном случае загрузите каждый входной набор данных, прикрепленный к блокноту, используя ссылку Download JSON из меню каждой data ячейки.

скриншот видимого меню ячейки записной книжки

  1. Загрузите каждый набор данных на страницу, используя d3-fetch.
d3.json("/path/to/data.json").then(function(data) {
  console.log(data); // [{"Hello": "world"}, …]
});
  1. Получите содержимое каждой ячейки, содержащей переменную или функцию в записной книжке, и поместите ее внутрь функции.then из предыдущего шага. Этот визуализатор блокнотов можно помогает идентифицировать соответствующие ячейки.

  2. Адаптируйте синтаксис только что скопированных функций как подходящий. Например, следующая ячейка записной книжки:

root = { let i = 0; return d3.hierarchy(data).eachBefore(d => d.index = i++); }

может быть преобразован в:

function getRoot(){
   let i = 0;
    return d3.hierarchy(data).eachBefore(d => d.index = i++);
}

root = getRoot()
  1. Если это необходимо какой-либо функции из блокнота, определите переменную width и инициализируйте ее желаемым значением.

  2. адаптируйте код манипулирования DOM, чтобы добавлять элементы в DOM, а не полагаться на неявное выполнение наблюдаемой средой выполнения.

Демо в обрезанном ниже:

d3.json("https://rawcdn.githack.com/d3/d3-hierarchy/46f9e8bf1a5a55e94c40158c23025f405adf0be5/test/data/flare.json").then(function(data) {

  const width = 800
    , nodeSize = 17
    , format = d3.format(",")
    , getRoot = function(){
       let i = 0;
        return d3.hierarchy(data).eachBefore(d => d.index = i++);
    }
    , columns = [
      {
        label: "Size", 
        value: d => d.value, 
        format, 
        x: 280
      },
      {
        label: "Count", 
        value: d => d.children ? 0 : 1, 
        format: (value, d) => d.children ? format(value) : "-", 
        x: 340
      }
    ]
    , root = getRoot()
    , chart = function() {
      const nodes = root.descendants();

      const svg = d3.select('#chart')
          .attr("viewBox", [-nodeSize / 2, -nodeSize * 3 / 2, width, (nodes.length + 1) * nodeSize])
          .attr("font-family", "sans-serif")
          .attr("font-size", 10)
          .style("overflow", "visible");


  const link = svg.append("g")
      .attr("fill", "none")
      .attr("stroke", "#999")
    .selectAll("path")
    .data(root.links())
    .join("path")
      .attr("d", d => `
        M${d.source.depth * nodeSize},${d.source.index * nodeSize}
        V${d.target.index * nodeSize}
        h${nodeSize}
      `);

      const node = svg.append("g")
        .selectAll("g")
        .data(nodes)
        .join("g")
          .attr("transform", d => `translate(0,${d.index * nodeSize})`);

      node.append("circle")
          .attr("cx", d => d.depth * nodeSize)
          .attr("r", 2.5)
          .attr("fill", d => d.children ? null : "#999");

      node.append("text")
          .attr("dy", "0.32em")
          .attr("x", d => d.depth * nodeSize + 6)
          .text(d => d.data.name);

      node.append("title")
          .text(d => d.ancestors().reverse().map(d => d.data.name).join("/"));

      for (const {label, value, format, x} of columns) {
        svg.append("text")
            .attr("dy", "0.32em")
            .attr("y", -nodeSize)
            .attr("x", x)
            .attr("text-anchor", "end")
            .attr("font-weight", "bold")
            .text(label);

        node.append("text")
            .attr("dy", "0.32em")
            .attr("x", x)
            .attr("text-anchor", "end")
            .attr("fill", d => d.children ? null : "#555")
          .data(root.copy().sum(value).descendants())
            .text(d => format(d.value, d));
      }

  }

  chart()
    
}).catch(function(err) {
  console.log('error processing data', err)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.min.js"></script>
<svg id = 'chart'></svg>

person Mehdi    schedule 22.03.2020
comment
Привет Менди, спасибо, рецепт для людей! Чтобы быть идеальным, возможно, вы можете включить помощник ObservableHq, notebook-visualizer: в этом случае ноутбук-визуализатор-с отступом-деревом... Цитата на ваших итенах 1 и/или 4. PS: интересно также приведите в постскриптуме название этой методологии (теперь я вижу после того, как @cal_br_mar процитировал переписать методологию с нуля). - person Peter Krauss; 27.03.2020
comment
хорошо, я просто добавил ссылку на визуализатор ноутбука в ответ. - person Mehdi; 27.03.2020

Самый простой способ — использовать их встроенную версию во время выполнения. Вот очень похожий способ повторного использования блокнота в шаблоне HTML5.

Вы также можете загрузить среду выполнения и блокнот js для размещения на своем сервере.

Хитрость здесь заключается в том, чтобы использовать среду выполнения для общения с наблюдаемыми реактивными ячейками.

В этом примере я использую d3.json для получения новых данных json и переопределения ячейки data из исходного ноутбук.

<div id="observablehq-e970adfb"></div>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script type="module">

//Import Observable Runtime

import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";
import define from "https://api.observablehq.com/@d3/indented-tree.js?v=3";
const inspect = Inspector.into("#observablehq-e970adfb");

// Notebook instance
const notebook =(new Runtime).module(define, name => (name === "chart") && inspect());


// Using D3.json to load new Json Data

d3.json("https://gist.githubusercontent.com/radames/9018398d6e63bcaae86a0bf125dc6973/raw/33f19a49e1123a36e172cfc7483f0a444caf6ae3/newdata.json").then((newdata) =>{
  
  // When data is loaded you can use notebook to redefine a cell
  // In this case the data cell, where in the notebook it's using a FileAtachent
  // Here you can redefine with any structure hierarchy structure like
  
  notebook.redefine("data", newdata);
})


</script>

Редактирование для добавления шагов с использованием проекта Северо

Используя визуализатор Severo Notebook, вы можете понять поток данных вашего ноутбука и переписать свой автономный код. Имейте в виду, что переписывание с нуля может стать очень сложным, поскольку ваш код использует функции Observable, такие как реактивность и управление состоянием. В этом случае я рекомендую вам использовать среду выполнения Observable, следуя моему ответу выше.

Имея это в виду, давайте посмотрим на визуализатор и следуем инструкциям Северо< /а>

введите здесь описание изображения

  • Зеленые ячейки соответствуют внешнему коду, импортированному в записную книжку: библиотека, импортированная с помощью require (например, d3 = require("d3@5")): обычно вы устанавливаете ее в свой проект с помощью npm install и< br> затем импортируйте его как блокнот, импортированный модулем ES (например, импортируйте {радио} из "@jashkenas/inputs"): вам придется повторить тот же процесс
    в этом блокноте, изучив его собственный граф зависимостей.
  • Серые ячейки — это анонимные (безымянные) ячейки, которые, как правило, не переносятся. Они часто содержат поясняющие тексты, и от них не может зависеть никакая другая ячейка, поэтому их удаление не должно нарушать код
    . Но будьте осторожны: если ваша основная ячейка диаграммы не имеет имени, вам
    все равно понадобится скопировать ее код.
  • Черные ячейки — это фактический код записной книжки, написанный пользователем, и вы захотите скопировать его в свой проект.
  • Фиолетовые клетки — самые прочные. Они соответствуют функциям Observable, которые обычно часто используются авторами записных книжек (см. Стандартную библиотеку), и их миграция в отдельное приложение может быть самой сложной частью переписывания с нуля, особенно изменяемых ячеек и представлений ячеек, поскольку они управлять внутренним состоянием.

Вот код, преобразованный в соответствии с этими инструкциями

<!--- Green Cells / Imports --->
<script src="https://d3js.org/d3.v5.min.js"></script>

<!--- Char Container --->

<div class="chart"></div>
<script>
  // Run main function
  main();

  // async main so we can run our code like Observable cell by cell
  async function main() {
    // as in Observable each cell runs as an async function
    // so here you can await the output to continue
    const data = await d3.json("https://gist.githubusercontent.com/radames/9018398d6e63bcaae86a0bf125dc6973/raw/33f19a49e1123a36e172cfc7483f0a444caf6ae3/newdata.json");

    // run complex code as inline await / async
    const root = await (async() => {
      let i = 0;
      return d3.hierarchy(data).eachBefore(d => d.index = i++);
    })()

    // easy constant
    const nodeSize = 17;

    // easy constant function
    const format = d3.format(",");

    // easy constant
    const columns = [{
        label: "Size",
        value: d => d.value,
        format,
        x: 280
      },
      {
        label: "Count",
        value: d => d.children ? 0 : 1,
        format: (value, d) => d.children ? format(value) : "-",
        x: 340
      }
    ];
    // on Observable width is reactive, here we have to do it manually
    const width = window.innerHTML;

    window.addEventListener('resize', updateWidth);

    function updateWidth() {
      // update your chart on resize event
    }
    // inline function gets chart svg node
    const chart = (() => {
      const nodes = root.descendants();

      const svg = d3.create("svg")
        .attr("viewBox", [-nodeSize / 2, -nodeSize * 3 / 2, width, (nodes.length + 1) * nodeSize])
        .attr("font-family", "sans-serif")
        .attr("font-size", 10)
        .style("overflow", "visible");

      const link = svg.append("g")
        .attr("fill", "none")
        .attr("stroke", "#999")
        .selectAll("path")
        .data(root.links())
        .join("path")
        .attr("d", d => `
          M${d.source.depth * nodeSize},${d.source.index * nodeSize}
          V${d.target.index * nodeSize}
          h${nodeSize}
        `);

      const node = svg.append("g")
        .selectAll("g")
        .data(nodes)
        .join("g")
        .attr("transform", d => `translate(0,${d.index * nodeSize})`);

      node.append("circle")
        .attr("cx", d => d.depth * nodeSize)
        .attr("r", 2.5)
        .attr("fill", d => d.children ? null : "#999");

      node.append("text")
        .attr("dy", "0.32em")
        .attr("x", d => d.depth * nodeSize + 6)
        .text(d => d.data.name);

      node.append("title")
        .text(d => d.ancestors().reverse().map(d => d.data.name).join("/"));

      for (const {
          label,
          value,
          format,
          x
        } of columns) {
        svg.append("text")
          .attr("dy", "0.32em")
          .attr("y", -nodeSize)
          .attr("x", x)
          .attr("text-anchor", "end")
          .attr("font-weight", "bold")
          .text(label);

        node.append("text")
          .attr("dy", "0.32em")
          .attr("x", x)
          .attr("text-anchor", "end")
          .attr("fill", d => d.children ? null : "#555")
          .data(root.copy().sum(value).descendants())
          .text(d => format(d.value, d));
      }

      return svg.node();
    })()

    // select element container append chart
    const container = document.querySelector(".chart")
    container.appendChild(chart);

  }
</script>

person calmar    schedule 23.03.2020
comment
Привет, Кэл, это почти то же самое, что и щелкнуть Загрузить код на сайте блокнота... Как я уже отмечал, использование runtime.js создает черный ящик, которого мне нужно избегать... И по сравнению с решением @Mehdi, с временем выполнения производительность хуже... Можете ли вы объяснить преимущество вашего подхода? - person Peter Krauss; 24.03.2020
comment
Привет, среда выполнения — это не черный ящик, исходный код, github.com/observablehq/runtime легкий синтаксический анализатор javascript с преимуществами реактивности. Я не уверен в производительности. Другие полезные ресурсы: github.com/asg017/unofficial-observablehq-compiler и github.com/severo/observable-to-standalone - person calmar; 24.03.2020
comment
Привет @car_br_mar, спасибо за вашу последнюю ссылку, ответ там (!)... Можете ли вы отредактировать свой ответ, добавив новый раздел с краткой информацией о переписать метод с нуля и проиллюстрировать его использование на примере дерева с отступом? - person Peter Krauss; 24.03.2020
comment
Вы можете использовать визуализатор блокнота. на иллюстрации (!). - person Peter Krauss; 24.03.2020
comment
Спасибо, cal_br_mar (!). PS: с изданием ваш ответ может получить половину вознаграждения... Но извините, я выбрал другое несколько часов назад. - person Peter Krauss; 28.03.2020
comment
Я обновил это, имейте в виду, что этот код было относительно легко переписать, однако, если вы используете самые интересные функции из среды выполнения Observable, этот перевод больше не будет проще. Я по-прежнему рекомендую использовать среду выполнения Observable, она очень легкая и работает в большинстве современных браузеров. - person calmar; 30.03.2020
comment
Отлично, спасибо! Что касается использования среды выполнения Observable в сложных интерфейсах, кажется, что лучше (более просто) использовать react-d3- библиотека.github.io - person Peter Krauss; 30.03.2020