Apakah ada cara untuk mengonversi kolom CSV menjadi hubungan hierarkis?

27

Saya memiliki csv dari 7 juta catatan keanekaragaman hayati di mana tingkat taksonomi adalah sebagai kolom. Contohnya:

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

Saya ingin membuat visualisasi dalam D3, tetapi format data harus berupa jaringan, di mana setiap nilai kolom yang berbeda adalah anak dari kolom sebelumnya untuk nilai tertentu. Saya perlu beralih dari csv ke yang seperti ini:

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

Saya belum datang dengan ide bagaimana melakukan ini tanpa menggunakan seribu untuk loop. Adakah yang punya saran tentang cara membuat jaringan ini baik dengan python atau javascript?

Andres Camilo Zuñiga Gonzalez
sumber
Tidak terkait dengan pertanyaan Anda, tetapi tepat setelah saya menulis jawaban saya, saya perhatikan nanuntuk sebuah Filum yang mengandung Magnoliopsida. Apa itu nan? Phylum adalah Anthophyta, atau Magnolia (alternatifnya adalah Phylum Angiospermae).
Gerardo Furtado

Jawaban:

16

Untuk membuat objek bersarang tepat yang Anda inginkan kami akan menggunakan campuran JavaScript murni dan metode D3 bernama d3.stratify. Namun, ingatlah bahwa 7 juta baris (lihat post scriptum di bawah) banyak yang bisa dihitung.

Sangat penting untuk menyebutkan bahwa, untuk solusi yang diusulkan ini, Anda harus memisahkan Kingdoms dalam array data yang berbeda (misalnya, menggunakan Array.prototype.filter). Pembatasan ini terjadi karena kita memerlukan simpul root, dan dalam taksonomi Linnaean tidak ada hubungan antara Kerajaan (kecuali jika Anda membuat "Domain" sebagai peringkat teratas, yang akan menjadi root untuk semua eukariota, tetapi kemudian Anda akan memiliki yang sama masalah untuk Archaea dan Bakteri).

Jadi, misalkan Anda memiliki CSV ini (saya menambahkan beberapa baris lagi) hanya dengan satu Kerajaan:

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

Berdasarkan CSV itu, kami akan membuat array di sini bernama tableOfRelationships yang, seperti namanya, memiliki hubungan antara peringkat:

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
    })
  })
});

Untuk data di atas, ini adalah 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"         |
+---------+----------------------+---------------+

Lihatlah nullsebagai induk dariAnimalia : itu sebabnya saya katakan bahwa Anda perlu memisahkan dataset Anda dengan Kingdoms, hanya ada satu nullnilai di seluruh tabel.

Akhirnya, berdasarkan tabel itu, kami membuat hierarki menggunakan d3.stratify():

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

const hierarchicalData = stratify(tableOfRelationships);

Dan ini adalah demo. Buka konsol peramban Anda (snipet tidak terlalu bagus untuk tugas ini) dan periksa beberapa level ( children) objek:


PS : Saya tidak tahu jenis data apa yang akan Anda buat, tetapi Anda benar-benar harus menghindari peringkat taksonomi. Seluruh taksonomi Linnaean sudah usang, kami tidak menggunakan peringkat lagi: karena sistematika filogenetik dikembangkan pada pertengahan 60-an, kami hanya menggunakan taksa, tanpa peringkat taksonomi (guru biologi evolusi di sini). Juga, saya cukup ingin tahu tentang 7 juta baris ini, karena kami telah menggambarkan lebih dari 1 juta spesies!

Gerardo Furtado
sumber
3
. @ gerardo Terima kasih atas jawaban Anda, saya akan melihat apakah itu berfungsi dalam sampel baris 7M. Basis data berisi baris berulang untuk banyak spesies. jadi idenya adalah untuk menunjukkan berapa banyak catatan yang ada untuk peringkat taksonomi tertentu. Idenya adalah untuk membuat sesuatu yang mirip dengan Zoomable Icicle Tree milik Mike Bostock .
Andres Camilo Zuñiga Gonzalez
9

Sangat mudah untuk melakukan apa yang Anda butuhkan menggunakan python dan python-benedictpustaka (ini open source di Github :

Instalasi 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())

Output cetak pertama adalah:

{
    "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": {}
                        }
                    }
                }
            }
        }
    }
}

Output cetak kedua adalah:

{
    "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": []
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}
Fabio Caccamo
sumber
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);

Master Kruk
sumber
5

Ini kelihatannya mudah, jadi mungkin saya tidak mengerti masalah Anda.

Struktur data yang Anda inginkan adalah serangkaian kamus, pasangan kunci / nilai. Kamus kerajaan tingkat atas Anda memiliki kunci untuk masing-masing kerajaan Anda, yang nilainya adalah kamus filum. Kamus filum (untuk satu kerajaan) memiliki kunci untuk setiap nama filum dan setiap kunci memiliki nilai yang merupakan kamus kelas, dan sebagainya.

Untuk membuatnya mudah dikodekan, kamus genus Anda akan memiliki kunci untuk setiap spesies, tetapi nilai untuk spesies tersebut adalah kamus kosong.

Ini harus menjadi apa yang Anda inginkan; tidak diperlukan perpustakaan aneh.

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

Untuk mengujinya, saya menggunakan data Anda dan pprintdari perpustakaan standar.

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

mendapatkan

{'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': {}}}}}}}}

Membaca pertanyaan Anda lagi, Anda mungkin menginginkan tabel besar pasangan ('tautan dari grup yang lebih umum', 'tautan ke grup yang lebih spesifik'). Yaitu, tautan 'Animalia' ke 'Animalia: Chordata' dan 'Animalia: Chordata' tautan ke 'Animalia: Chordata: Mammalia "dll. Sayangnya,' nan 'dalam data Anda berarti Anda memerlukan nama lengkap di setiap tautan. Jika ( orang tua, anak) pasangan adalah apa yang Anda inginkan, berjalan di pohon dengan cara ini:

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}"')

memberi:

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"
Charles Merriam
sumber
Ini tidak mengembalikan diktat bersarang dengan namedan childrenseperti yang diminta dalam pertanyaan.
Fabio Caccamo
Tidak, tidak. Yang diminta adalah "sesuatu seperti ini"; Saya menganggap itu sebagai mencoba menemukan struktur data ide. Seseorang bisa saja membangun struktur kebiasaan dengan berjalan di pohon, latihan empat baris.
Charles Merriam
3

Dalam Python, salah satu cara untuk menyandikan pohon adalah dengan menggunakan dict, di mana kunci mewakili node dan nilai yang terkait adalah induk simpul:

{'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}

Keuntungan dari hal ini adalah Anda memastikan bahwa node tersebut unik, karena dictstidak dapat memiliki kunci duplikat.

Jika Anda ingin menyandikan grafik yang diarahkan lebih umum sebagai gantinya (yaitu, node dapat memiliki lebih dari satu orang tua), Anda dapat menggunakan daftar untuk nilai dan memiliki anak-anak yang mewakili (atau orang tua, saya kira):

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

Anda dapat melakukan sesuatu yang mirip dengan Objek di JS, menggantikan Array untuk daftar, jika perlu.

Berikut kode Python yang saya gunakan untuk membuat dict pertama di atas:

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}}
dizzy77
sumber
2

Mungkin cara paling sederhana untuk mengubah data Anda menjadi hierarki adalah dengan memanfaatkan operator bersarang bawaan D3 d3.nest():

Nesting memungkinkan elemen dalam array dikelompokkan ke dalam struktur hierarki pohon;

Dengan mendaftarkan fungsi-fungsi utama via nest.key() Anda dapat dengan mudah menentukan struktur hierarki Anda. Sama seperti Gerardo yang dijabarkan dalam jawabannya, Anda dapat menggunakan .columnsproperti yang terpapar pada array data setelah menguraikan CSV Anda untuk secara otomatis menghasilkan fungsi-fungsi utama ini. Seluruh kode bermuara pada baris berikut:

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

Perhatikan, bagaimanapun, bahwa hierarki yang dihasilkan tidak persis menyerupai struktur yang diminta dalam pertanyaan Anda sebagai objek { key, values }bukan{ name, children } ; Ngomong-ngomong, ini juga berlaku untuk jawaban Gerardo. Ini tidak sakit untuk kedua jawaban, karena hasilnya dapat diatasi d3.hierarchy()dengan menetapkan fungsi pengakses anak - anak :

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

Demo berikut menyatukan semua bagian:

Anda mungkin juga ingin melihat kunci d3.nest () dan konversi nilai menjadi nama dan anak-anak jika Anda merasa perlu memiliki struktur yang diposting.

altocumulus
sumber
Nikmati d3.nestsaat berlangsung: akan segera ditinggalkan.
Gerardo Furtado
@ GerardoFurtado Itu adalah pikiran pertama saya sendiri. Namun, saya tidak dapat menemukan referensi yang mendukung asumsi ini. Saya pikir saya telah membaca tentang penghapusannya dan bahkan terkejut masih menemukannya di bundel. koleksi d3 diarsipkan, namun tidak ada catatan penghentian di atasnya. Apakah Anda memiliki informasi yang dapat dipercaya tentang masalah ini?
altocumulus
Itu untuk v6, lihat di sini . Lihatlah "d3-collection [Removed!]" .
Gerardo Furtado
@ GerardoFurtado Tidak, itu bukan referensi yang ada dalam pikiran saya. Tetap saja, itu menjawab pertanyaanku, dengan sedih.
altocumulus
1

Tantangan yang menyenangkan. Coba kode javascript ini. Saya menggunakan set Lodash untuk kesederhanaan.

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))

Ini menghasilkan hasil akhir (mirip) dengan apa yang Anda inginkan.

[
  {
    "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": []
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]
ZephDavies
sumber
1

Bahkan, @Charles Merriam solusinya sangat elegan.

Jika Anda ingin membuat hasil yang sama dengan pertanyaan, maka cobalah sebagai berikut.

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()

masukkan deskripsi gambar di sini

Carson Arucard
sumber