Có cách nào để chuyển đổi các cột CSV thành các mối quan hệ phân cấp không?


27

Tôi có một csv gồm 7 triệu hồ sơ đa dạng sinh học trong đó các mức phân loại là cột. Ví dụ:

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

Tôi muốn tạo trực quan hóa trong D3, nhưng định dạng dữ liệu phải là một mạng, trong đó mỗi giá trị khác nhau của cột là con của cột trước đó cho một giá trị nhất định. Tôi cần phải đi từ csv đến một cái gì đó như thế này:

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

Tôi đã không nghĩ ra cách nào để làm điều này mà không cần sử dụng hàng ngàn vòng lặp. Có ai có gợi ý về cách tạo mạng này trên python hoặc javascript không?


Không liên quan đến câu hỏi của bạn, nhưng ngay sau khi tôi viết câu trả lời, tôi nhận thấy một nanPhylum có chứa Magnoliopsida. Cái gì nanvậy Phylum là Anthophyta hay còn gọi là Magnolia (đó là Phylum Angiospermae cũ).
Gerardo Furtado

Câu trả lời:


16

Để tạo đối tượng lồng nhau chính xác mà bạn muốn, chúng tôi sẽ sử dụng kết hợp JavaScript thuần túy và phương thức D3 có tên d3.stratify. Tuy nhiên, có nhớ rằng 7 triệu hàng (xin vui lòng xem Scriptum bài dưới đây) là rất nhiều để tính toán.

Điều rất quan trọng là phải đề cập rằng, đối với giải pháp được đề xuất này, bạn sẽ phải tách Vương quốc trong các mảng dữ liệu khác nhau (ví dụ: bằng cách sử dụng Array.prototype.filter). Hạn chế này xảy ra bởi vì chúng tôi cần một nút gốc và trong phân loại Linnaean, không có mối quan hệ nào giữa các Vương quốc (trừ khi bạn tạo "Miền" làm thứ hạng hàng đầu, sẽ là gốc cho tất cả các sinh vật nhân chuẩn, nhưng sau đó bạn sẽ có cùng vấn đề đối với Archaea và Vi khuẩn).

Vì vậy, giả sử bạn có CSV này (tôi đã thêm một số hàng) chỉ với một Vương quốc:

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

Dựa trên CSV đó, chúng tôi sẽ tạo một mảng ở đây có tên tableOfRelationships, như tên ngụ ý, có mối quan hệ giữa các cấp bậc:

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

Đối với dữ liệu trên, đây là 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"         |
+---------+----------------------+---------------+

Hãy xem nullnhư là cha mẹ của Animalia: đó là lý do tại sao tôi nói với bạn rằng bạn cần tách dữ liệu của mình theo Kingdoms, chỉ có thể có một nullgiá trị trong toàn bộ bảng.

Cuối cùng, dựa trên bảng đó, chúng tôi tạo cấu trúc phân cấp bằng cách sử dụng d3.stratify():

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

const hierarchicalData = stratify(tableOfRelationships);

Và đây là bản demo. Mở bảng điều khiển của trình duyệt của bạn (một đoạn mã không tốt cho nhiệm vụ này) và kiểm tra một số cấp độ ( children) của đối tượng:


Tái bút : Tôi không biết loại dataviz nào bạn sẽ tạo, nhưng bạn thực sự nên tránh xếp hạng phân loại. Toàn bộ phân loại Linnaean đã lỗi thời, chúng tôi không sử dụng các cấp bậc nữa: vì hệ thống phát sinh gen được phát triển vào giữa những năm 60, chúng tôi chỉ sử dụng phân loại, không có bất kỳ thứ hạng phân loại nào (giáo viên sinh học tiến hóa ở đây). Ngoài ra, tôi khá tò mò về 7 triệu hàng này, vì chúng tôi đã mô tả chỉ hơn 1 triệu loài!


3
. @ gerardo Cảm ơn câu trả lời của bạn, tôi sẽ xem liệu nó có hoạt động trong một mẫu của các hàng 7 giây không. Cơ sở dữ liệu chứa các hàng lặp đi lặp lại cho nhiều loài. Vì vậy, ý tưởng là chỉ ra có bao nhiêu hồ sơ cho một thứ hạng phân loại nhất định. Ý tưởng là tạo ra một cái gì đó tương tự như Cây băng có thể phóng to của Mike Bostock .
Andres Camilo Zuñiga Gonzalez

9

Thật dễ dàng để làm chính xác những gì bạn cần bằng python và python-benedictthư viện (nó là nguồn mở trên Github :

Cài đặt 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())

Đầu ra in đầu tiên sẽ là:

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

Đầu ra được in thứ hai sẽ là:

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

Điều này có vẻ đơn giản, vì vậy có lẽ tôi không hiểu vấn đề của bạn.

Cấu trúc dữ liệu bạn muốn là một tập hợp các từ điển, cặp khóa / giá trị lồng nhau. Từ điển vương quốc cấp cao nhất của bạn có một chìa khóa cho mỗi vương quốc của bạn, có giá trị là từ điển phylum. Một từ điển phylum (cho một vương quốc) có một khóa cho mỗi tên phylum và mỗi khóa có một giá trị là một từ điển lớp, v.v.

Để đơn giản hóa mã, từ điển chi của bạn sẽ có một khóa cho mỗi loài, nhưng các giá trị cho loài sẽ là từ điển trống.

Đây phải là những gì bạn muốn; không cần thư viện lạ.

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

Để kiểm tra nó, tôi đã sử dụng dữ liệu của bạn và pprinttừ thư viện chuẩn.

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

nhận được

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

Đọc lại câu hỏi của bạn, bạn có thể muốn có một bảng lớn các cặp ('liên kết từ nhóm chung hơn', 'liên kết đến nhóm cụ thể hơn'). Đó là, 'Animalia' liên kết đến 'Animalia: Chordata' và 'Animalia: Chordata' liên kết đến 'Animalia: Chordata: Mammalia ", v.v. Thật không may,' nan 'trong dữ liệu của bạn có nghĩa là bạn cần tên đầy đủ ở mỗi liên kết. Nếu ( cặp cha mẹ, con cái là những gì bạn muốn, đi trên cây theo cách này:

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

cho:

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"

Điều này không trả về một lệnh được lồng với namechildrennhư được yêu cầu trong câu hỏi.
Fabio Caccamo

Không, nó không có. Những gì được yêu cầu là "một cái gì đó như thế này"; Tôi coi đó là cố gắng tìm cấu trúc dữ liệu ý tưởng. Người ta chỉ có thể xây dựng một cấu trúc tùy chỉnh bằng cách đi bộ trên cây, một bài tập bốn dòng.
Charles Merriam

3

Trong Python, một cách để mã hóa cây là sử dụng a dict, trong đó các khóa đại diện cho các nút và giá trị liên quan là cha mẹ của nút:

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

Một lợi thế của điều này là bạn đảm bảo các nút là duy nhất, vì dictskhông thể có các khóa trùng lặp.

Thay vào đó, nếu bạn muốn mã hóa một biểu đồ có hướng tổng quát hơn (nghĩa là các nút có thể có nhiều hơn một cha mẹ), bạn có thể sử dụng danh sách cho các giá trị và có đại diện cho con (hoặc cha mẹ, tôi cho rằng):

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

Bạn có thể làm điều gì đó tương tự với các Đối tượng trong JS, thay thế Mảng cho danh sách, nếu cần.

Đây là mã Python tôi đã sử dụng để tạo dict đầu tiên ở trên:

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

Có lẽ cách đơn giản nhất để biến dữ liệu của bạn thành một hệ thống phân cấp là sử dụng toán tử lồng nhau tích hợp của D3 d3.nest():

Lồng nhau cho phép các phần tử trong một mảng được nhóm thành một cấu trúc cây phân cấp;

Bằng cách đăng ký các chức năng chính thông qua nest.key()bạn có thể dễ dàng chỉ định cấu trúc phân cấp của bạn. Giống như Gerardo đưa ra trong câu trả lời của mình, bạn có thể sử dụng thuộc .columnstính được hiển thị trên mảng dữ liệu sau khi phân tích cú pháp CSV của bạn để tự động tạo các chức năng chính này. Toàn bộ mã rút xuống các dòng sau:

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

Tuy nhiên, lưu ý rằng hệ thống phân cấp kết quả không hoàn toàn giống với cấu trúc được yêu cầu trong câu hỏi của bạn vì các đối tượng { key, values }thay vì { name, children }; Nhân tiện, điều này cũng đúng với câu trả lời của Gerardo. Tuy nhiên, điều này không gây hại cho cả hai câu trả lời vì kết quả có thể bị tắc nghẽn bằng d3.hierarchy()cách chỉ định chức năng truy cập trẻ em :

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

Bản demo sau đây đặt tất cả các phần lại với nhau:

Bạn cũng có thể muốn xem khóa d3.nest () và chuyển đổi giá trị thành tên và con trong trường hợp bạn cảm thấy cần phải có chính xác cấu trúc được đăng của mình.


Tận hưởng d3.nesttrong khi nó kéo dài: nó sẽ bị từ chối sớm.
Gerardo Furtado

@GerardoFurtado Đó là suy nghĩ đầu tiên của riêng tôi. Tuy nhiên, tôi không thể tìm thấy bất kỳ tài liệu tham khảo nào ủng hộ giả định này. Tôi nghĩ rằng tôi đã đọc về việc loại bỏ nó và thậm chí ngạc nhiên khi vẫn thấy nó chứa trong gói. Bộ sưu tập d3 được lưu trữ, nhưng không có ghi chú khấu hao trên đó. Bạn có bất kỳ thông tin đáng tin cậy về vấn đề này?
altocumulus

Đó là cho v6, nhìn vào đây . Nhìn vào "bộ sưu tập d3 [Đã xóa!]" .
Gerardo Furtado

@GerardoFurtado Không, đó không phải là tài liệu tham khảo mà tôi có trong đầu. Tuy nhiên, nó trả lời câu hỏi của tôi, thật đáng buồn.
altocumulus

1

Một thử thách thú vị. Hãy thử mã javascript này. Tôi sử dụng bộ củaashash để đơn giản.

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

Điều này tạo ra kết quả cuối cùng (tương tự) với những gì bạn muốn.

[
  {
    "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

Trên thực tế, @Charles Merriam giải pháp của nó rất thanh lịch.

Nếu bạn muốn đưa ra một kết quả giống như câu hỏi, hãy thử như sau.

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

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.