Автоматически создавать словарную ветку в Python3 - PullRequest
0 голосов
/ 11 января 2019

Мне нужна ваша помощь для автоматизации разветвления словаря. Я перебираю строки большого набора данных с более чем 100 миллионами строк. Я разделяю каждую строку и выбираю интересующие части:

#I quickly wrote this to create a database so that you can test the script
fruits = ['apple','banana','citron']
cars = ['VW', 'Opel', 'Fiat']
countries = ['Bosnia','Egypt','USA','Ireland']
genomic_contexts = ['CDS', 'UTR5', 'UTR3', 'Intron']
database=[[fruits[random.randint(0,2)],cars[random.randint(0,2)],
countries[random.randint(0,3)],genomic_contexts[random.randint(0,3)]] 
for x in range(100)]

A_B_C_D_dict = {}
for line in database:
    line = line.split(',')
    A = line[0]
    B = line[1]
    C = line[2]
    D = line[3]
    #creating dict branch, if not existing yet and counting the combinations
    A_B_C_D_dict[A]=my_dict.get(A,{})
    A_B_C_D_dict[A][B]=my_dict[A].get(B,{})
    A_B_C_D_dict[A][B][C]=my_dict[A][B].get(C,{})
    A_B_C_D_dict[A][B][C][D]=my_dict[A][B][C].get(D,0)
    A_B_C_D_dict[A][B][C][D] += 1

Теперь я хотел бы определить функцию, которая делает это автоматически, вместо того, чтобы всегда писать ветку вручную (это должно работать для разных длин ветки, а не всегда 4)! Мой код должен выглядеть так:

for line in database:
    line = line.split(',')
    A = line[0]
    B = line[1]
    C = line[2]
    D = line[3]
    add_dict_branch('A_B_C_D_dict',0)
    A_B_C_D_dict[A][B][C][D] += 1

Моя попытка подобной функции заключается в следующем, но я могу себя унизить:

def select_nest(dict_2,keys,last,counter=0):
    if last == 0:
        return dict_2
    if counter == last:
        return dict_2[globals()[keys[counter-1]]]
    else:
        return select_nested(
        dict_2[globals()[keys[counter-1]]],keys,last,counter+1)


def add_dict_branch(dict_1,end_type):
    if type(dict_1) != type(str()):
        raise KeyError(dict_1," should be string!")
    keys = dict_1.split('_')
    keys = keys[:len(keys)-1]

    for x in range(len(keys)):
        key = globals()[keys[x]]

        if x < len(keys)-1:
            select_nest(globals()[dict_1],keys,x)[key] = \
            select_nest(globals()[dict_1],keys,x).get(key,{})
        else:
            select_nest(globals()[dict_1],keys,x)[key] = \
            select_nest(globals()[dict_1],keys,x).get(key,end_type)

Любые комментарии были бы хорошими, либо указав на мою ошибку или предложения по новым подходам Мне действительно нужно написать свой код в отношении производительности. Если он медленный, я не могу его использовать из-за нескольких миллионов итераций.

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Мне удалось найти ошибку в скрипте, и я также сделал ее более эффективной, напрямую передавая словарь в функцию вместо того, чтобы выполнять его повторный поиск через функцию globals (). Здесь я представляю свой рабочий скрипт для добавления словарных веток на случай, если кто-то обнаружит эту угрозу:

def select_nest(dict_2,keys,last,counter=0):

    if last == 0:
        return dict_2
    if counter == 0:
        counter += 1
    if counter == last:
        return dict_2[keys[counter-1]]
    else:
        return select_nest(dict_2[keys[counter-1]],keys,last,counter+1)

def add_dict_branch(dict_1,keys,end_type):

    for count,key in enumerate(keys):
        if count < len(keys)-1:
            if key not in select_nest(dict_1,keys,count).keys():
                select_nest(dict_1,keys,count)[key] = {}
        else:
            if key not in select_nest(dict_1,keys,count).keys():
                select_nest(dict_1,keys,count)[key] = end_type

А вот пример, как его использовать:

add_dict_branch(transcript_dict1,[method,RBP,RNA,transcript],0)
transcript_dict1[method][RBP][RNA][transcript] += 1
0 голосов
/ 11 января 2019

Для словаря d = dict(), следующий код

d.get(key, 0)
d[key] += 1

можно сделать в одной строке с объектом collections.defaultdict.

d = defaultdict(int)
d[key] += 1

Фактически, если ключ не существует, он создает ключ со значением на основе переданной функции (int() возвращает 0). Если вам нужны вложенные словари, где последний является целочисленным счетчиком, вам нужно что-то вроде

d = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(int))))

Это можно обобщить в функции.

def generate_nested_dicts(depth):
    if depth < 1:
        raise ValueError('The structure must have depth >= 1 but got {}'.format(depth))
    s = 'defaultdict(int)'
    for i in range(depth - 1):
        s = 'defaultdict(lambda: {})'.format(s)
    return eval(s)

и обновить ключ

def update_key(d, keys):
    for key in keys[:-1]:
        d = d[key]
    d[keys[-1]] += 1

Тогда вы можете использовать это так.

d = generate_nested_dictionaries(4)
for line in dataset:
    update_key(d, line)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...