2015-03-20 1 views
1

Я пытаюсь использовать itertools.product для управления бухгалтерией некоторых вложенных циклов, где количество вложенных циклов неизвестно заранее. Ниже приведен конкретный пример, в котором я выбрал два вложенных цикла; выбор двух - только для ясности, то, что мне нужно, - это решение, которое работает для произвольного числа циклов.с помощью python itertools для управления вложенными для циклов

Этот вопрос предусматривает расширение/обобщение вопроса появляясь здесь: Efficient algorithm for evaluating a 1-d array of functions on a same-length 1d numpy array

Теперь я простирающийся выше технику с использованием itertools трюк я узнал здесь: Iterating over an unknown number of nested loops in python

Преамбула:

from itertools import product 

def trivial_functional(i, j): return lambda x : (i+j)*x 

idx1 = [1, 2, 3, 4] 
idx2 = [5, 6, 7] 
joint = [idx1, idx2] 

func_table = [] 
for items in product(*joint): 
    f = trivial_functional(*items) 
    func_table.append(f) 

В конце вышеперечисленного цикла itertools у меня есть 12-элементный 1-мерный массив функций, func_ta ble, каждый элемент был построен из тривиального_функционала.

Вопрос:

Пусть мне дадут пару целых чисел, (i_1, i_2), где эти числа должны быть интерпретированы как показатели idx1 и idx2 соответственно. Как я могу использовать itertools.product для определения правильного соответствующего элемента массива func_table ?

Я знаю, как взломать ответ, написав мою собственную функцию, которая имитирует бухгалтерию itertools.product, но, несомненно, есть встроенная функция itertools.product, предназначенная именно для этой цели?

+0

Позвольте мне перефразировать ваш вопрос: вам в основном нужны не только элементы входных последовательностей, но и их индексы? – dhke

+0

Да, это эффективный способ его поместить. – aph

+0

Опять же, я могу вручную посмотреть, как работает itertools и написать независимую функцию, которая возвращает индексы. Но это кажется ненужным повторением работы, так как itertools, должно быть, решил эту проблему уже, поэтому я бы предпочел использовать унифицированный синтаксис itertools, если он доступен. – aph

ответ

2

Я не знаю способ расчета плоского индекса другой, чем делать это сам. К счастью, это не так уж сложно:

def product_flat_index(factors, indices): 
    if len(factors) == 1: return indices[0] 
    else: return indices[0] * len(factors[0]) + product_flat_index(factors[1:], indices[1:]) 

>> product_flat_index(joint, (2, 1)) 
9 

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

from functools import reduce 
from operator import getitem, setitem, itemgetter 

def get_items(container, indices): 
    return reduce(getitem, indices, container) 

def set_items(container, indices, value): 
    c = reduce(getitem, indices[:-1], container) 
    setitem(c, indices[-1], value) 

def initialize_table(lengths): 
    if len(lengths) == 1: return [0] * lengths[0] 
    subtable = initialize_table(lengths[1:]) 
    return [subtable[:] for _ in range(lengths[0])] 

func_table = initialize_table(list(map(len, joint))) 
for items in product(*map(enumerate, joint)): 
    f = trivial_functional(*map(itemgetter(1), items)) 
    set_items(func_table, list(map(itemgetter(0), items)), f) 

>>> get_items(func_table, (2, 1)) # same as func_table[2][1] 
<function> 
+0

Это элегантно, и я думаю, что это «правильный» способ справиться с неизвестной степенью гнездования. Это почти наверняка медленнее. Но это питон, поэтому мы не против :-) –

+1

В вашем первом фрагменте кода, Uri, что такое функция product_index в строке 3? Я думаю, что это опечатка? – aph

+0

Спасибо! Исправлено. –

0

я предлагаю использовать enumerate() в нужном месте:

from itertools import product 

def trivial_functional(i, j): return lambda x : (i+j)*x 

idx1 = [1, 2, 3, 4] 
idx2 = [5, 6, 7] 
joint = [idx1, idx2] 

func_table = [] 
for items in product(*joint): 
    f = trivial_functional(*items) 
    func_table.append(f) 

Из того, что я понял из ваших комментариев и кода, func_table просто индексируется встречаемости определенного входа в последовательности. Вы можете получить доступ к нему снова с помощью:

for index, items in enumerate(product(*joint)): 
    # because of the append(), index is now the 
    # position of the function created from the 
    # respective tuple in join() 
    func_table[index](some_value) 
1

Это немного грязный, но здесь вы идете:

from itertools import product 

def trivial_functional(i, j): return lambda x : (i+j)*x 

idx1 = [1, 2, 3, 4] 
idx2 = [5, 6, 7] 
joint = [enumerate(idx1), enumerate(idx2)] 

func_map = {} 
for indexes, items in map(lambda x: zip(*x), product(*joint)): 
    f = trivial_functional(*items) 
    func_map[indexes] = f 

print(func_map[(2, 0)](5)) # 40 = (3+5)*5 
2

Настолько многочисленные ответы были весьма полезны, благодаря всем для решений.

Оказалось, что если я немного переделаю проблему с помощью Numpy, я смогу выполнить ту же самую бухгалтерию и решить проблему, которую я пытался решить с значительно улучшенной скоростью относительно чистых решений python. Хитрость заключается в том, чтобы использовать метод Numpy reshape вместе с обычным многомерным индексированием синтаксиса массива.

Вот как это работает. Мы просто преобразовать func_table в массив Numpy, и изменить его форму:

func_table = np.array(func_table) 
component_dimensions = [len(idx1), len(idx2)] 
func_table = np.array(func_table).reshape(component_dimensions) 

Теперь func_table может быть использован, чтобы вернуть правильную функцию не только для одной 2d точки, но для полного массива 2d точек :

dim1_pts = [3,1,2,1,3,3,1,3,0] 
dim2_pts = [0,1,2,1,2,0,1,2,1] 
func_array = func_table[dim1_pts, dim2_pts] 

Как обычно, Numpy на помощь!

 Смежные вопросы

  • Нет связанных вопросов^_^