2014-10-19 3 views
0

Я работаю с воссозданием this population pyramid by Mike Bostock. У меня есть основной вопрос о том, как работает .data().d3js: Сопряжение CSV строк в элементах «g»

У меня есть данные в CSV файл следующим образом:

enter image description here

Итак, у меня есть количество мужчин и женщин в каждой region (Dodoma, Аруша и т.д.). Следуя примеру Майка, я хочу создать гистограмму, где мужчины и женщины занимают разные rects, которые перекрываются с 80% opacity. Я хочу, чтобы region была моей осью x, а число людей - моей осью y. Вот мои x- и у-оси шкалы:

 var x = d3.scale.ordinal() 
       .rangeRoundBands([0, w], 0.05); 

     var y = d3.scale.linear() 
       .range([0, h-padding]); 

И вот моя попытка перегруппировать мужчины + женщины по каждому региону:

 var regions = svg.append("g") 
      .classed("regions", true); 

      var region = regions.selectAll(".region") 
       .data(dataset) 
       .enter() 
       .append("g") 
       .attr("class", "region") 
       .attr("transform", function(region) { return "translate(" + x(region) + ",0)"; }); 

       region.selectAll("rect") 
       .data(dataset) 
       .enter() 
       .append("rect") 
       .attr("x", function(d, i) { 
        return x(i); 
       }) 
       .attr("y", function(d) { return h - y(d.people_0); }) 
       .attr("width", barWidth) 
       .attr("height", function(d) { return y(d.people_0); }); 

Я в конечном итоге с этим:

enter image description here

То есть, у меня есть 50 g элементов, которые имеют все данных, и просто создают полную гистограмму fo г каждой пары гендерных регионов (мужчины из Додомы, женщины из Додомы; мужчины из Танги, женщины из Танги и т. д.). Таким образом, полная диаграмма перерисовывается 50 раз. У меня 25 регионов, поэтому я хочу 25 g элементов с только номера мужчин и женщин для этого региона.

Я думаю, что все это можно было бы связать в .data(). Но я не знаю, как сообщить d3, что я хочу группировать свои наблюдения по регионам, и в итоге получается 2 rects за g.

Here's my JSFiddle (хотя я не знаю, как загрузить мои внешние данные, поэтому ничего не появляется, любые намеки на это будут оценены).

Edited добавить:

Я попытался с помощью .nest, дал комментарий Беньямина. Так что у меня есть:

 dataset = d3.nest() 
      .key(function(d) { return d.round; }) 
      .key(function(d) { return d.region; }) 
      .rollup(function(v) { return v.map(function(d) { return d.people_0; }); }) 
      .map(dataset); 

И я теперь зовет меня rects вот так:

   region.selectAll("rect") 
       .data(function(region) { return data[round][region]; }) 
       .enter() 
       .append("rect") 
       .attr("x", function(d, i) { 
        return x(i); 
       }) 
       .attr("y", function(d) { return h - y(d.people_0); }) 
       .attr("width", barWidth) 
       .attr("height", function(d) { return y(d.people_0); }); 

Нет удачи. FWIW, console.log(dataset) показывает мне, что данные действительно были свернуты. Но мне все еще сложно позвонить и связать его с моими g.region элементами.

Может кто-нибудь объяснить, что делает data[round][region]? Я не понимаю, как d3 интерпретирует это.

enter image description here

+2

вы делали в «гнездо()', как в примере ? Это группирует данные в «год» и «возраст» в примере, который вы связали. вам придется сделать то же самое для региона. –

+0

@BenjaminUdinktenCate - Спасибо. Пробовал '.nest', но он все еще не работает. Теперь ни один из прямых не появляется, и элементы 'g.region' теряются. Я обновил свой вопрос с дополнительной информацией, следуя этому предложению. Я думаю, что моя основная путаница заключается в том, как следует называть '.data()' по желанию. –

+1

'data [round] [region]' обращается к 2D-массиву. Вы связываете свои данные дважды; попробуйте 'region.append (« rect »)'. Вы можете поместить '.each (function (d) {console.log (d)})' next в цепочке, чтобы гарантировать, что 'rect' имеют свои родительские данные 'g'. – mgold

ответ

1

Существует несколько способов сделать это. Мой подход должен был бы создать гнездо два уровня для данных, по регионам и по полу:

dataset = d3.nest() 
    .key(function(d) { 
     return d.region; 
    }) 
    .sortKeys(d3.ascending) 
    .key(function(d) { 
     return d.sex; 
    }) 
    .entries(dataset); 

Это даст вам структуру, которую легко использовать в дальнейшем, который будет выглядеть примерно так:

key: "dodoma" 
    values: 
     key: "female" 
     values: 
      people_0: "43" 
      region: "dodoma" 
      sex: "female" 
     key: "male 
     values: 
      people_0: "77" 
      region: "dodoma" 
      sex: "male" 
key: "killimanj" 
    values: ... 

Это довольно многословие и может быть упрощено (например, с использованием .map). This is a great nest tutorial page для получения дополнительной информации.

С этого момента необходимо произвести всего несколько изменений для рисования rect. См. Этот плунж: http://embed.plnkr.co/PPLbQER4FERA6D7T4m9P/preview.

Plunker (http://plnkr.co/) является хорошей альтернативой jsfiddle, если вы хотите связать внешние данные кстати.

Несколько изменений отклониться от исходного примера Майка, в том числе с использованием class="male" \ "female" в CSS, чтобы получить синие \ розовых цветов, а не с помощью rect:first-child

+0

Замечательный - спасибо, Генри. Plunker, учебник по гнезду и код выглядят великолепно. –

1

http://bost.ocks.org/mike/selection/ объясняет, как работает данные в d3. если вы назовете его один раз, он «привяжет» каждую запись данных к указанному вами элементу.

В вашем случае вы должны гнездо() по регионам, и вы получите массив объектов, который выглядит как:

['regionA' : [{region, sex, people}, {region, sex, people}], 'regionB' : [{region, sex, people}, {region, sex, people}] 

вы затем сделать двойную итерацию данных. поэтому вы сначала вызываете .data() (его массив областей на этот раз, то есть regionA) и свяжите его с группой, называемой областью , а затем снова свяжите data() (на этот раз его массив объектов для одного региона, т.е. [{region, sex, people} , {region, sex, people}]) на прямоугольниках.

Смежные вопросы