Как исправить вложенный цикл Python, чтобы он работал быстрее? - PullRequest
0 голосов
/ 14 января 2019

Я пытаюсь написать фрагмент кода, который считывает некоторые строки с листа (используя openpyxl), а затем создает список на основе данных в строках и существующего списка (который основан на том же листе). Я создал цикл, который выполняет эту работу, но он слишком медленный, когда он получает файл большего размера. Есть ли способ сделать это быстрее?

biglist = []

rows = #some rows extracted from Excel with openpyxl

lists = [[1, 'E1000', 0], #this is just a small sample
         [1, 'F1000', 0], 
         [1, 'G1000', 4], 
         [1, 'H1000', 3], 
         [1, 'I1000', 5],
         [2, 'E1000', 1]] 

for row in rows:
        for cell in row:
            smalllist =[]
            smalllist.append(1)
            smalllist.append(cell.coordinate)
            style = 0
            for l in lists:
                for i,cl in enumerate(l):
                    if l[i][0] == smalllist[0] and l[i][1] == smalllist[1]:
                        style = l[i][2]
            smalllist.append(style)
            smalllist.append(cell.value)
            biglist.append(smalllist)

Что действительно замедляет цикл, так это кусок:

            for l in lists:
                for i,cl in enumerate(l):
                    if l[i][0] == smalllist[0] and l[i][1] == smalllist[1]:
                        style = l[i][2]

Есть ли способ ускорить его? Я пробовал использовать карты и списки, но это было не намного быстрее или даже медленнее.

Ответы [ 5 ]

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

В дополнение к ответу Ральфа (что на самом деле является первой очевидной вещью), существует еще несколько возможных микрооптимизаций:

Во-первых, не создавайте smalllist пустым только для добавления к нему, вместо этого заполните его напрямую (я предполагаю, что вы реализовали решение Ральфа) - это позволит избежать затрат на множественное разрешение и вызов метода - а затем псевдоним biglist.append и styles_dict.get (то же самое, избегает затрат на постоянное повторное разрешение метода):

biglist = []
# local alias
append = biglist.append
get_style = styles_dict.get

for row in rows:
    for cell in row:
        # avoids a double lookup
        coords = cell.coordinate
        append([1, coords, get_style((1, coords), 0), cell.value])

Или напишите вспомогательную функцию для создания «маленького списка» и используйте понимание списка:

def tranform(cell):
    coords = cell.coordinate
    return [1, coords, get_style((1, coords), 0), cell.value]

biglist = [transform(cell) for row in rows for cell in row]

Вы можете использовать модуль timeit, чтобы проверить, какое решение быстрее.

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

Вы можете создать dictionary из lists и поиска в словаре вместо циклического повторения lists каждый раз -

dict_keys = [(l[0], l[1]) for l in lists]
dict_values = [l[2] for l in lists]
dict_of_lists = { k:v for (k, v) in zip(dict_keys, dict_values) }

И вы можете заменить внутренний цикл for поиском -

key = (smalllist[0], smallist[1])
try:
    style = dict_of_lists[key]
except KeyError:
    style = 0
0 голосов
/ 14 января 2019

Вы не показали, что такое styles, но вы могли бы попытаться построить из него отображение (отображение имеет почти линейное время поиска), поэтому вам не нужно каждый раз повторять значение styles. Это при условии, что эта часть действительно является узким местом вашего кода (как вы указали в вопросе).

Вы можете построить dict перед циклом:

styles_dict = {
    (cl[0], cl[1]): cl[2]
    for cl in s
    for s in styles}

И посмотрите, какие стили внутри выглядят так:

style = styles_dict.get((smalllist[0], smalllist[1]))
if style is not None:
    pass

Это работает для вас?

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

Вы можете использовать dict для поиска ваших стилей, используя первые два столбца в качестве ключа:

styles = {(1, 'E1000'): 0, 
    (1, 'F1000'): 0, 
    ...}

Тогда смотри вверх и применяй свой стиль вот так ...

key = (smalllist[0], smalllist[1])
style = styles[key]

Это должно быть быстрее для больших списков стилей, так как поиск имеет постоянное время, а не линейный.

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

Попробуйте:

  1. импортировать ваш рабочий лист как фрейм данных Pandas
  2. применить любой фильтр к строкам и столбцам.

Примечание: По моему опыту, вы напишите гораздо меньше строк кода, и он будет работать быстрее, чем традиционный цикл for.

...