มีวิธีแปลงคอลัมน์ CSV เป็นความสัมพันธ์แบบลำดับชั้นหรือไม่


27

ฉันมีบันทึกความหลากหลายทางชีวภาพ 7 ล้าน csv ซึ่งระดับอนุกรมวิธานเป็นคอลัมน์ ตัวอย่างเช่น

RecordID,kingdom,phylum,class,order,family,genus,species
1,Animalia,Chordata,Mammalia,Primates,Hominidae,Homo,Homo sapiens
2,Animalia,Chordata,Mammalia,Carnivora,Canidae,Canis,Canis
3,Plantae,nan,Magnoliopsida,Brassicales,Brassicaceae,Arabidopsis,Arabidopsis thaliana
4,Plantae,nan,Magnoliopsida,Fabales,Fabaceae,Phaseoulus,Phaseolus vulgaris

ฉันต้องการสร้างการสร้างภาพข้อมูลใน D3 แต่รูปแบบข้อมูลจะต้องเป็นเครือข่ายโดยที่แต่ละคอลัมน์มีค่าต่างกันเป็นลูกของคอลัมน์ก่อนหน้าสำหรับค่าที่แน่นอน ฉันต้องเปลี่ยนจาก csv เป็นอย่างนี้:

{
  name: 'Animalia',
  children: [{
    name: 'Chordata',
    children: [{
      name: 'Mammalia',
      children: [{
        name: 'Primates',
        children: 'Hominidae'
      }, {
        name: 'Carnivora',
        children: 'Canidae'
      }]
    }]
  }]
}

ฉันไม่ได้คิดเกี่ยวกับวิธีการทำสิ่งนี้โดยไม่ใช้ลูปเป็นพัน ไม่มีใครมีข้อเสนอแนะเกี่ยวกับวิธีการสร้างเครือข่ายนี้ในหลามหรือจาวาสคริปต์?


ไม่เกี่ยวข้องกับคำถามของคุณ แต่หลังจากที่ฉันเขียนคำตอบของฉันฉันสังเกตเห็นnanสำหรับไฟลัมที่มี Magnoliopsida นั่นอะไรน่ะnan? ไฟลัมคือ Anthophyta หรือแม็กโนเลีย (มันคือไฟลัสอังจิโอเพอร์มาเอ)
Gerardo Furtado

คำตอบ:


16

สำหรับการสร้างวัตถุที่ซ้อนกันที่คุณต้องการเราจะใช้การผสมผสานของ JavaScript บริสุทธิ์และวิธี D3 d3.stratifyชื่อ อย่างไรก็ตามโปรดทราบว่ามี 7 ล้านแถว (โปรดดูโพสต์สคริปต์ด้านล่าง) เป็นจำนวนมากในการคำนวณ

เป็นเรื่องสำคัญมากที่ต้องพูดถึงว่าสำหรับวิธีแก้ปัญหาที่เสนอนี้คุณจะต้องแยกราชอาณาจักรออกเป็นชุดข้อมูลต่างๆ (ตัวอย่างเช่นการใช้Array.prototype.filter) ข้อ จำกัด นี้เกิดขึ้นเพราะเราต้องการโหนดรูทและในอนุกรมวิธาน Linnaean ไม่มีความสัมพันธ์ระหว่างราชอาณาจักร (เว้นแต่คุณจะสร้าง"โดเมน"เป็นอันดับสูงสุดซึ่งจะเป็นรูตสำหรับยูคาริโอตทั้งหมด แต่จากนั้นคุณจะมีเหมือนกัน ปัญหาสำหรับ Archaea และแบคทีเรีย)

ดังนั้นสมมติว่าคุณมี CSV นี้ (ฉันเพิ่มแถวอื่น ๆ ) ด้วยอาณาจักรเดียว:

RecordID,kingdom,phylum,class,order,family,genus,species
1,Animalia,Chordata,Mammalia,Primates,Hominidae,Homo,Homo sapiens
2,Animalia,Chordata,Mammalia,Carnivora,Canidae,Canis,Canis latrans
3,Animalia,Chordata,Mammalia,Cetacea,Delphinidae,Tursiops,Tursiops truncatus
1,Animalia,Chordata,Mammalia,Primates,Hominidae,Pan,Pan paniscus

จาก CSV นั้นเราจะสร้างอาร์เรย์ที่ชื่อtableOfRelationshipsซึ่งมีความหมายดังนี้มีความสัมพันธ์ระหว่างอันดับ:

const data = d3.csvParse(csv);

const taxonomicRanks = data.columns.filter(d => d !== "RecordID");

const tableOfRelationships = [];

data.forEach(row => {
  taxonomicRanks.forEach((d, i) => {
    if (!tableOfRelationships.find(e => e.name === row[d])) tableOfRelationships.push({
      name: row[d],
      parent: row[taxonomicRanks[i - 1]] || null
    })
  })
});

สำหรับข้อมูลข้างต้นนี่คือtableOfRelationships:

+---------+----------------------+---------------+
| (Index) |         name         |    parent     |
+---------+----------------------+---------------+
|       0 | "Animalia"           | null          |
|       1 | "Chordata"           | "Animalia"    |
|       2 | "Mammalia"           | "Chordata"    |
|       3 | "Primates"           | "Mammalia"    |
|       4 | "Hominidae"          | "Primates"    |
|       5 | "Homo"               | "Hominidae"   |
|       6 | "Homo sapiens"       | "Homo"        |
|       7 | "Carnivora"          | "Mammalia"    |
|       8 | "Canidae"            | "Carnivora"   |
|       9 | "Canis"              | "Canidae"     |
|      10 | "Canis latrans"      | "Canis"       |
|      11 | "Cetacea"            | "Mammalia"    |
|      12 | "Delphinidae"        | "Cetacea"     |
|      13 | "Tursiops"           | "Delphinidae" |
|      14 | "Tursiops truncatus" | "Tursiops"    |
|      15 | "Pan"                | "Hominidae"   |
|      16 | "Pan paniscus"       | "Pan"         |
+---------+----------------------+---------------+

ลองดูในnullฐานะที่เป็นผู้ปกครองของAnimalia: นั่นคือเหตุผลที่ฉันบอกคุณว่าคุณต้องแยกชุดข้อมูลของคุณตามราชอาณาจักรมีเพียงnullค่าเดียวในตารางทั้งหมด

ในที่สุดตามตารางนั้นเราสร้างลำดับชั้นโดยใช้d3.stratify():

const stratify = d3.stratify()
    .id(function(d) { return d.name; })
    .parentId(function(d) { return d.parent; });

const hierarchicalData = stratify(tableOfRelationships);

และนี่คือตัวอย่าง เปิดคอนโซลของเบราว์เซอร์ของคุณ (ตัวอย่างหนึ่งไม่ดีสำหรับงานนี้) และตรวจสอบหลายระดับ ( children) ของวัตถุ:


PS : ฉันไม่รู้ว่าคุณจะสร้างดาต้าวิซแบบไหน แต่คุณควรหลีกเลี่ยงการจัดอันดับทางด้านอนุกรมวิธาน อนุกรมวิธานทั้งหมดของ Linnaean ล้าสมัยเราไม่ได้ใช้อันดับอีกต่อไปเนื่องจากระบบวิวัฒนาการของสายวิวัฒนาการได้รับการพัฒนาในช่วงกลางยุค 60 เราใช้เพียงแท็กซ่าเท่านั้นโดยไม่มีอันดับอนุกรมวิธาน นอกจากนี้ฉันยังอยากรู้อยากเห็นเกี่ยวกับ 7 ล้านแถวเนื่องจากเราได้อธิบายเพียง 1 ล้านสปีชีส์!


3
. @ gerardo ขอบคุณสำหรับคำตอบของคุณฉันจะดูว่ามันใช้งานได้ในตัวอย่างของแถว 7M หรือไม่ ฐานข้อมูลมีแถวซ้ำหลายสายพันธุ์ ดังนั้นความคิดคือการแสดงจำนวนระเบียนที่มีสำหรับการจัดอันดับอนุกรม ความคิดที่จะสร้างสิ่งที่คล้ายกับไมค์สต็อคของ Zoomable เฉยชาต้นไม้
Andres Camilo Zuñiga Gonzalez

9

มันง่ายที่จะทำสิ่งที่คุณต้องการโดยใช้ python และไลบรารี่python-benedict (เป็นโอเพ่นซอร์สบนGithub :

การติดตั้ง pip install python-benedict

from benedict import benedict as bdict

# data source can be a filepath or an url
data_source = """
RecordID,kingdom,phylum,class,order,family,genus,species
1,Animalia,Chordata,Mammalia,Primates,Hominidae,Homo,Homo sapiens
2,Animalia,Chordata,Mammalia,Carnivora,Canidae,Canis,Canis
3,Plantae,nan,Magnoliopsida,Brassicales,Brassicaceae,Arabidopsis,Arabidopsis thaliana
4,Plantae,nan,Magnoliopsida,Fabales,Fabaceae,Phaseoulus,Phaseolus vulgaris
"""
data_input = bdict.from_csv(data_source)
data_output = bdict()

ancestors_hierarchy = ['kingdom', 'phylum', 'class', 'order', 'family', 'genus', 'species']
for value in data_input['values']:
    data_output['.'.join([value[ancestor] for ancestor in ancestors_hierarchy])] = bdict()

print(data_output.dump())
# if this output is ok for your needs, you don't need the following code

keypaths = sorted(data_output.keypaths(), key=lambda item: len(item.split('.')), reverse=True)

data_output['children'] = []
def transform_data(d, key, value):
    if isinstance(value, dict):
        value.update({ 'name':key, 'children':[] })
data_output.traverse(transform_data)

for keypath in keypaths:
    target_keypath = '.'.join(keypath.split('.')[:-1] + ['children'])
    data_output[target_keypath].append(data_output.pop(keypath))

print(data_output.dump())

เอาต์พุตการพิมพ์แรกจะเป็น:

{
    "Animalia": {
        "Chordata": {
            "Mammalia": {
                "Carnivora": {
                    "Canidae": {
                        "Canis": {
                            "Canis": {}
                        }
                    }
                },
                "Primates": {
                    "Hominidae": {
                        "Homo": {
                            "Homo sapiens": {}
                        }
                    }
                }
            }
        }
    },
    "Plantae": {
        "nan": {
            "Magnoliopsida": {
                "Brassicales": {
                    "Brassicaceae": {
                        "Arabidopsis": {
                            "Arabidopsis thaliana": {}
                        }
                    }
                },
                "Fabales": {
                    "Fabaceae": {
                        "Phaseoulus": {
                            "Phaseolus vulgaris": {}
                        }
                    }
                }
            }
        }
    }
}

เอาต์พุตที่พิมพ์ที่สองจะเป็น:

{
    "children": [
        {
            "name": "Animalia",
            "children": [
                {
                    "name": "Chordata",
                    "children": [
                        {
                            "name": "Mammalia",
                            "children": [
                                {
                                    "name": "Carnivora",
                                    "children": [
                                        {
                                            "name": "Canidae",
                                            "children": [
                                                {
                                                    "name": "Canis",
                                                    "children": [
                                                        {
                                                            "name": "Canis",
                                                            "children": []
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                },
                                {
                                    "name": "Primates",
                                    "children": [
                                        {
                                            "name": "Hominidae",
                                            "children": [
                                                {
                                                    "name": "Homo",
                                                    "children": [
                                                        {
                                                            "name": "Homo sapiens",
                                                            "children": []
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        },
        {
            "name": "Plantae",
            "children": [
                {
                    "name": "nan",
                    "children": [
                        {
                            "name": "Magnoliopsida",
                            "children": [
                                {
                                    "name": "Brassicales",
                                    "children": [
                                        {
                                            "name": "Brassicaceae",
                                            "children": [
                                                {
                                                    "name": "Arabidopsis",
                                                    "children": [
                                                        {
                                                            "name": "Arabidopsis thaliana",
                                                            "children": []
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                },
                                {
                                    "name": "Fabales",
                                    "children": [
                                        {
                                            "name": "Fabaceae",
                                            "children": [
                                                {
                                                    "name": "Phaseoulus",
                                                    "children": [
                                                        {
                                                            "name": "Phaseolus vulgaris",
                                                            "children": []
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}

5

var log = console.log;
var data = `
1,Animalia,Chordata,Mammalia,Primates,Hominidae,Homo,Homo sapiens
2,Animalia,Chordata,Mammalia,Carnivora,Canidae,Canis,Canis
3,Plantae,nan,Magnoliopsida,Brassicales,Brassicaceae,Arabidopsis,Arabidopsis thaliana
4,Plantae,nan,Magnoliopsida,Fabales,Fabaceae,Phaseoulus,Phaseolus vulgaris`;
//make array of rows with array of values
data = data.split("\n").map(v=>v.split(","));
//init tree
var tree = {};
data.forEach(row=>{
    //set current = root of tree for every row
    var cur = tree; 
    var id = false;
    row.forEach((value,i)=>{
        if (i == 0) {
            //set id and skip value
            id = value;
            return;
        }
        //If branch not exists create. 
        //If last value - write id
        if (!cur[value]) cur[value] = (i == row.length - 1) ? id : {};
        //Move link down on hierarhy
        cur = cur[value];
    });
}); 
log("Tree:");
log(JSON.stringify(tree, null, "  "));

//Now you have hierarhy in tree and can do anything with it.
var toStruct = function(obj) {
    let ret = [];
    for (let key in obj) {
        let child = obj[key];
        let rec = {};
        rec.name = key;
        if (typeof child == "object") rec.children = toStruct(child);
        ret.push(rec);
    }
    return ret;
}
var struct = toStruct(tree);
console.log("Struct:");
console.log(struct);


5

ดูเหมือนจะตรงไปตรงมาดังนั้นบางทีฉันอาจไม่เข้าใจปัญหาของคุณ

โครงสร้างข้อมูลที่คุณต้องการคือชุดพจนานุกรมคู่คีย์ / ค่าที่ซ้อนกัน พจนานุกรมราชอาณาจักรระดับสูงสุดของคุณมีคีย์สำหรับแต่ละอาณาจักรของคุณซึ่งมีค่าเป็นพจนานุกรมไฟลัม พจนานุกรมไฟลัม (สำหรับหนึ่งราชอาณาจักร) มีคีย์สำหรับชื่อไฟลัมแต่ละชื่อและแต่ละคีย์มีค่าที่เป็นพจนานุกรมคลาสและอื่น ๆ

เพื่อให้ง่ายต่อการเขียนโปรแกรมพจนานุกรมสกุลของคุณจะมีคีย์สำหรับแต่ละสปีชีส์ แต่ค่าสำหรับสปีชีส์จะเป็นพจนานุกรมที่ว่างเปล่า

นี่ควรเป็นสิ่งที่คุณต้องการ ไม่ต้องใช้ห้องสมุดแปลก ๆ

import csv

def read_data(filename):
    tree = {}
    with open(filename) as f:
        f.readline()  # skip the column headers line of the file
        for animal_cols in csv.reader(f):
            spot = tree
            for name in animal_cols[1:]:  # each name, skipping the record number
                if name in spot:  # The parent is already in the tree
                    spot = spot[name]  
                else:
                    spot[name] = {}  # creates a new entry in the tree
                    spot = spot[name]
    return tree

เพื่อทดสอบฉันใช้ข้อมูลของคุณและpprintจากไลบรารีมาตรฐาน

from pprint import pprint
pprint(read_data('data.txt'))

ได้รับ

{'Animalia': {'Chordata': {'Mammalia': {'Carnivora': {'Canidae': {'Canis': {'Canis': {}}}},
                                        'Primates': {'Hominidae': {'Homo': {'Homo sapiens': {}}}}}}},
 'Plantae': {'nan': {'Magnoliopsida': {'Brassicales': {'Brassicaceae': {'Arabidopsis': {'Arabidopsis thaliana': {}}}},
                                       'Fabales': {'Fabaceae': {'Phaseoulus': {'Phaseolus vulgaris': {}}}}}}}}

อ่านคำถามของคุณอีกครั้งคุณอาจต้องการตารางคู่ใหญ่ ('ลิงก์จากกลุ่มทั่วไปมากขึ้น', 'ลิงก์ไปยังกลุ่มที่เฉพาะเจาะจงมากขึ้น') นั่นคือลิงก์ 'Animalia' ไปยัง 'Animalia: Chordata' และ 'Animalia: Chordata' ลิงก์ไปยัง 'Animalia: Chordata: Mammalia "เป็นต้น แต่น่าเสียดายที่' nan 'ในข้อมูลของคุณหมายความว่าคุณต้องการชื่อเต็มในแต่ละลิงก์ถ้า (( คู่พ่อแม่, เด็ก) เป็นสิ่งที่คุณต้องการ, เดินบนต้นไม้ด้วยวิธีนี้:

def walk_children(tree, parent=''):
    for child in tree.keys():
        full_name = parent + ':' + child
        yield (parent, full_name)
        yield from walk_children(tree[child], full_name)

tree = read_data('data.txt')
for (parent, child) in walk_children(tree):
    print(f'parent="{parent}" child="{child}"')

ให้:

parent="" child=":Animalia"
parent=":Animalia" child=":Animalia:Chordata"
parent=":Animalia:Chordata" child=":Animalia:Chordata:Mammalia"
parent=":Animalia:Chordata:Mammalia" child=":Animalia:Chordata:Mammalia:Primates"
parent=":Animalia:Chordata:Mammalia:Primates" child=":Animalia:Chordata:Mammalia:Primates:Hominidae"
parent=":Animalia:Chordata:Mammalia:Primates:Hominidae" child=":Animalia:Chordata:Mammalia:Primates:Hominidae:Homo"
parent=":Animalia:Chordata:Mammalia:Primates:Hominidae:Homo" child=":Animalia:Chordata:Mammalia:Primates:Hominidae:Homo:Homo sapiens"
parent=":Animalia:Chordata:Mammalia" child=":Animalia:Chordata:Mammalia:Carnivora"
parent=":Animalia:Chordata:Mammalia:Carnivora" child=":Animalia:Chordata:Mammalia:Carnivora:Canidae"
parent=":Animalia:Chordata:Mammalia:Carnivora:Canidae" child=":Animalia:Chordata:Mammalia:Carnivora:Canidae:Canis"
parent=":Animalia:Chordata:Mammalia:Carnivora:Canidae:Canis" child=":Animalia:Chordata:Mammalia:Carnivora:Canidae:Canis:Canis"
parent="" child=":Plantae"
parent=":Plantae" child=":Plantae:nan"
parent=":Plantae:nan" child=":Plantae:nan:Magnoliopsida"
parent=":Plantae:nan:Magnoliopsida" child=":Plantae:nan:Magnoliopsida:Brassicales"
parent=":Plantae:nan:Magnoliopsida:Brassicales" child=":Plantae:nan:Magnoliopsida:Brassicales:Brassicaceae"
parent=":Plantae:nan:Magnoliopsida:Brassicales:Brassicaceae" child=":Plantae:nan:Magnoliopsida:Brassicales:Brassicaceae:Arabidopsis"
parent=":Plantae:nan:Magnoliopsida:Brassicales:Brassicaceae:Arabidopsis" child=":Plantae:nan:Magnoliopsida:Brassicales:Brassicaceae:Arabidopsis:Arabidopsis thaliana"
parent=":Plantae:nan:Magnoliopsida" child=":Plantae:nan:Magnoliopsida:Fabales"
parent=":Plantae:nan:Magnoliopsida:Fabales" child=":Plantae:nan:Magnoliopsida:Fabales:Fabaceae"
parent=":Plantae:nan:Magnoliopsida:Fabales:Fabaceae" child=":Plantae:nan:Magnoliopsida:Fabales:Fabaceae:Phaseoulus"
parent=":Plantae:nan:Magnoliopsida:Fabales:Fabaceae:Phaseoulus" child=":Plantae:nan:Magnoliopsida:Fabales:Fabaceae:Phaseoulus:Phaseolus vulgaris"

สิ่งนี้จะไม่ส่งคืนคำสั่งที่ซ้อนกันด้วยnameและchildrenตามที่ร้องขอในคำถาม
Fabio Caccamo

ไม่มันไม่ สิ่งที่ขอคือ "อะไรแบบนี้"; ฉันทำเช่นนั้นขณะพยายามค้นหาโครงสร้างข้อมูลความคิด เราสามารถสร้างโครงสร้างที่กำหนดเองโดยการเดินต้นไม้ซึ่งเป็นการออกกำลังกายสี่บรรทัด
Charles Merriam

3

ใน Python วิธีหนึ่งในการเข้ารหัสแผนผังคือการใช้ a dictโดยที่ปุ่มแทนโหนดและค่าที่เกี่ยวข้องคือพาเรนต์ของโหนด:

{'Homo sapiens': 'Homo',
 'Canis': 'Canidae',
 'Arabidopsis thaliana': 'Arabidopsis',
 'Phaseolus vulgaris': 'Phaseoulus',
 'Homo': 'Hominidae',
 'Arabidopsis': 'Brassicaceae',
 'Phaseoulus': 'Fabaceae',
 'Hominidae': 'Primates',
 'Canidae': 'Carnivora',
 'Brassicaceae': 'Brassicales',
 'Fabaceae': 'Fabales',
 'Primates': 'Mammalia',
 'Carnivora': 'Mammalia',
 'Brassicales': 'Magnoliopsida',
 'Fabales': 'Magnoliopsida',
 'Mammalia': 'Chordata',
 'Magnoliopsida': 'nan',
 'Chordata': 'Animalia',
 'nan': 'Plantae',
 'Animalia': None,
 'Plantae': None}

ข้อดีของการทำเช่นนี้คือคุณต้องแน่ใจว่าโหนดนั้นไม่ซ้ำกันเนื่องจากdictsไม่มีคีย์ที่ซ้ำกัน

หากคุณต้องการเข้ารหัสกราฟกำกับทั่วไปแทน (เช่นโหนดสามารถมีมากกว่าหนึ่งพาเรนต์) คุณสามารถใช้ลิสต์สำหรับค่าและมีเอเจนต์ลูก (หรือผู้ปกครองฉันคิดว่า):

{'Homo': ['Homo sapiens', 'ManBearPig'],
'Ursus': ['Ursus arctos', 'ManBearPig'],
'Sus': ['ManBearPig']}

คุณสามารถทำสิ่งที่คล้ายกับ Objects ใน JS, การแทนที่ Arrays สำหรับรายการถ้าจำเป็น

นี่คือรหัส Python ที่ฉันใช้สร้าง dict แรกข้างบน:

import csv

ROWS = []
# Load file: tbl.csv
with open('tbl.csv', 'r') as in_file:
    csvreader = csv.reader(in_file)

    # Ignore leading row numbers
    ROWS = [row[1:] for row in csvreader]
    # Drop header row
    del ROWS[0]

# Build dict
mytree = {row[i]: row[i-1] for row in ROWS for i in range(len(row)-1, 0, -1)}
# Add top-level nodes
mytree = {**mytree, **{row[0]: None for row in ROWS}}

2

วิธีที่ง่ายที่สุดในการเปลี่ยนข้อมูลของคุณให้เป็นลำดับชั้นคือการใช้ประโยชน์จากผู้ให้บริการการทำรังในตัวของ D3 d3.nest():

การทำรังช่วยให้องค์ประกอบในอาร์เรย์สามารถจัดกลุ่มเป็นโครงสร้างต้นไม้แบบลำดับชั้น

โดยการลงทะเบียนฟังก์ชั่นคีย์ผ่านทางnest.key()คุณสามารถระบุโครงสร้างของลำดับชั้นของคุณได้อย่างง่ายดาย เช่นเดียวกับ Gerardo ในคำตอบของเขาคุณสามารถใช้.columnsคุณสมบัติที่เปิดเผยใน data array หลังจากทำการแยก CSV ของคุณเพื่อสร้างฟังก์ชันหลักเหล่านี้โดยอัตโนมัติ รหัสทั้งหมดที่เดือดลงไปที่บรรทัดต่อไปนี้:

const nester = d3.nest();                             // Create a nest operator
const [, ...taxonomicRanks] = data.columns;           // Get rid of the RecordID property
taxonomicRanks.forEach(r => nester.key(d => d[r]));   // Register key functions
const nest = nester.entries(data);                    // Calculate hierarchy

แต่โปรดทราบว่าลำดับส่งผลให้ไม่ได้ว่ามีลักษณะคล้ายกับโครงสร้างที่มีการร้องขอในคำถามของคุณเป็นวัตถุที่มี{ key, values }แทน{ name, children }; โดยวิธีการนี้ถือเป็นจริงสำหรับคำตอบของ Gerardo สิ่งนี้ไม่ได้ส่งผลกระทบต่อทั้งสองคำตอบเนื่องจากผลลัพธ์สามารถแออัดd3.hierarchy()โดยการระบุฟังก์ชั่นสำหรับเด็ก :

d3.hierarchy(nest, d => d.values)   // Second argument is the children accessor

ตัวอย่างต่อไปนี้รวบรวมชิ้นส่วนทั้งหมดเข้าด้วยกัน:

คุณอาจต้องการดูคีย์ d3.nest () และการแปลงค่าเป็นชื่อและลูกในกรณีที่คุณรู้สึกว่าจำเป็นต้องมีโครงสร้างที่โพสต์ของคุณ


สนุกd3.nestในขณะที่มันกินเวลา: มันจะเลิกใช้ในไม่ช้า
Gerardo Furtado

@ GerardoFurtado นั่นเป็นความคิดแรกของฉัน อย่างไรก็ตามฉันไม่พบการอ้างอิงใด ๆ ที่สนับสนุนสมมติฐานนี้ ฉันคิดว่าฉันได้อ่านเกี่ยวกับการลบและรู้สึกประหลาดใจที่ยังพบว่ามีอยู่ในชุดข้อมูล d3-collection ถูกเก็บถาวร แต่ไม่มีโน้ตที่ไม่เห็นด้วย คุณมีข้อมูลที่เชื่อถือได้ในเรื่องนี้หรือไม่?
altocumulus

ที่สำหรับ v6 ดูที่นี่ ดู"d3 คอลเลกชัน [ลบ]"
Gerardo Furtado

@ GerardoFurtado ไม่นั่นไม่ใช่การอ้างอิงที่ฉันมีอยู่ในใจ ถึงกระนั้นก็ตอบคำถามของฉันเศร้า
altocumulus

1

ความสนุกที่ท้าทาย ลองใช้รหัสจาวาสคริปต์นี้ ฉันใช้ชุดของ Lodash เพื่อความเรียบง่าย

import { set } from 'lodash'

const csvString = `RecordID,kingdom,phylum,class,order,family,genus,species
    1,Animalia,Chordata,Mammalia,Primates,Hominidae,Homo,Homo sapiens
    2,Animalia,Chordata,Mammalia,Carnivora,Canidae,Canis,Canis
    3,Plantae,nan,Magnoliopsida,Brassicales,Brassicaceae,Arabidopsis,Arabidopsis thaliana
    4,Plantae,nan,Magnoliopsida,Fabales,Fabaceae,Phaseoulus,Phaseolus vulgaris`

// First create a quick lookup map
const result = csvString
  .split('\n') // Split for Rows
  .slice(1) // Remove headers
  .reduce((acc, row) => {
    const path = row
      .split(',') // Split for columns
      .filter(item => item !== 'nan') // OPTIONAL: Filter 'nan'
      .slice(1) // Remove record id
    const species = path.pop() // Pull out species (last entry)
    set(acc, path, species)
    return acc
  }, {})

console.log(JSON.stringify(result, null, 2))

// Then convert to the name-children structure by recursively calling this function
const convert = (obj) => {
  // If we're at the end of our chain, end the chain (children is empty)
  if (typeof obj === 'string') {
    return [{
      name: obj,
      children: [],
    }]
  }
  // Else loop through each entry and add them as children
  return Object.entries(obj)
    .reduce((acc, [key, value]) => acc.concat({
      name: key,
      children: convert(value), // Recursive call
    }), [])
}

const result2 = convert(result)

console.log(JSON.stringify(result2, null, 2))

สิ่งนี้จะสร้างผลลัพธ์สุดท้าย (คล้าย) กับสิ่งที่คุณต้องการ

[
  {
    "name": "Animalia",
    "children": [
      {
        "name": "Chordata",
        "children": [
          {
            "name": "Mammalia",
            "children": [
              {
                "name": "Primates",
                "children": [
                  {
                    "name": "Hominidae",
                    "children": [
                      {
                        "name": "Homo",
                        "children": [
                          {
                            "name": "Homo sapiens",
                            "children": []
                          }
                        ]
                      }
                    ]
                  }
                ]
              },
              {
                "name": "Carnivora",
                "children": [
                  {
                    "name": "Canidae",
                    "children": [
                      {
                        "name": "Canis",
                        "children": [
                          {
                            "name": "Canis",
                            "children": []
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "name": "Plantae",
    "children": [
      {
        "name": "Magnoliopsida",
        "children": [
          {
            "name": "Brassicales",
            "children": [
              {
                "name": "Brassicaceae",
                "children": [
                  {
                    "name": "Arabidopsis",
                    "children": [
                      {
                        "name": "Arabidopsis thaliana",
                        "children": []
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "name": "Fabales",
            "children": [
              {
                "name": "Fabaceae",
                "children": [
                  {
                    "name": "Phaseoulus",
                    "children": [
                      {
                        "name": "Phaseolus vulgaris",
                        "children": []
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]

1

ในความเป็นจริง @Charles Merriam ทางออกของมันนั้นสง่างามมาก

หากคุณต้องการให้ผลลัพธ์เหมือนกับคำถามให้ลองทำดังนี้

from io import StringIO
import csv


CSV_CONTENTS = """RecordID,kingdom,phylum,class,order,family,genus,species
1,Animalia,Chordata,Mammalia,Primates,Hominidae,Homo,Homo sapiens
2,Animalia,Chordata,Mammalia,Carnivora,Canidae,Canis,Canis
3,Plantae,nan,Magnoliopsida,Brassicales,Brassicaceae,Arabidopsis,Arabidopsis thaliana
4,Plantae,nan,Magnoliopsida,Fabales,Fabaceae,Phaseoulus,Phaseolus vulgaris
"""


def recursive(dict_data):
    lst = []
    for key, val in dict_data.items():
        children = recursive(val)
        lst.append(dict(name=key, children=children))
    return lst


def main():
    with StringIO() as io_f:
        io_f.write(CSV_CONTENTS)
        io_f.seek(0)
        io_f.readline()  # skip the column headers line of the file
        result_tree = {}
        for row_data in csv.reader(io_f):
            cur_dict = result_tree  # cursor, back to root
            for item in row_data[1:]:  # each item, skip the record number
                if item not in cur_dict:
                    cur_dict[item] = {}  # create new dict
                    cur_dict = cur_dict[item]
                else:
                    cur_dict = cur_dict[item]

    # change answer format
    result_list = []
    for cur_kingdom_name in result_tree:
        result_list.append(dict(name=cur_kingdom_name, children=recursive(result_tree[cur_kingdom_name])))

    # Optional
    import json
    from os import startfile
    output_file = 'result.json'
    with open(output_file, 'w') as f:
        json.dump(result_list, f)
    startfile(output_file)


if __name__ == '__main__':
    main()

ป้อนคำอธิบายรูปภาพที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.