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

Я разработал класс, используемый для некоторых симуляций эпидемии, которые я делаю. Индивидуумами являются «S» (восприимчивые), «I» (инфицированные) или «R» (восстановленные). Это стандартные сокращения в исследовательском сообществе. Я предполагаю, что некоторые вычисления сделаны заранее, чтобы определить, кто будет заражен (и восстанавливаться), когда. В зависимости от изучаемой популяции я буду делать это разными способами.

Некоторые вопросы, которые у меня есть:

  1. Должен ли я беспокоиться о том, что я передаю некоторые диктофоны, которые позже могут быть отредактированы пользователем? Каков наилучший способ предотвратить это?

  2. Любые советы о том, как сделать это чистым? Я умеренно знаком с Python, но это самоучка, и это первый раз, когда я серьезно работаю с классами.

import scipy
import pylab as py
from collections import Counter

class SIREpidemic(object):
    """
    This will have the basic commands we want for any variety of SIR epidemic.
    When an epidemic is initialized, we will have already calculated the time of
    infection and recovery for each individual.  These will be passed in as dicts.
    Each individual, if infected will have individual.inftime as the time of infection
    and individual.rectime as time of recovery.
    """

    def __init__(self, infectionTime, recoveryTime, N):
        '''Process the infectionTime and RecoveryTime to determine who is susceptible
        when. infectionTime[individual] gives time of infection of individual.
        recoveryTime[individual] gives recovery time of individual (the keys for both
        lists must match).  N is the total population size (may include individuals
        that are never infected.'''

        statusTypes = ['S', 'I', 'R']
        self._timeSeries = {status:scipy.array([]) for status in statusTypes}
        self._timeSeries['t'] = scipy.array([])
        self.N = N
        self._infectionTime = infectionTime
        self._recoveryTime = recoveryTime

        infTimes = [infectionTime[individual] for individual in infectionTime.keys()]
        recTimes = [recoveryTime[individual] for individual in infectionTime.keys()]

        infTimeCounter = Counter(infTimes)
        recTimeCounter = Counter(recTimes)

        self._timeSeries['t'] = scipy.array(sorted(set(infTimes+recTimes)))
        for status in statusTypes:
            self._timeSeries[status] = 0*self._timeSeries['t']  #initializing to be the right size

        datum = {'S':1, 'I':0, 'R': 0}
        for index, time in enumerate(self._timeSeries['t']):  
            incidence = infTimeCounter[time]
            recoveries = recTimeCounter[time]
            datum['S'] -= float(incidence)/self.N
            datum['I'] += float(incidence - recoveries)/self.N
            datum['R'] += float(recoveries)/self.N
            for status in statusTypes:
                self._timeSeries[status][index]=datum[status]

    def infTime(self, individual):
        return self._infectionTime.get(individual,None)
    def recTime(self, individual):
        return self._recoveryTime.get(individual,None)

    def t(self):
        return self._timeSeries['t']
    def S(self):
        return self._timeSeries['S']
    def I(self):
        return self._timeSeries['I']
    def R(self):
        return self._timeSeries['R']

    def size(self):
        return self._timeSeries['R'][-1]
    def initial_size(self):
        return 1-self._timeseries['S'][0] 


    def plot(self,x=None, y=None, fid = None):  #x and y are either 'S', 'I', 'R', 'cumulative', or 't'.
        '''if no arguments, plot S,I, and R vs t.  if just one argument, then plot that versus t.  if two arguments, plot second versus first.  Need to add a collection of arguments to pass to plot.'''
        if y == None and x == None:
                self.plot('t', 'S')
                self.plot('t', 'I')
                self.plot('t', 'R')
        else:
            if y == None:
                y = x
                x = 't'
            elif x == None: #but y was something else
                x = 't'
            if fid != None:
                py.figure(fid)
            if x == 't':
                py.plot(self._timeSeries[x],self._timeSeries[y], label = r'$'+y+'$')
            else:
                py.plot(self._timeSeries[x],self._timeSeries[y], label = r'$'+y+'$ versus $'+x+'$')
11 голосов | спросил Joel 16 TueEurope/Moscow2014-12-16T04:54:04+03:00Europe/Moscow12bEurope/MoscowTue, 16 Dec 2014 04:54:04 +0300 2014, 04:54:04

1 ответ


8

Соглашения о кодировании Python

В коде есть несколько мест, где у вас есть следующее:

if y == None and x == None:

Pythonic способ проверить None использовать is:

if y is None and x is None:

if fid is not None:

Обратите внимание, что это работает, потому что None всегда один и тот же объект.

Есть несколько других незначительных проблем с форматированием, которые не считаются идиоматическим стилем кода Python (например, после запятой нет пробела). документ PEP8 описывает некоторые стандартные правила кодирования python. Я бы рекомендовал прочитать это.

Константы

В коде есть несколько примеров неназванных констант, например, 't' находится в нескольких местах. Создание именных переменных для них, вообще говоря, хорошо для удобства обслуживания.

Также statusTypes = ['S', 'I', 'R'] не ограничивается одним конкретным экземпляром класса, поэтому я бы переместил его за пределы __init__

Генераторные выражения

Если вам не нужен список, вам не нужно его создавать:

infTimes = [infectionTime[individual] for individual in infectionTime.keys()]
recTimes = [recoveryTime[individual] for individual in infectionTime.keys()]

infTimeCounter = Counter(infTimes)
recTimeCounter = Counter(recTimes)

Здесь вы должны сделать 2 списка в памяти, прежде чем использовать Counter. Учитывая, что вам действительно не нужен весь список, который будет создан здесь, я вместо этого предпочел бы использовать выражение генератора :

infTimes = (infectionTime[individual] for individual in infectionTime.keys())
recTimes = (recoveryTime[individual] for individual in infectionTime.keys())

infTimeCounter = Counter(infTimes)
recTimeCounter = Counter(recTimes)

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

Затем вы создадите отсортированный список и удалите дубликаты.

self._timeSeries['t'] = scipy.array(sorted(set(infTimes+recTimes)))

Эта работа уже была выполнена с помощью Counter, но вы могли бы сделать что-то вроде этого:

combined_count = infTimeCounter + recTimeCounter
self._timeSeries['t'] = scipy.array(sorted(combined_count.elements()))

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

Документация

В коде есть несколько частей, которые не сразу очевидны и выиграют от docstring. В частности, не совсем ясно, что они делают:

def size(self):
    return self._timeSeries['R'][-1]
def initial_size(self):
    return 1-self._timeseries['S'][0] 

plot

Метод plot действительно может помочь с объяснением того, что x, y и fid находятся в docstring. Докшрин также может быть лучше отформатирован, поскольку он очень широк.

def plot(self,x=None, y=None, fid=None):
    if y == None and x == None:
            self.plot('t', 'S')
            self.plot('t', 'I')
            self.plot('t', 'R')

Сначала отступы и форматирование отключены здесь, поскольку это пробел в python имеет важное значение, поскольку пространство шириной 8 символов здесь немного нечетное, просто сделайте все согласованным. Добавление дополнительной именованной переменной может помочь прочитать здесь. Вот как это выглядит с этими изменениями:

def plot(self, x=None, y=None, fid=None):
    if y is None and x is None:
        for status in statusTypes: 
            self.plot('t', status)
    elif y is None:
        y_label = x
        x_label = 't'
    elif x is None:
        y_label = y
        x_label = 't'
    else:
        y_label = y
        x_label = x

    if fid is None:
        py.figure(fid)
ответил shuttle87 16 TueEurope/Moscow2014-12-16T06:48:21+03:00Europe/Moscow12bEurope/MoscowTue, 16 Dec 2014 06:48:21 +0300 2014, 06:48:21

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132