Реализация пула в цикле for с большим количеством входных данных

Я пытался улучшить свой код (с помощью numba и многопроцессорности), но у меня не получается, потому что у моей функции много аргументов.

Я уже упростил его другими функциями (см. ниже)...

Поскольку каждый агент (экземпляр класса) не зависит друг от друга для этих действий, я хотел бы заменить for на Pool.

Так что я бы получил большую функцию pooling(), которую я бы вызвал и передал список агентов

from multiprocessing import Pool

p = Pool(4)
p.map(pooling, list(agents))

Но где мне ДОБАВИТЬ все аргументы, которые потребуются функции объединения?

As it is:

def check_demographics(month, my_agents, families, firms, year, mortality_men, mortality_women, fertility, state_id):

    dummy = list(my_agents)
    d = str(state_id.iloc[0])

# Place where I would like to replace the LOOP. All below would be a function 

    for agent in dummy:

        if agent.get_region_id()[:2] == d:

            # Brithday
            if month % 12 == agent.month - 1:
                agent.update_age()

            # Mortality probability 
            if agent.get_gender() == 'Male':
                prob = mortality_men[mortality_men['age'] == agent.get_age()][year].iloc[0]

            # When gender is Female
            else:
                # Extract specific agent data to calculate mortality 'Female'
                prob = mortality_women[mortality_women['age'] == agent.get_age()][year].iloc[0]

            # Give birth decision 
                age = agent.get_age()
                if 14 < age < 50:
                    pregnant(agent, fertility, year, families, my_agents)

            # Mortality procedures 
            if fixed_seed.random() < prob:
                mortal(my_agents, my_graveyard, families, agent, firms)

Это самая трудоемкая функция в моей программе. И @jit мало чем помогает.

Огромное спасибо


person B Furtado    schedule 05.07.2016    source источник
comment
Примечание: глобальная переменная или параметр my_graveyard отсутствует.   -  person Laurent LAPORTE    schedule 05.07.2016


Ответы (1)


Да, параметров много! Рассмотрите возможность использования класса.

Что ж, поскольку Pool.map поддерживает только один повторяемый аргумент, вам нужно сгруппировать все в одном месте. Я предлагаю вам использовать шаблон «Фасад»: промежуточный класс, используемый для хранения всех необходимых параметров и имеющий единственный метод (я называю его check) без параметров (это метод).

class Facade(object):
    def __init__(self, agent, d, families, fertility, firms, month, mortality_men, mortality_women, my_agents,
                 my_graveyard, year):
        self.agent = agent
        self.d = d
        self.families = families
        self.fertility = fertility
        self.firms = firms
        self.month = month
        self.mortality_men = mortality_men
        self.mortality_women = mortality_women
        self.my_agents = my_agents
        self.my_graveyard = my_graveyard
        self.year = year

    def check(self):
        (agent, d, families, fertility, firms,
         month, mortality_men, mortality_women,
         my_agents, my_graveyard, year) = (
            self.agent, self.d, self.families, self.fertility, self.firms,
            self.month, self.mortality_men, self.mortality_women,
            self.my_agents, self.my_graveyard, self.year)
        if agent.get_region_id()[:2] == d:

            # Brithday
            if month % 12 == agent.month - 1:
                agent.update_age()

            # Mortality probability
            if agent.get_gender() == 'Male':
                prob = mortality_men[mortality_men['age'] == agent.get_age()][year].iloc[0]

            # When gender is Female
            else:
                # Extract specific agent data to calculate mortality 'Female'
                prob = mortality_women[mortality_women['age'] == agent.get_age()][year].iloc[0]

                # Give birth decision
                age = agent.get_age()
                if 14 < age < 50:
                    pregnant(agent, fertility, year, families, my_agents)

            # Mortality procedures
            if fixed_seed.random() < prob:
                mortal(my_agents, my_graveyard, families, agent, firms)

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

Тогда ваш цикл может быть примерно таким:

def check_demographics(month, my_agents, families, firms,
                       year, mortality_men, mortality_women,
                       fertility, state_id, my_graveyard):
    d = str(state_id.iloc[0])
    pool = Pool(4)
    facades = [Facade(agent, d, families, fertility, firms,
                      month, mortality_men, mortality_women,
                      my_agents, my_graveyard, year)
               for agent in my_agents]
    pool.map(Facade.check, facades)

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

person Laurent LAPORTE    schedule 05.07.2016
comment
Отлично, спасибо. Но вы правы, список my_agents меняется. Вот почему я создал новый list(agents)через который я повторяю. Будет ли это работать в этом случае? - person B Furtado; 06.07.2016
comment
Как минимум примените map к копии агентов: list(agents). Почему список меняется? - person Laurent LAPORTE; 06.07.2016
comment
Я сделал. Я также получил эту ошибку _pickle.PicklingError: Can't pickle <function Facade.check at 0x00000000084C0840>: attribute lookup check on demographics failed Список меняется, потому что я должен удалить агента, когда он/она умирает. И в вашем примере у меня нет агента, пока я не инициирую for loop - person B Furtado; 06.07.2016
comment
Вместо того, чтобы удалять мертвых агентов, создайте еще один список для хранения живых агентов. Используйте возвращаемое значение метода check: вернуть агента, если он жив, или None, если он мертв. - person Laurent LAPORTE; 06.07.2016
comment
Все равно ошибка. Все должно быть разборчиво. Мои входы включают панды DataFrames, списки экземпляров, строки и int - person B Furtado; 06.07.2016