Чи є спосіб перетворити стовпці CSV в ієрархічні відносини?


27

У мене є csv із 7 мільйонів записів біорізноманіття, де рівні таксономії є стовпцями. Наприклад:

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

Я не придумав, як це зробити, не використовуючи тисячу для циклів. Чи є хтось із пропозицій, як створити цю мережу на python або javascript?


Не пов’язане з вашим запитанням, але якраз після того, як я написав свою відповідь, я помітив nanтипи, що містять Magnoliopsida. Що це nan? Характер - це Anthophyta, або, магнолія, старий Phylum Angiospermae.
Джерардо Фуртадо

Відповіді:


16

Для створення точного вкладеного об'єкта, який ви хочете, ми будемо використовувати суміш чистого JavaScript та метод D3 з назвою d3.stratify. Однак майте на увазі, що 7 мільйонів рядків (див. Пост scriptum нижче) - це багато для обчислення.

Дуже важливо згадати, що для цього запропонованого рішення вам доведеться відокремлювати Королівства в різних масивах даних (наприклад, використовуючи Array.prototype.filter). Це обмеження виникає тому, що нам потрібен кореневий вузол, і в лінійській систематиці немає відносин між Королівствами (якщо ви не створите "Домен" як вищий ранг, який буде коренем для всіх еукаріотів, але тоді у вас буде те саме проблема для Археї та бактерій).

Отже, припустимо, у вас є цей 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: тому я сказав вам, що вам потрібно відокремити свій набір даних від Kingdoms, 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 : Я не знаю, який тип даних ви створите, але вам справді слід уникати таксономічних рангів. Вся лінійська систематика застаріла, ми вже не використовуємо ранги: оскільки філогенетична систематика була розроблена в середині 60-х, ми використовуємо лише таксони, без будь-якого таксономічного рангу (тут еволюційний викладач біології). Також мені досить цікаво про ці 7 мільйонів рядків, оскільки ми описали трохи більше 1 мільйона видів!


3
. @ gerardo Дякую за вашу відповідь, я побачу, чи працює вона у зразку з 7М рядків. База даних містить повторні рядки для багатьох видів. тому ідея полягає в тому, щоб показати, скільки записів існує для певного таксономічного рангу. Ідея полягає у створенні чогось схожого на Zoomble Icicle Tree Майка Бостока .
Андрес Каміло Зуньіга Гонсалес

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"

Це не повертає вкладену Dict з nameі childrenв відповідно до проханням в цьому питанні.
Фабіо Каккамо

Ні, це не так. Просили "щось подібне"; Я вважаю, що намагаюся знайти структуру даних ідеї. Можна було просто побудувати власну структуру, гуляючи по дереву, вправа на чотири лінії.
Чарльз Мерріам

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

Ви можете зробити щось подібне з об'єктами в JS, замінивши масиви списків, якщо це необхідно.

Ось код 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()вас, ви можете легко вказати структуру вашої ієрархії. Так само, як Герардо виклав у своїй відповіді, ви можете використовувати .columnsвластивість, викладену в масиві даних, після розбору 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 }; до речі, це аналогічно справедливо і для відповіді Герардо. Це не завадить для обох відповідей, оскільки результати можуть бути перевантажені d3.hierarchy(), вказавши функцію дитячого аксесуара :

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

Наступна демонстрація демонструє всі частини разом:

Ви також можете ознайомитись з перетворенням ключів і значень d3.nest () на ім’я та дітей, якщо ви відчуваєте необхідність мати саме вашу розміщену структуру.


Насолоджуйтесь, d3.nestпоки це триває: незабаром буде застаріло.
Джерардо Фуртадо

@GerardoFurtado Це була моя перша думка. Однак я не зміг знайти жодної посилання, що підтверджувала б це припущення. Я подумав, що прочитав про його видалення, і навіть здивувався, коли все-таки виявив його, що міститься в пакеті. d3-колекція архівується, але на ній відсутня примітка про застарілість. Чи є у вас якась достовірна інформація з цього питання?
altocumulus

Це для v6, дивіться тут . Подивіться на "d3-колекцію [Видалено!]" .
Джерардо Фуртадо

@GerardoFurtado Ні, це не було посиланням, яке я мав на увазі. І все-таки, це сумно відповідає на моє запитання.
altocumulus

1

Веселий виклик. Спробуйте цей код JavaScript. Я використовую набір Лодаша для простоти.

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

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.