16

私は d3.js を使用して、動物 (生物) のファミリー (一度に最大 4000) をツリー グラフとして視覚化していますが、データ ソースはディレクトリ リストや名前空間オブジェクトのリストでもかまいません。私のデータは次のようになります:

json = {
    organisms:[
        {name: 'Hemiptera.Miridae.Kanakamiris'},
        {name: 'Hemiptera.Miridae.Neophloeobia.incisa'},
        {name: 'Lepidoptera.Nymphalidae.Ephinephile.rawnsleyi'},
        ... etc ...
    ]
}

私の質問は次のとおりです。ツリーマップなどの多くの d3 ビジュアライゼーションで使用されるように、上記のデータを階層的な親/子データ構造に変換する最良の方法を見つけようとしています(データの例については、d3/ のflare.json を参照してください) examples/data/ ディレクトリ)。目的のデータ構造の例を次に示します。

{"name": "ROOT",
 "children": [
        {"name": "Hemiptera",
         "children": [
             {"name": "Miridae",
              "children": [
                  {"name": "Kanakamiris", "children":[]},
                  {"name": "Neophloeobia",
                   "children": [
                       {"name": "incisa", "children":[] }
                   ]}
              ]}
         ]},
        {"name": "Lepidoptera",
         "children": [
             {"name": "Nymphalidae",
              "children": [
                  {"name": "Ephinephile",
                   "children": [
                       {"name": "rawnsleyi", "children":[] }
                   ]}
              ]}
         ]}
    ]}
}

編集:ROOTマスター親ノードが1つしかないd3の例の構造に準拠するために、元の必要なデータ構造をすべてノード内に含めました。

私は一般的な設計パターンを理解しようとしています。おまけとして、javascript、php、または python のいずれかでいくつかのソリューションを見たいと思っています。javascript が私の好みです。php に関して: 私が実際に使用しているデータは、結果を json としてエンコードする php スクリプトによるデータベースへの呼び出しから取得されます。php スクリプトのデータベースの結果は、それが php ベースの回答に使用される場合、順序付けられた配列 (以下を参照) です。

Array
(
    [0] => Array
        (
            ['Rank_Order'] => 'Hemiptera'
            ['Rank_Family'] => 'Miridae'
            ['Rank_Genus'] => 'Kanakamiris'
            ['Rank_Species'] => ''
        ) ........

ここで: 'Rank_Order'isParentOf 'Rank_Family'isParentOf 'Rank_Genus'isParentOf'Rank_Species'

ここでphpソリューションに焦点を当てた同様の質問をしましたが、唯一の答えはサーバーで機能していません。何が起こっているのかよくわかりません。そのため、デザインパターンの観点からこの質問をし、参照を含めたいと思いますJavaScriptとd3.jsでの私の実際の使用に。

4

3 に答える 3

7

以下は、提供した構造に固有のものであり、より一般的にすることはかなり簡単です。addChild関数を単純化できると確信しています。コメントがお役に立てば幸いです。

function toHeirarchy(obj) {

  // Get the organisms array
  var orgName, orgNames = obj.organisms;

  // Make root object
  var root = {name:'ROOT', children:[]};

  // For each organism, get the name parts
  for (var i=0, iLen=orgNames.length; i<iLen; i++) {
    orgName = orgNames[i].name.split('.');

    // Start from root.children
    children = root.children;

    // For each part of name, get child if already have it
    // or add new object and child if not
    for (var j=0, jLen=orgName.length; j<jLen; j++) {
      children = addChild(children, orgName[j]);      
    }
  }
  return root;

  // Helper function, iterates over children looking for 
  // name. If found, returns its child array, otherwise adds a new
  // child object and child array and returns it.
  function addChild(children, name) {

    // Look for name in children
    for (var i=0, iLen=children.length; i<iLen; i++) {

      // If find name, return its child array
      if (children[i].name == name) {
        return children[i].children;        
      }
    }
    // If didn't find name, add a new object and 
    // return its child array
    children.push({'name': name, 'children':[]});
    return children[children.length - 1].children;
  }
}
于 2012-08-26T07:57:35.967 に答える
5

最初の入力があれば、次のコードのようなものが目的の出力を生成すると思います。これが最も美しい方法だとは思いませんが、当時はそう思いました。

最初に文字列の初期配列を次のような配列の配列に分割するために、データを前処理するのが最も簡単なようです。

[
   ["Hemiptera","Miridae","Kanakamiris" ],
   ["Hemiptera","Miridae","Neophloeobia","incisa" ],
   //etc
]

...そして、それを処理して、次のような形式で作業オブジェクトを取得します。

  working = {
       Hemiptera : {
           Miridae : {
              Kanakamiris : {},
              Neophloeobia : {
                  incisa : {}
              }
           }
       },
       Lepidoptera : {
           Nymphalidae : {
              Ephinephile : {
                  rawnsleyi : {}
              }
           }
       }
    }

...配列ではなくオブジェクトを操作すると、子アイテムが既に存在するかどうかを簡単にテストできるためです。上記の構造を作成したら、最後にもう一度処理して、最終的な目的の出力を取得します。そう:

// start by remapping the data to an array of arrays
var organisms = data.organisms.map(function(v) {
        return v.name.split(".");
    });

// this function recursively processes the above array of arrays
// to create an object whose properties are also objects
function addToHeirarchy(val, level, heirarchy) {
    if (val[level]) {
        if (!heirarchy.hasOwnProperty(val[level]))
            heirarchy[val[level]] = {};
        addToHeirarchy(val, level + 1, heirarchy[val[level]]);
    }
}
var working = {};    
for (var i = 0; i < organisms.length; i++)
    addToHeirarchy(organisms[i], 0, working);

// this function recursively processes the object created above
// to create the desired final structure
function remapHeirarchy(item) {
    var children = [];
    for (var k in item) {
        children.push({
            "name" : k,
            "children" : remapHeirarchy(item[k])
        });
    }
    return children;
}

var heirarchy = {
    "name" : "ROOT",
    "children" : remapHeirarchy(working)
};

デモ: http://jsfiddle.net/a669F/1/

于 2012-08-26T03:12:07.007 に答える
2

私自身の質問に対する別の回答....昨日、私はd3.jsについて多くを学びませんでした。この質問に関連して、d3.nest() with .key() and .entries() は私の友人 (すべての d3 関数)。この回答には初期データの変更が含まれるため、私が尋ねた特定の質問に対する適切な回答とは言えない場合があります。ただし、誰かが同様の質問をしており、サーバー上で変更できる場合、これは非常に簡単な解決策です。

データベースからのデータを次の形式で返します。

json = {'Organisms': [
    { 'Rank_Order': 'Hemiptera',
      'Rank_Family': 'Miridae',
      'Rank_Genus': 'Kanakamiris',
      'Rank_Species': '' },
    {}, ...
]}

次にd3.nest()を使用

organismNest = d3.nest()
    .key(function(d){return d.Rank_Order;})
    .key(function(d){return d.Rank_Family;})
    .key(function(d){return d.Rank_Genus;})
    .key(function(d){return d.Rank_Species;})
    .entries(json.Organism);

これは次を返します:

{
key: "Hemiptera"
  values: [
    {
      key: "Cicadidae"
      values: [
        {
          key: "Pauropsalta "
          values: [
            {
              key: "siccanus"
              values: [
                       Rank_Family: "Cicadidae"
                       Rank_Genus: "Pauropsalta "
                       Rank_Order: "Hemiptera"
                       Rank_Species: "siccanus"
                       AnotherOriginalDataKey: "original data value"

etc etc, nested and lovely

これは、上記の質問で目的の形式として説明した配列と非常によく似たものを返しますが、いくつかの違いがあります。特に、すべてを囲む ROOT 要素はありません。また、元々必要だったキーは「名前」と「子」でしたが、.nest() はキーをそれぞれ「キー」と「値」として返します。これらの代替キーは、適切なデータ アクセサー関数 (基本的な d3 コンセプト) を定義するだけで d3.js で簡単に使用できます...しかし、それは質問の元の範囲を超えています...誰かにも役立つことを願っています

于 2012-09-01T03:37:57.390 に答える