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

수천 개의 for 루프를 사용하지 않고이를 수행하는 방법에 대한 아이디어를 얻지 못했습니다. 파이썬이나 자바 스크립트 에서이 네트워크를 만드는 방법에 대한 제안이 있습니까?


귀하의 질문과 관련이 없지만 답변을 작성한 직후 nan에는 Magnoliopsida를 포함하는 Phylum에 대한 것으로 나타났습니다 . 저게 뭐야 nan? Phylum은 Anthophyta 또는 대안으로 Magnolia입니다 (오래된 Phylum Angiospermae입니다).
Gerardo Furtado

답변:


16

정확히 중첩 된 객체를 만들려면 순수한 JavaScript와 D3 메소드를 혼합하여 사용합니다 d3.stratify. 그러나 7 백만 개의 행이 있다는 것을 명심하십시오 ( post scriptum 참조). 아래 참조)은 계산해야 할 것이 많다는 점을 .

이 제안 된 솔루션에 대해 왕국 을 다른 데이터 배열 (예 :)을 사용하여 분리 해야한다는 점을 언급하는 것이 매우 중요합니다 Array.prototype.filter. 이 제한은 루트 노드가 필요하기 때문에 발생하며 Linnaean 분류 체계에서 왕국 사이에는 아무런 관계가 없습니다 ( "도메인" 을 최상위 순위로 만들지 않는 한 모든 진핵 생물의 루트가되지만 Archaea와 Bacteria에 대한 문제).

따라서 하나의 왕국 으로이 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) 개체 의 여러 수준 ( )을 검사하십시오 .


추신 : 나는 어떤 종류의 dataviz를 만들지 모르겠지만 분류학 순위를 피해야합니다. Linnaean 분류 체계 전체가 구식이므로 더 이상 순위를 사용하지 않습니다. 60 년대 중반에 계통 발생 체계가 개발 되었기 때문에 분류 학적 순위가없는 분류 체계 만 사용합니다 (진화 생물학 교사). 또한, 우리는 백만 종이 넘는 종을 묘사했기 때문에이 7 백만 줄에 대해 매우 궁금합니다.


3
. @ gerardo 답장을 보내 주셔서 감사합니다. 7M 행의 샘플에서 작동하는지 확인하겠습니다. 데이터베이스에는 많은 종에 대해 반복되는 행이 포함되어 있습니다. 아이디어는 특정 분류 순위에 대한 레코드 수를 표시하는 것입니다. 아이디어는 Mike Bostock의 Zoomable Icicle Tree 와 비슷한 것을 만드는 것 입니다.
안드레스 카밀로 Zuñiga 곤잘레스

9

파이썬과 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

이것은 간단 해 보이므로 귀하의 문제를 이해하지 못할 수도 있습니다.

원하는 데이터 구조는 중첩 된 사전 세트, 키 / 값 쌍입니다. 최상위 왕국 사전에는 각 왕국에 대한 열쇠가 있으며 그 가치는 phylum 사전입니다. phylum 사전 (한 왕국에 대한)에는 각 phylum 이름에 대한 키가 있으며 각 키에는 클래스 사전 등의 값이 있습니다.

코드 작성을 간단하게하기 위해 속 사전에는 각 종의 키가 있지만 종 값은 빈 사전이됩니다.

이것은 당신이 원하는 것이어야합니다. 이상한 라이브러리가 필요하지 않습니다.

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"

이와 중첩 딕셔너리를 반환하지 않습니다 namechildren질문에 요청한다.
Fabio Caccamo

아닙니다. 요청 된 것은 "이것과 같은 것"이었다. 아이디어 데이터 구조를 찾으려고 노력합니다. 4 줄짜리 나무를 걸어서 커스텀 구조를 만들 수도 있습니다.
찰스 메리 암

3

파이썬에서 트리를 인코딩하는 한 가지 방법 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']}

필요한 경우 JS의 객체와 비슷한 것을 수행하여 목록을 배열로 대체 할 수 있습니다.

위의 첫 번째 dict를 작성하는 데 사용한 Python 코드는 다음과 같습니다.

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가 자신의 답변에 제시 한 것처럼 .columnsCSV를 구문 분석 한 후 데이터 배열에 노출 된 속성을 사용하여 이러한 주요 기능 생성을 자동화 할 수 있습니다 . 전체 코드는 다음 줄로 요약됩니다.

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- 컬렉션이 보관되었지만 더 이상 사용되지 않는 메모는 없습니다. 이 문제에 대한 신뢰할만한 정보가 있습니까?
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.