Список языков и его обработка

Я вот подумал: представление языков в виде дерева не очень удобно, т.к. с поиском вхождения того или иного языка в список начинаются проблемы. Поэтому, довольно логичным шагом стало преобразовать список в первую нормальную форму, с ней будет работать проще всего. Текущий вид списка:

langs_info[:1]

[{'langs': [{'lang_name': u'A+',
'type': u'Array language',
'wiki_link': u'A+ (programming language)'},
{'lang_name': u'APL',
'type': u'Array language',
'wiki_link': u'APL (programming language)'},
{'lang_name': u'Chapel',
'type': u'Array language',
'wiki_link': u'Chapel (programming language)'},
{'lang_name': u'Fortran', 'type': u'Array language', 'wiki_link': None},
{'lang_name': u'J',
'type': u'Array language',
'wiki_link': u'J (programming language)'},
{'lang_name': u'Julia',
'type': u'Array language',
'wiki_link': u'Julia (programming language)'},
{'lang_name': u'K',
'type': u'Array language',
'wiki_link': u'K (programming language)'},
{'lang_name': u'Matlab', 'type': u'Array language', 'wiki_link': None},
{'lang_name': u'S',
'type': u'Array language',
'wiki_link': u'S (programming language)'},
{'lang_name': u'X10',
'type': u'Array language',
'wiki_link': u'X10 (programming language)'},
{'lang_name': u'ZPL',
'type': u'Array language',
'wiki_link': u'ZPL (programming language)'}],
'name': u'Array language'}]
def flat_langs(langs_info):
    flatten_langs = []
    for langs in langs_info:
        for lang in langs['langs']:
            lang['type'] = langs['name']
            flatten_langs.append(lang)
    return flatten_langs

В принципе, полученный код работает достаточно быстро, но меня гложут сомнения, может быть, проще всего сразу, на этапе построения списка преобразовывать? Кроме того, я пока что не очень понимаю принципов работы с памятью в Python. Например, будут ли в коде выше создаваться копии объектов или будут переиспользованы старые? Если говорить о скорости работы, то:

%timeit flat_langs(langs_info)
1000 loops, best of 3: 313 µs per loop

Добавление элемента в словарь должно выйти быстрее и предыдущий код построения списка языков можно просто переписать следующим образом:

def process_type(section):
    result = []
    lang_type = re.search('==\s*(.+[^\s*])\s*==', section)
    lines = section.split('\n')
    # * [[wiki_link | lang_name]]
    r = re.compile('^\*+\s*(\[\[' # * or ** and [[
                        '((?P<wiki_link>[^[]*?)(\|))?' #optional wiki_link using lazy algorithm
                         '(?P<lang_name>[^{}]+?)'
                         '\]\])') # ]]
    for line in lines:
        lang = r.search(line)
        if lang:
            lang_rec = lang.groupdict()
            lang_rec['lang_type'] = lang_type.group(1)
            result.append(lang_rec)
    return result

langs_info = []
for section in langs.sections[:-3]:
    section_data = langs.section(section['index'])
    lang_list = process_type(section_data)
    langs_info += lang_list

Тем не менее, все это было не более чем подготовкой. Дальше я планировал загрузить данные в DataFrame из Pandas и работать уже с ними. Решение довольно логичное, т.е. возможности предоставляемые DataFrame довольно обширны и манипулировать данными очень удобно.
Но, опять таки с учетом небольшого опыта работы с Pandas я мучаюсь сомнениями на тему того, нужно ли строить индексы 1 в DataFrame или нет 2.

data = pd.DataFrame.from_dict(langs_info) # (1)
data_with_idx = data.set_index('lang_name') # (2)

В принципе, DataFrame можно проиндексировать и “на месте”, но мне для экспериментов нужны 2 штуки, так что логичнее оставить оба. Содержимое не индексированного DataFrame:

data.head(10)
lang_name lang_type wiki_link
0 A+ Array language A+ (programming language)
1 APL Array language APL (programming language)
2 Chapel Array language Chapel (programming language)
3 Fortran Array language None
4 J Array language J (programming language)
5 Julia Array language Julia (programming language)
6 K Array language K (programming language)
7 Matlab Array language None
8 S Array language S (programming language)
9 X10 Array language X10 (programming language)

И проиндексированный DataFrame:

data_with_idx.head(10)
lang_type wiki_link
lang_name
A+ Array language A+ (programming language)
APL Array language APL (programming language)
Chapel Array language Chapel (programming language)
Fortran Array language None
J Array language J (programming language)
Julia Array language Julia (programming language)
K Array language K (programming language)
Matlab Array language None
S Array language S (programming language)
X10 Array language X10 (programming language)

А вот плане скорости работы индексы позволяют ускорить код в ~2 раза:

%timeit data_idx2.ix['Rust']
1000 loops, best of 3: 277 µs per loop
%timeit data[data.lang_name == 'Rust']
1000 loops, best of 3: 482 µs per loop

Но! Если искать по тексту на прямую, то можно организовать регистронезависимый поиск, например, что тоже интересно, пусть и ощутимо медленнее:

%timeit data[data.lang_name.str.lower() == 'rust']
1000 loops, best of 3: 1.07 ms per loop

Если говорить о данном конкретном примере, то, наверное, все равно, т.к. объем
данных очень не велик, но в случае больших объемов, ускорение в 2 раза может
оказаться существенным.

Leave a Reply