Как создать диаграмму рассеяния на основе определенных критериев с помощью d3.js

#javascript #svg #d3.js

#javascript #svg #d3.js

Вопрос:

Для изучения того, как использовать d3.js , я пытался использовать набор данных titanic для обучения, доступный на kaggle.

Я пытаюсь достичь цели:

  1. Создайте диаграмму рассеяния возраста и тарифа с возрастом по оси x и тарифом по оси y

  2. Используйте столбец пола, чтобы на диаграмме рассеяния мужчина был квадратным, а женщина — кругами

  3. Имейте непрозрачность, чтобы указать условие — выжил или не выжил.

Я использовал следующий код:

 // set the dimensions and margins of the graph
var margin = {
    top: 10,
    right: 30,
    bottom: 30,
    left: 60
  },
  width = 460 - margin.left - margin.right,
  height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .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   ")");

//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(data) {

  // Add X axis
  var x = d3.scaleLinear()
    .domain([0, 80])
    .range([0, width]);
  svg.append("g")
    .attr("transform", "translate(0,"   height   ")")
    .call(d3.axisBottom(x));

  // Add Y axis
  var y = d3.scaleLinear()
    .domain([0, 600])
    .range([height, 0]);
  svg.append("g")
    .call(d3.axisLeft(y));

  // Add dots
  svg.append('g')
    .selectAll("dot")
    .data(data)
    .enter()
    .append("circle")
    .attr("cx", function(d) {
      return x(d.age);
    })
    .attr("cy", function(d) {
      return y(d.fare);
    })
    .attr("r", 1.5)
    .style("fill", "#69b3a2")

})  
 <script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>  

Условие, которое я хочу реализовать, это:

 if(d.sex == "female"){
    return d3.symbolCircle;
} else if (d.sex == "male"){
    return d3.symbolSquare;
}
  

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

Кто-нибудь может мне помочь, пожалуйста. Я действительно заранее благодарю вас.

В случае, если у вас нет набора данных, его также можно найти здесь.

Ответ №1:

Во-первых, вам нужно посмотреть на свои данные. Все ваши свойства указаны в верхнем регистре, а чтение CSV-файла всегда означает, что вам нужно проанализировать ваши строки: числа и даты по-прежнему являются строками, вам нужно использовать их как таковые:

 // set the dimensions and margins of the graph
var margin = {
    top: 10,
    right: 30,
    bottom: 30,
    left: 60
  },
  width = 460 - margin.left - margin.right,
  height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .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   ")");

//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
  // All values are strings here, so we need to parse some of them.
  // You can do that using ` x` or `Number(x)`, where `x = "123"`
  const data = rawData.map(function(d) {
    return {
      age: Number(d.Age),
      // cabin: d.Cabin,
      // embarked: e.Embarked,
      fare: Number(d.Fare),
      // name: d.Name,
      // parch: Number(d.Parch),
      // passengerId: Number(d.PassengerId)
      // pclass: Number(Pclass),
      sex: d.Sex,
      // sibSp: Number(d.SibSp),
      survived: d.Survived === "1"
      // ticket: d.Ticket,
    };
  });

  // Add X axis
  var x = d3.scaleLinear()
    .domain([0, 80])
    .range([0, width]);
  svg.append("g")
    .attr("transform", "translate(0,"   height   ")")
    .call(d3.axisBottom(x));

  // Add Y axis
  var y = d3.scaleLinear()
    .domain([0, 600])
    .range([height, 0]);
  svg.append("g")
    .call(d3.axisLeft(y));

  // Add dots
  svg.append('g')
    .selectAll("dot")
    .data(data)
    .enter()
    .append("circle")
    .attr("cx", function(d) {
      return x(d.age);
    })
    .attr("cy", function(d) {
      return y(d.fare);
    })
    .attr("r", 1.5)
    .style("fill", "#69b3a2")

})  
 <script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>  

Во-вторых, круги могут быть только кругами. Чтобы рисовать как квадраты, так и круги, вам нужно использовать <path> . Вы d3.symbol* были правы, но вам нужно получить доступ к их .draw() функции. d3.path это генератор, позволяющий легко нарисовать d атрибут пути:

 // set the dimensions and margins of the graph
var margin = {
    top: 10,
    right: 30,
    bottom: 30,
    left: 60
  },
  width = 460 - margin.left - margin.right,
  height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .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   ")");

//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
  // All values are strings here, so we need to parse some of them.
  // You can do that using ` x` or `Number(x)`, where `x = "123"`
  const data = rawData.map(function(d) {
    return {
      age: Number(d.Age),
      // cabin: d.Cabin,
      // embarked: e.Embarked,
      fare: Number(d.Fare),
      // name: d.Name,
      // parch: Number(d.Parch),
      // passengerId: Number(d.PassengerId)
      // pclass: Number(Pclass),
      sex: d.Sex,
      // sibSp: Number(d.SibSp),
      survived: d.Survived === "1"
      // ticket: d.Ticket,
    };
  });

  // Add X axis
  var x = d3.scaleLinear()
    .domain([0, 80])
    .range([0, width]);
  svg.append("g")
    .attr("transform", "translate(0,"   height   ")")
    .call(d3.axisBottom(x));

  // Add Y axis
  var y = d3.scaleLinear()
    .domain([0, 600])
    .range([height, 0]);
  svg.append("g")
    .call(d3.axisLeft(y));

  // Add dots
  svg.append('g')
    .selectAll("path")
    .data(data)
    .enter()
    .append("path")
    .attr("transform", function(d) {
      return "translate("   [x(d.age), y(d.fare)]   ")";
    })
    .attr("d", function(d) {
      const path = d3.path();
      const shape = d.sex == "female" ? d3.symbolCircle : d3.symbolSquare;
      shape.draw(path, 8);
      return path.toString();
    })
    .style("fill", "#69b3a2")

})  
 <script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>  

(Кроме того, вы также могли бы нарисовать rect s для обоих, но учитывая, что женские закругленные углы с rx атрибутом).

Наконец, вы можете использовать цветовую шкалу для заливки с d3.scaleOrdinal помощью , но если у вас есть только два цвета и вы не используете ее для окрашивания нескольких объектов (например, линейной диаграммы и легенды), просто используйте оператор if:

 // set the dimensions and margins of the graph
var margin = {
    top: 10,
    right: 30,
    bottom: 30,
    left: 60
  },
  width = 460 - margin.left - margin.right,
  height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .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   ")");

//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
  // All values are strings here, so we need to parse some of them.
  // You can do that using ` x` or `Number(x)`, where `x = "123"`
  const data = rawData.map(function(d) {
    return {
      age: Number(d.Age),
      // cabin: d.Cabin,
      // embarked: e.Embarked,
      fare: Number(d.Fare),
      // name: d.Name,
      // parch: Number(d.Parch),
      // passengerId: Number(d.PassengerId)
      // pclass: Number(Pclass),
      sex: d.Sex,
      // sibSp: Number(d.SibSp),
      survived: d.Survived === "1"
      // ticket: d.Ticket,
    };
  });

  // Add X axis
  var x = d3.scaleLinear()
    .domain([0, 80])
    .range([0, width]);
  svg.append("g")
    .attr("transform", "translate(0,"   height   ")")
    .call(d3.axisBottom(x));

  // Add Y axis
  var y = d3.scaleLinear()
    .domain([0, 600])
    .range([height, 0]);
  svg.append("g")
    .call(d3.axisLeft(y));

  // Add dots
  svg.append('g')
    .selectAll("path")
    .data(data)
    .enter()
    .append("path")
    .attr("transform", function(d) {
      return "translate("   [x(d.age), y(d.fare)]   ")";
    })
    .attr("d", function(d) {
      const path = d3.path();
      const shape = d.sex == "female" ? d3.symbolCircle : d3.symbolSquare;
      shape.draw(path, 8);
      return path.toString();
    })
    .style("fill", function(d) {
      return d.survived ? "#69b3a2" : "#ddd";
    })

})  
 <script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>  

Комментарии:

1. Спасибо за вашу помощь. Я хотел спросить еще одну вещь: в style мы устанавливаем «заливку» для цвета, но я прочитал и обнаружил, что непрозрачность отличается от цвета. Если мы хотим заменить заливку непрозрачностью, можем ли мы продолжить с той же логикой?

2. Да, вы можете использовать любой атрибут. Для непрозрачности вам нужно вернуть число от 0 до 1. В SVG у вас есть opacity , но также более конкретные fill-opacity и stroke-opacity . Посмотрите, что вам нужно

3. Большое спасибо. Итак, концепция непрозрачности подобна вероятности, имеющей значение от 0 до 1.

4. Да, 0 — это кусок стекла, 1 — кирпичная стена

Ответ №2:

Одна интересная вещь в SVG заключается в том, что a <rect> с атрибутами rx and ry , равными половине его ширины или высоты (они, конечно, одинаковы), фактически становится кругом.

Итак, предположим, у вас есть

 var diameter = 3;
  

Все, что вам нужно, это:

 .attr("rx", function(d) {
  return d.Sex === "male" ? 0 : diameter / 2
})
.attr("ry", function(d) {
  return d.Sex === "male" ? 0 : diameter / 2
})
  

И, конечно, вычтите позиции x и y на половину диаметра (т. Е. Радиуса).

Это похоже на взлом, но преимущество такого подхода заключается в том, что довольно легко перейти от квадрата к «кругу», просто изменив значения rx / ry (можно переходить по путям, но это немного сложнее). Взгляните на этот переход с преувеличенным радиусом и ограниченным доменом:

 // set the dimensions and margins of the graph
var margin = {
    top: 10,
    right: 30,
    bottom: 10,
    left: 60
  },
  width = 660 - margin.left - margin.right,
  height = 500 - margin.top - margin.bottom;

var diameter = 12;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .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   ")");

//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", row, function(data) {

  // Add X axis
  var x = d3.scaleLinear()
    .domain([0, 80])
    .range([0, width]);
  svg.append("g")
    .attr("transform", "translate(0,"   height   ")")
    .call(d3.axisBottom(x));

  // Add Y axis
  var y = d3.scaleLinear()
    .domain([0, 300])
    .range([height, 0]);
  svg.append("g")
    .call(d3.axisLeft(y));

  // Add dots
  svg.append('g')
    .selectAll("dot")
    .data(data)
    .enter()
    .append("rect")
    .attr("x", function(d) {
      return x(d.Age) - diameter / 2;
    })
    .attr("y", function(d) {
      return y(d.Fare) - diameter / 2;
    })
    .attr("width", diameter)
    .attr("height", diameter)
    .style("fill", "#69b3a2")
    .transition()
    .duration(2000)
    .attr("rx", function(d) {
      return d.Sex === "male" ? 0 : diameter / 2
    })
    .attr("ry", function(d) {
      return d.Sex === "male" ? 0 : diameter / 2
    })
    .style("fill", function(d) {
      return  d.Survived ? "#69b3a2" : "tan"
    });

})

function row(d) {
  d.Age =  d.Age;
  d.Fare =  d.Fare;
  return d;
}  
 <script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>  

Комментарии:

1. Спасибо за вашу помощь. Я хотел спросить еще одну вещь: в style мы устанавливаем «заливку» для цвета, но я прочитал и обнаружил, что непрозрачность отличается от цвета. Если мы хотим заменить заливку непрозрачностью, можем ли мы продолжить с той же логикой, если мы используем то же условие изменения непрозрачности в соответствии с выжившим или нет?

2. @KC да, вы можете.