Отсутствующие объекты Rect в гистограмме D3 - PullRequest
1 голос
/ 10 марта 2020

Я работал над созданием простой визуализации гистограммы D3, но по какой-то причине бары (rects) не появляются в моем виде. Визуализация использует .csv в качестве необработанных данных, используемых для визуализации. Единственная часть диаграммы, которую я могу получить, это оси, но даже когда я изменяю порядок вызовов функций при рисовании осей, они также исчезают.

Я пытался использовать вызов функции .selectAll("rect"), а также вызовы функций .join("rect") и .append("rect"), но, похоже, ничего не работает. Код, который я написал ниже. Я перепробовал множество онлайн-ресурсов, чтобы выяснить причину, по которой прямоугольные объекты не будут отображаться безрезультатно.

Я использую d3. js API версии 5.

<script>
    var margin = { top: 30, right: 30, bottom: 30, left: 30 },
        width = 1000 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;

    var svg = d3.select('body').append('svg')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
      .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    //defines the x-axis which is based on a numerical format
    var xScale = d3.scaleLinear()
        .rangeRound([0, width]);

    //defines the y-axis which is based on a numerical format    
    var yScale = d3.scaleLinear()
        .rangeRound([height, 0]);


    d3.csv("http://raw.githubusercontent.com/naija-queen/Raw-Data/master/crashing.csv").then(function (data) {
        //sets the domain of the x-axis
        xScale.domain([0, d3.max(data, function(d){
            return (Number(d.episode));
        })])

        //sets the domain of the y-axis
        yScale.domain([0, d3.max(data, function(d){
            return (Number(d.percent));
        })])

        //draws the x-axis
        svg.append('g')
        .attr("transform", "translate(0," + height + ")")
        //.ticks() command allows you to define how many ticks you want to see in the visualization
        .call(d3.axisBottom(xScale).ticks(16))

        //draws the y-axis
        svg.append("g")
        //for some reason moving arround the function call below makes the y-axis disappear
        .call(d3.axisLeft(yScale))
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("text-anchor", "end")
        .text("Percent Watched");

        //parts of a bar chart
        svg.append('bar')
        .attr("fill", "lightskyblue")
        .selectAll("rect")
        .data(data)
        .join("rect")
        // d => is basically function(d){}
        .attr("x", d => xScale(Number(d.episode)))
        .attr("y", d => yScale(Number(d.percent)))
        .attr("width", 20)
        .attr("height", d => yScale(0) - yScale(Number(d.percent)));          
    });

</script>

Я полный новичок в D3, поэтому, пожалуйста, объясните свой ответ, если можете.

1 Ответ

1 голос
/ 10 марта 2020

Ваши проблемы l ie здесь:

svg.append('bar')
    .attr("fill", "lightskyblue")
    .selectAll("rect")
    .data(data)
    .join("rect")

Вы добавляете элемент bar в SVG - это не допустимый элемент SVG, и он не будет отображать и будут его дети (прямоугольники, которые вы хотите нарисовать).

Я просто собираюсь использовать выбор enter (объединение сочетает в себе функциональность ввода / выхода / обновления, но мы должны сначала использовать функцию ввода):

   svg
    .selectAll(null) 
    .data(data)
    .enter()
    .append("rect")    
    .attr("...")

Выбор ввода создает элемент в DOM для каждого элемента в массиве данных, который еще не имеет соответствующего элемента в DOM. Я могу выбрать все элементы rect с помощью selectAll("rect"), но для каждого выбранного мной элемента, который уже существует, один элемент в массиве данных не будет введен. Поэтому я ничего не выбираю: .selectAll(null), который создает пустой выбор, не содержащий элементов DOM. Таким образом, вводится каждый элемент массива данных независимо от того, что уже находится в DOM.

В вашем случае использование selectAll("rect") нормально, так как rect

не будет выбрано * Теперь я связываю массив данных: .data(data)

И тогда я получаю доступ к вводу выбора: .enter(). Это возвращает выбор заполнителей. Поскольку мы ничего не выбрали, для каждого элемента массива данных создается один заполнитель. Теперь я могу добавить элементы SVG с .append("rect"). С этим у меня есть выбор вновь созданных прямоугольников. Теперь я могу изменить их по своему усмотрению, например, используя привязанные данные, чтобы установить их ширину / высоту, x / y и т. Д. c.

. Для простоты я представил только использование введите выбор здесь. Если я использую selectAll("rect") и ректы уже существуют, теперь у меня есть потенциальные возможности обновления и / или выхода - что позволило бы обновить существующие прямоугольники, для которых есть соответствующие элементы в массиве данных, или выйти из элементов, для которых больше нет соответствующих элементы в массиве данных.

var margin = { top: 30, right: 30, bottom: 30, left: 30 },
        width = 600 - margin.left - margin.right,
        height = 300 - margin.top - margin.bottom;

    var svg = d3.select('body').append('svg')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
      .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    //defines the x-axis which is based on a numerical format
    var xScale = d3.scaleLinear()
        .rangeRound([0, width]);

    //defines the y-axis which is based on a numerical format    
    var yScale = d3.scaleLinear()
        .rangeRound([height, 0]);


    d3.csv("http://raw.githubusercontent.com/naija-queen/Raw-Data/master/crashing.csv").then(function (data) {
        //sets the domain of the x-axis
        xScale.domain([0, d3.max(data, function(d){
            return (Number(d.episode));
        })])

        //sets the domain of the y-axis
        yScale.domain([0, d3.max(data, function(d){
            return (Number(d.percent));
        })])

        //draws the x-axis
        svg.append('g')
        .attr("transform", "translate(0," + height + ")")
        //.ticks() command allows you to define how many ticks you want to see in the visualization
        .call(d3.axisBottom(xScale).ticks(16))

        //draws the y-axis
        svg.append("g")
        //for some reason moving arround the function call below makes the y-axis disappear
        .call(d3.axisLeft(yScale))
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("text-anchor", "end")
        .text("Percent Watched");

        //parts of a bar chart
        svg
        .selectAll(null)
        .data(data)
        .enter()
        .append("rect")
        .attr("fill", "lightskyblue")
        // d => is basically function(d){}
        .attr("x", d => xScale(Number(d.episode)))
        .attr("y", d => yScale(Number(d.percent)))
        .attr("width", 20)
        .attr("height", d => yScale(0) - yScale(Number(d.percent)));          
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Кроме того, если вы хотите, чтобы введенные вами элементы содержались в родительском g, вы можете использовать selectAll вложенный g, такой как с:

svg
 .append("g")
 .selectAll(null) 
 .data(data)
 .enter()
 .append("rect")    
 .attr("...")
...