Как использовать функцию квантования с моим файлом данных json

Я следил за руководством по хороплету с использованием D3 по этой ссылке. http://Synthese.sbecker.net/articles/2012/07/18/learning-d3-part-7-choropleth-maps

вместо безработицы у меня есть файл json, в котором указано количество автомобильных аварий по округам по штатам. Формат этого json-файла

 {
    "id":1001,
    "crashTotal":2
  },

И это для каждого из элементов в файле json; по одному на каждый округ. Идентификатор — это код штата + округа FIPS, а crashTotal — его тезка.

Я внимательно следил за кодом примера и наткнулся на функцию квантования.

// quantize function takes a data point and returns a number
// between 0 and 8, to indicate intensity, the prepends a 'q'
// and appends '-9'

function quantize(d) {
  return "q" + Math.min(8, ~~(data[d.id] * 9 / 12)) + "-9";
}

Для меня данные — это набор переменных, равный файлу crashes.json. Я не понимаю, почему я не могу использовать значения crashTotal из своих данных для использования в соответствии с функцией квантования.

Когда я пытаюсь использовать следующий код

~~data[d.id] or +data[d.id]

Я получаю 0 или NaN. Почему это? Я новичок в использовании d3, поэтому я не уверен, как это должно работать. Спасибо.

Мой код очень близок к коду примера, но с моими JSON-файлами страны и штата США, преобразованными из шейп-файлов переписи. Кто-нибудь может помочь?

РЕДАКТИРОВАТЬ: я бы решил, что объясню проблему немного подробнее. Это не проблема между использованием функции квантования или квантования по шкале d3, а скорее в том, как получить доступ к моим данным, чтобы раскрасить каждый округ. Как уже говорилось, мой файл данных представляет собой JSON в формате, указанном выше. Ниже показано, как я устанавливаю данные и как я вызываю квантизацию.

d3.json("Crashes.json", function(crashes) {

    max = +crashes[0].crashTotal;
    min = +crashes[0].crashTotal;
    maxFIPS = +crashes[0].id;
    minFIPS = +crashes[0].id;
    for(i = 0; i < crashes.length; i++) {
        if(+crashes[i].crashTotal > max) {
            maxFIPS = +crashes[i].id;
            max = +crashes[i].crashTotal;
        }
        if(+crashes[i].crashTotal < min) {
            minFIPS = +crashes[i].id;
            min = +crashes[i].crashTotal;
        }
    }

    data=crashes;

    //for(i = 0; i < data.length; i++) {
    //  document.writeln(data[i].id + " " + data[i].crashTotal);
//  }

    counties.selectAll("path")
        .attr("class", quantize);
        //.text(function (d){return "" + d.value;});

    //console.log("maxFIPS:" + maxFIPS + " minFIPS:" + minFIPS + "\n" + "max:" + max + " min:" + min);
});

function quantize(d) {

return "q" + Math.min(8, ~~data[d.id]) + "-9";
}

Если бы я заменил data[d.id] в приведенной выше функции квантизации, он бы фактически окрашивался в соответствии с цветовой схемой, указанной в скобках или документе CSS. Как мне заставить это использовать числа CrashTotal из моих данных?

РЕДАКТИРОВАТЬ [3-6-2014] После ответа Амелии у меня теперь есть следующая кодовая скобка.

d3.json("Crashes.json", function(crashes) {
        crashDataMap = d3.map();

        crashes.forEach(function(d) {crashDataMap.set(d.id, d);});

        data = crashDataMap.values();
        quantize = d3.scale.quantize()
        .domain(d3.extent(data, function(d) {return d.crashTotal;}))
        .range(d3.range(9).map(function(i) {return "q" + i + "-9"}));

        //min = d3.min(crashDataMap.values(), function(d) {return d.crashTotal;});
        //max = d3.max(crashDataMap.values(), function(d) {return d.crashTotal;});
        //console.log(quantize(crashDataMap.get(6037).crashTotal));

        counties.selectAll("path")
            .attr("class", function(d) {return quantize(crashDataMap.get(d.id).crashTotal);});
        });

Это должно дать мне правильный цвет для моей карты, но моя карта остается белой. Я могу подтвердить, что, тестируя квантизацию, я получаю правильное имя класса из моего файла CSS.

console.log(quantize(crashDataMap.get(1001).crashTotal)); //returns q0-9

Приветствуется дополнительная помощь. Спасибо.

EDIT2[3-6-2014] Я решил просто опубликовать весь код, который у меня есть здесь, надеясь, что кто-то сможет разобраться в безумии, почему это не работает.

//CSS or <style></style> bracket
svg {
        background: white;
    }

    path {
        fill: none;
        stroke: #000;
        stroke-width: 0.1px;
    }

    #counties path{

        stroke: #000;
        stroke-width: 0.25px;
    }


    #states path{
        fill: none;
        stroke: #000;
        stroke-width: 0.5px;
    }

.Blues .q0-9{fill:rgb(247,251,255)}
.Blues .q1-9{fill:rgb(222,235,247)}
.Blues .q2-9{fill:rgb(198,219,239)}
.Blues .q3-9{fill:rgb(158,202,225)}
.Blues .q4-9{fill:rgb(107,174,214)}
.Blues .q5-9{fill:rgb(66,146,198)}
.Blues .q6-9{fill:rgb(33,113,181)}
.Blues .q7-9{fill:rgb(8,81,156)}
.Blues .q8-9{fill:rgb(8,48,107)}

//Crashes.js file
    var width = 960
    var height = 500;

    var data;
    var crashDataMap;
    var quantize;

    var path = d3.geo.path();

    var zoom = d3.behavior.zoom()
        .on("zoom", zoomed);

    var svg = d3.select("#chart").append("svg")
        .attr("width", width)
        .attr("height", height)
        .call(zoom)
        .append("g");


    var counties = svg.append("g")
        .attr("id", "counties")
        .attr("class", "Blues");

    var states = svg.append("g")
        .attr("id", "states");


    d3.json("county.json", function(county) {
        var countyFeatures = topojson.feature(county, county.objects.county);
        counties.selectAll("path")
            .data(countyFeatures.features)
          .enter().append("path")
            .attr("d", path);
    }); 

    d3.json("state.json", function(state) {
        var stateFeatures = topojson.feature(state, state.objects.state);
        states.selectAll("path")
            .data(stateFeatures.features)
          .enter().append("path")
            .attr("d", path);   

    });


    d3.json("Crashes.json", function(crashes) {
            crashDataMap = d3.map();

            crashes.forEach(function(d) {crashDataMap.set(d.id, d);});

            data = crashDataMap.values();
            quantize = d3.scale.quantize()
            .domain(d3.extent(data, function(d) {return d.crashTotal;}))
            .range(d3.range(9).map(function(i) {return "q" + i + "-9"}));

            /*
            for(i  = 0; i < data.length; i++) {
                console.log(quantize(crashDataMap.get(data[i].id).crashTotal));
            }   
            */

            counties.selectAll("path")
                .attr("class", function(d) {return quantize(crashDataMap.get(d.id).crashTotal);});

        });

            function zoomed() {
                svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
            };

Взгляните, где я сгенерировал пути для округов. После оператора .enter().append("path"), если бы я ввел код .attr("class", "q8-9), он окрасил бы каждый округ в соответствии со схемой, определенной как q8-9.

Если я вызову counties.selectAll("path").attr("class", "q8-9") где-нибудь за пределами кодовой скобки, ничего не произойдет; карта остается белой. Это беспокоит меня, поскольку я явно понятия не имею, почему это может произойти. Я могу убедиться, что элементы пути есть как для округа, так и для штата.


person kthieu    schedule 05.03.2014    source источник
comment
Можете ли вы вывести содержимое «данных» после подготовки и разместить его здесь?   -  person snrlx    schedule 05.03.2014
comment
Мне нужно перебирать данные так ==›› for(i = 0; i ‹ data.length; i++) document.writeln(data[i].id + data[i].crashTotal); Видя, что данные содержат около 3000 элементов. Я опубликую все это на pastebin по следующей ссылке pastebin.com/SH74Hhnn   -  person kthieu    schedule 05.03.2014
comment
Я только что заметил, что в моей распечатке есть нулевое значение. В моих данных отсутствует число для crashTotal. Я исправлю это сейчас, но я все еще застрял в вышеуказанной проблеме.   -  person kthieu    schedule 05.03.2014
comment
Вместо использования функции квантования вы можете использовать шкалы квантования D3 -- bl.ocks.org/mbostock/4060606   -  person Lars Kotthoff    schedule 05.03.2014
comment
Я также использовал это как источник. Мой вопрос заключается в том, как определить домен и диапазон для использования шкал квантования d3. Домен, который я думаю, будет [min, max] от crashTotals из моих данных, в то время как диапазон каким-то образом вернет имя класса одного из определенных цветов в скобке ‹style›‹/style›. Возможно ли это также без очередей, потому что в примере используются очереди? Несмотря на это, проблема остается в том, что я не знаю, как получить доступ к значению crashTotal из моих данных, используя эту конкретную функцию квантования.   -  person kthieu    schedule 05.03.2014


Ответы (1)


Чтобы объяснить, что происходит в исходном коде:

В учебнике, на который вы ссылаетесь, используются два файла данных: один для карт и один для значений данных. К сожалению, он не содержит ссылок на фактически используемые файлы данных, но оба они имеют формат JSON. У округов есть свойство «id», и это свойство, по-видимому, используется в качестве ключей во втором файле данных JSON. То есть этот второй файл (data) должен иметь форму:

{
  "1001":   ".097",
  "1003":   ".091",
  "1005":   ".134",
  /*...*/
}

Это отличается от структуры данных, используемой в очень похожем примере Майка Бостока, в котором используется .tsv файл для данных о безработице, который затем используется для создания хэш-карты d3.map словарь данных.

var rateById = d3.map();
queue.defer(d3.tsv, "unemployment.tsv", function(d) { rateById.set(d.id, +d.rate); })
//this is equivalent to 
/*
  d3.tsv("unemployment.tsv", 
         function(d) { rateById.set(d.id, +d.rate); }, 
        readyFunction );
*/
//except that the readyFunction is only run when *both* data files are loaded.

//When two functions are given as parameters to d3.tsv, 
//the first one is called on each row of the data.
//In this case, it adds the id and rate as a key:value pair to the hashmap

Оба этих примера заканчиваются структурой данных, в которой значения id являются ключами, которые можно использовать для получения соответствующего значения данных. Напротив, ваши данные находятся в массиве без ключа, а ваши значения идентификатора являются просто еще одним свойством, а не ключом. Вот почему data[d.id] возвращал вам ошибку - вместо захвата номера данных, соответствующего этому идентификатору, он захватывает элемент вашего массива по индексу, эквивалентному номеру идентификатора. Это либо возвращает объект, который становится NaN при преобразовании в число, либо undefined, который становится нулем.

В любом примере, получив число, они хотят преобразовать его в целое число от 0 до 8, чтобы назначить один из ColorBrewer к пути. В учебнике Скотта Беккера для этого используется несколько произвольный расчет, в примере Майка Бостока используется шкала квантования с жестко закодированной областью. Вы говорите, что хотите определить домен на основе ваших данных.

Чтобы помочь вам понять, что вам нужно сделать:

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

Один из вариантов — создать объект d3.map (var crashDataMap = d3.map();), а затем использовать вызов forEach для существующего массива данных, чтобы добавить каждый объект на карту с помощью map.set(key, value) со своим идентификатором в качестве ключа.

crashDataArray.forEach( function(d){ crashDataMap.set( d.id, d) });

Затем, когда вы устанавливаете класс для своих фигур, вы можете использовать crashDataMap.get(d.id) для получения данных о сбоях, которые соответствуют идентификатору формы, и вы можете извлечь из них правильный номер.

Для разделения ваших данных на категории вы, вероятно, захотите использовать шкалу квантования, аналогичную примеру Майка Бостока. В исходном массиве данных вы можете использовать d3.extent с соответствующей функцией доступа. чтобы получить общее количество сбоев из каждой записи и найти максимальное и минимальное значение для настройки домена.

person AmeliaBR    schedule 06.03.2014
comment
Спасибо. Попробуем и посмотрим, как это работает. У меня уже есть максимальное и минимальное значение из приведенного выше цикла, поэтому я думаю, что могу оставить его для домена. - person kthieu; 06.03.2014
comment
Я отредактировал проблему с моей текущей ситуацией. Пожалуйста, посмотрите. Спасибо. - person kthieu; 07.03.2014
comment
Можете ли вы проверить в DOM, дают ли пути правильные имена классов? Насколько я могу судить из вашего обновленного кода, они должны быть. Что наводит меня на мысль, что проблема в вашем CSS. Используете ли вы правила CSS, такие как .Blues .q0-9{fill:rgb(247,251,255)}, как в примере? Потому что тогда вам нужно будет добавить класс Blues к вашему svg в целом. - person AmeliaBR; 07.03.2014
comment
Если ваши пути не получают добавленные классы, проблема, вероятно, заключается в том, что этот код выполняется до того, как ваши пути фактически созданы, т. е. до того, как функция обратного вызова из вашего файла geoJSON будет завершена. - person AmeliaBR; 07.03.2014
comment
Да. округа = svg.append(g).attr(id, округа).attr(класс, Блюз); - person kthieu; 07.03.2014
comment
Эта скобка кода crashes.json появляется после вызова скобки d3.json(URL для функции counties.json(counties)) . Итак, если вы говорите, что пути генерируются после завершения работы, будет ли это причиной? - person kthieu; 07.03.2014
comment
Кажется, что если я попытаюсь выбрать все элементы пути за пределами кода генерации county.json, ничего не будет применено. Например: counties.selectAll(path).attr(class, q8-9); нигде не работает. Я могу ясно видеть элементы пути county.json в DOM, однако - person kthieu; 07.03.2014
comment
Мне искренне жаль, что все мои проблемы там, но не могли бы вы взглянуть на мое последнее редактирование? - person kthieu; 07.03.2014
comment
Это будет не в тему, но имеет ли эта проблема какое-то отношение к Апатане? Я использую это для разработки этой визуализации. - person kthieu; 07.03.2014
comment
Проблема заключается в асинхронной природе кода — код внутри функций обратного вызова d3.json запускается всякий раз, когда этот конкретный файл готов, а не в том порядке, в котором они написаны в вашей программе. Пример Майка Бостока обошел это, используя d3.queue для запуска только одного обратного вызова после того, как все файлы были прочитаны. Учебное пособие Скотта Беккера обошло это, добавив код в конце функций обратного вызова both для добавления классов данных к путям округа, в зависимости от того, существовали ли пути и/или данные. Проверьте этот учебник еще раз, чтобы узнать, как он это сделал. - person AmeliaBR; 07.03.2014
comment
Я тоже об этом думал, поэтому я попытался использовать очередь, однако у меня есть другая проблема с этим, которую я нашел в другом вопросе здесь stackoverflow.com/questions/22247330/ - person kthieu; 08.03.2014