use CamelCase when naming classes
documentation should be included for each class briefly describing its purpose and functionality
classes need an initialization statement
class MyClass:
''' this is where documentation information should go'''
def init(self,value, additional_autofunction_arguments): #define an attribute with the contents of the value (also called an instance) self.attribute = value #define a function that will happen automatically (becuse it is under the init function) also called a non-public function self.autofunction = self._function() #non-public functions require underscore before according to PEP-8 #write the function def_function(self): return function(self.attribute)
classes that inherit the attributes of its parent
class ChildClass(MyClass): #using MyClass in the arguments allows access to that class def init(self): # You can call the parent class init so all functions in the parent class will run MyClass.init(self,value, additional_autofunction_arguments): #then create as many instances of the child class self.child_attribute = value
you can utilize super.()init(self) when calling a parent class after it has been identified in the class: class ChildClass(MyClass)
class Person():
def say_hello(self):
return "Hi, how are you?"
def eat_breakfast(self):
self.hungry = False
return "Yum that was delish!"
gail = Person()
print("1.", vars(gail))
gail.name = "Gail"
gail.age = 29
gail.weight = 'None of your business!'
print("2.", gail.say_hello())
print("3.", gail.eat_breakfast())
print("4.", vars(gail))
class Person():
def eat_sandwhich(self):
if (self.hungry):
self.relieve_hunger()
return "Wow, that really hit the spot! I am so full, but more importantly, I'm not hangry anymore!"
else:
return "Oh, I don't think I can eat another bite. Thank you, though!"
def relieve_hunger(self):
print("Hunger is being relieved")
self.hungry = False
the_snail = Person() the_snail.name = "the Snail" the_snail.hungry = True print("1. ", the_snail.hungry) print("2. ", the_snail.eat_sandwhich()) print("3. ", the_snail.hungry) print("4. ", the_snail.eat_sandwhich())
class BankAccount():
def set_balance(self, amount):
print("SETTING BALANCE")
self._balance += amount
def get_balance(self):
print("GETTING BALANCE")
return self._balance
def make_withdrawal(self, amount_requested):
if (self.check_min_bal(amount_requested)):
return self.check_min_bal(amount_requested)
if (self.check_max_withdrawal(amount_requested)):
return self.check_max_withdrawal(amount_requested)
else:
# ----------- NOTE THE CHANGE FROM self.set_balance(amount) TO self.balance = amount --------- #
self.balance = -amount_requested
return f"${amount_requested}"
def check_min_bal(self, amount_requested):
# ----------- NOTE THE CHANGE FROM self.get_balance() TO self.balance = --------------- #
if ((self.balance - amount_requested) > self._minimum_balance):
return False
else:
return f"Sorry, you do not have enough funds to withdrawal ${amount_requested} and maintain your minimum balance of ${self._minimum_balance}"
def check_max_withdrawal(self, amount_requested):
if (self._max_withdrawal > amount_requested):
return False
else:
return f"Sorry, your maximum withdraw amount is {self._max_withdrawal}"
def make_deposit(self, amount_to_deposit):
try:
(float(amount_to_deposit))
# ----------- NOTE THE CHANGE FROM self.set_balance(amount) TO self.balance = amount ----------- #
self.balance = float(amount_to_deposit)
return f"Thank you for the deposit of ${amount_to_deposit}. Your balance is now: ${self._balance}"
except:
return f"{amount_to_deposit} is not a number"
# ----------- HERE is where we are using the property() function-------------------------- #
balance = property(get_balance, set_balance)
# just a non-class function that makes an account and initializes its properties... what a good idea
def make_account():
new_account = BankAccount()
new_account._balance = 0
new_account._minimum_balance = 250
new_account._max_withdrawal = 150
return new_account
account_three = make_account()
print("1.", account_three.get_balance())
print("2.", account_three.set_balance(1000)) # returns None since assignment returns None
print("3.", account_three.get_balance())
print("4.", account_three.make_withdrawal(1000))
print("5.", account_three.make_withdrawal(100))
print("6.", account_three.make_withdrawal(300))
print("7.", account_three.make_deposit(250))
print("8.", account_three.make_deposit(2.50))
print("9.", account_three.make_deposit("hello"))
print("10.", vars(account_three))
#Define Passenger Class Here
class Passenger():
def __init__(self, first, last, email, rides_taken):
self.first = first
self.last = last
self.email = email
self.rides_taken = rides_taken
rebecca_black = Passenger("Rebecca", "Black", "rebecca.black@gmail.com", 0) # initialize Rebecca Black here
print(rebecca_black.first) # "Rebecca"
print(rebecca_black.last) # "Black"
print(rebecca_black.email) # "rebecca.black@gmail.com"
print(rebecca_black.rides_taken) # 0
Note: make sure before creating complex environments to map an outline to better understand functionality and placement
class Customer():
def __init__(self, name=None, orders=[], location=None):
self.name=name
self.orders = orders
self.location = location
self.total_spent = sum([i['item_cost']*i['quantity'] for i in orders])
def add_order(self, item_name, item_cost, quantity):
self.orders.append({'item_name': item_name, 'item_cost':item_cost, 'quantity':quantity})
self.total_spent += item_cost * quantity
class Business():
def __init__(self, name=None, biz_type=None, city=None, customers = []):
self.name = name
self.biz_type = biz_type
self.city = city
self.customers = customers
def add_customer(self, customer):
self.customers.append(customer)
#add top_n customers function
def top_n_customers(self, n):
top_n = sorted(self.customers, key = lambda x: x.total_spent, reverse=True)[:n]
for c in top_n:
print(c.name, c.total_spent)
#create instance
startup = Business('etsy_store2076', 'crafts')
customer1 = Customer(name='Bob', orders=[])
customer1.add_order('sweater', 24.99, 1)
customer1.orders
[{'item_cost': 24.99, 'item_name': 'sweater', 'quantity': 1}]
customer1.total_spent
#Generating Customers and orders at scale
import numpy as np
names = ['Liam', 'Emma', 'Noah','Olivia','William','Ava',
'James','Isabella','Logan','Sophia','Benjamin','Mia','Mason',
'Charlotte','Elijah','Amelia','Oliver','Evelyn','Jacob','Abigail]']
items = [('sweater',50), ('scarf', 35), ('gloves', 20), ('hat', 20)]
for i in range(10):
customer = Customer(name=np.random.choice(names)) #Create a customer
n_orders = np.random.randint(1,5) #Create an order or two, or three, or four, or five!
for order_n in range(n_orders):
idx = np.random.choice(len(items)) #np.random.choice doesn't work with nested lists; workaround
item = items[idx]
item_name = item[0]
item_price = item[1]
quantity = np.random.randint(1,4)
customer.add_order(item_name, item_price, quantity)
#Add the customer to our business
startup.add_customer(customer)
#testing the top n customers function
startup.top_n_customers(5)
startup.top_n_customers(50)
class Dog:
_species = "canine" #class variable
def __init__(self):
self._species = "I'm a dog INSTANCE" #instance variable
@classmethod
def species(cls):
return cls._species #class method
new_dog = Dog() #instance object
print("1. ---", Dog._species, "--- This is the dog **class** directly accessing its class variable")
print("2. ---", new_dog._species, "--- This is an **instance object** of the dog class accessing its own instance variable")
print("3. ---", Dog.species(), "--- This is the dog class invoking the species *class method* to access its class variable")
print("4. ---", new_dog.species(), "--- This is an **instance object** of the dog class invoking the *class method*")
class Animal(object): #superclass that will inheirit other object definitions and will contain attributes of all of the subclasses associated with it
def __init__(self, name, weight):
self.name = name
self.weight = weight
self.species = None
self.size = None
self.food_type = None
self.nocturnal = False
def sleep(self):
if self.nocturnal:
print("{} sleeps during the day!".format(self.name))
else:
print("{} sleeps during the night!".format(self.name))
def eat(self, food):
if self.food_type == 'omnivore':
print("{} the {} thinks {} is Yummy!".format(self.name, self.species, food))
elif (food == 'meat' and self.food_type == "carnivore") or (food == 'plants' and self.food_type == 'herbivore'):
print("{} the {} thinks {} is Yummy!".format(self.name, self.species, food))
else:
print("I don't eat this!")
class Elephant(Animal): #subclass as part of Animal superclass
def __init__(self, name, weight):
super().__init__(name, weight)
self.size = 'enormous'
self.species = 'elephant'
self.food_type = 'herbivore'
self.nocturnal = False
class Tiger(Animal): #subclass as part of Animal superclass
def __init__(self, name, weight):
super().__init__(name, weight)
self.size = 'large'
self.species = 'tiger'
self.food_type = 'carnivore'
self.nocturnal = True
class Raccoon(Animal): #subclass as part of Animal superclass
def __init__(self, name, weight):
super().__init__(name, weight)
self.size = 'small'
self.species = 'raccoon'
self.food_type = 'omnivore'
self.nocturnal = True
class Gorilla(Animal): #subclass as part of Animal superclass
def __init__(self, name, weight):
super().__init__(name, weight)
self.size = 'large'
self.species = 'gorilla'
self.food_type = 'herbivore'
self.nocturnal = False
def add_animal_to_zoo(zoo, animal_type, name, weight): #adds animals to the zoo
animal = None
if animal_type == "Gorilla":
animal = Gorilla(name, weight)
elif animal_type == "Raccoon":
animal = Raccoon(name, weight)
elif animal_type == "Tiger":
animal = Tiger(name, weight)
else:
animal = Elephant(name, weight)
zoo.append(animal)
return zoo
#adding animals to the zoo
to_create = ['Elephant', 'Elephant', 'Raccoon', 'Raccoon', 'Gorilla', 'Tiger', 'Tiger', 'Tiger']
zoo = []
for i in to_create:
zoo = add_animal_to_zoo(zoo, i, "name", 100)
zoo
def feed_animals(zoo, time='Day'): #feed animals function
for animal in zoo:
if time == 'Day':
# CASE: Daytime feeding--Only feed the animals that aren't nocturnal
if animal.nocturnal == False:
# If the animal is a carnivore, feed it "meat". Otherwise, feed it "plants"
if animal.food_type == 'carnivore':
animal.eat('meat')
else:
animal.eat('plants')
else:
# CASE: Night-time feeding--feed only the nocturnal animals!
if animal.nocturnal == True:
if animal.food_type == 'carnivore':
animal.eat('meat')
else:
animal.eat('plants')
import numpy as np
import pandas as pd
from tqdm.autonotebook import tqdm
np.random.seed(0)
Our Person class should have the following attributes:
is_alive = True
is_vaccinated, a boolean value which will be determined by generating a random value between 0 and 1. We will then compare this to (1 - pct_vaccinated), a variable that should be passed in at instantiation time. If the random number is greater, then this attribute should be True-- otherwise, False
is_infected = False
has_been_infected = False
newly_infected = False
class Person(object):
def __init__(self):
self.is_alive = True
self.is_infected = False
self.has_been_infected = False
self.newly_infected = False
self.is_vaccinated = False
def get_vaccinated(self, pct_vaccinated):
if (1 - pct_vaccinated) < np.random.random():
self.is_vaccinated = True
Our init method should take in the following arguments at instantiation time:
The attributes self.disease_name, self.mortality_rate, and self.total_time_steps should be set to the corresponding arguments passed in at instantiation time.
The attribute self.r0 should be set to set to r0 / 100 to convert the number to a decimal between 0 and 1.
keeping track of what time step the simulation is on (self.current_time_step: set to 0)
current number of infected during this time step (self._total_infected_counter: set to 0)
the total number of people that have been infected at any time during the simulation. (self.current_infected_counter: set to 0)
create an array to hold all of the Person objects in our simulation. Set self.population equal to an empty list.
create the population, and determining if they are healthy, vaccinated, or infected.
class Simulation(object):
def __init__(self, population_size, disease_name, r0, mortality_rate, total_time_steps, pct_vaccinated, num_initial_infected):
self.r0 = r0 / 100.
self.disease_name = disease_name
self.mortality_rate = mortality_rate
self.total_time_steps = total_time_steps
self.current_time_step = 0
self.total_infected_counter = 0
self.current_infected_counter = 0
self.dead_counter = 0
self.population = []
# This attribute is used in a function that is provided for you in order to log statistics from each time_step.
# Don't touch it!
self.time_step_statistics_df = pd.DataFrame()
# Create a for loop the size of the population we want in this simulation
for i in range(population_size):
# Create new person
new_person = Person()
# We'll add infected persons to our simulation first. Check if the current number of infected are equal to the
# num_initial_infected parameter. If not, set new_person to be infected
if self.current_infected_counter != num_initial_infected:
new_person.is_infected = True
# dont forget to increment both infected counters!
self.total_infected_counter += 1
self.current_infected_counter += 1
# if new_person is not infected, determine if they are vaccinated or not by using their `get_vaccinated` method
# Then, append new_person to self.population
else:
new_person.get_vaccinated(pct_vaccinated)
self.population.append(new_person)
print("-" * 50)
print("Simulation Initiated!")
print("-" * 50)
self._get_sim_statistics()
def _get_sim_statistics(self):
#In the interest of time, this method has been provided for you. No extra code needed.
num_infected = 0
num_dead = 0
num_vaccinated = 0
num_immune = 0
for i in self.population:
if i.is_infected:
num_infected += 1
if not i.is_alive:
num_dead += 1
if i.is_vaccinated:
num_vaccinated += 1
num_immune += 1
if i.has_been_infected:
num_immune += 1
assert num_infected == self.current_infected_counter
assert num_dead == self.dead_counter
print("")
print("Summary Statistics for Time Step {}".format(self.current_time_step))
print("")
print("-" * 50)
print("Disease Name: {}".format(self.disease_name))
print("R0: {}".format(self.r0 * 100))
print("Mortality Rate: {}%".format(self.mortality_rate * 100))
print("Total Population Size: {}".format(len(self.population)))
print("Total Number of Vaccinated People: {}".format(num_vaccinated))
print("Total Number of Immune: {}".format(num_immune))
print("Current Infected: {}".format(num_infected))
print("Deaths So Far: {}".format(num_dead))
For any given time step, our simulation should complete the following steps in order:
Once every infected person has interacted with 100 random living people, resolve all current illnesses and new infections 2A.
HINT: To randomly select an item from a list, use np.random.choice()!
def infected_interaction(self, infected_person):
num_interactions = 0
while num_interactions < 100:
# Randomly select a person from self.population
random_person = np.random.choice(self.population)
# This only counts as an interaction if the random person selected is alive. If the person is dead, we do nothing,
# and the counter doesn't increment, repeating the loop and selecting a new person at random.
# check if the person is alive.
if random_person.is_alive:
# CASE: Random person is not vaccinated, and has not been infected before, making them vulnerable to infection
if random_person.is_vaccinated == False and random_person.has_been_infected == False:
# Generate a random number between 0 and 1
random_number = np.random.random()
# If random_number is greater than or equal to (1 - self.r0), set random person as newly_infected
if random_number >= (1 - self.r0):
random_person.newly_infected = True
# Dont forget to increment num_interactions, and make sure it's at this level of indentation
num_interactions += 1
#Add this function to our Simulation class
Simulation.infected_interaction = infected_interaction
Iterate through every person in the population.
Check if the person is alive (since we dont need to bother checking anything for the dead ones)
If the person is infected, we need to resolve whether they survive the infection or die from it.
Generate a random number between 0 and 1.
If this number is greater than (1 - self.mortality_rate), the person has died.
Set the person's .is_alive and .is_infected attributes both to False.
Increment the simulation's self.dead_counter attribute by 1.
Decrement the simulation's self.current_infected_counter attribute by 1.
Else, the person has survived the infection and is now immune to future infections.
Set the person's is_infected attribute to False
Set the person's has_been_infected attribute to True
Decrement the simulation's self.current_infected_counter by 1.
If the person is newly infected:
Set the person's newly_infected attribute to False
Set the person's is_infected attribute to True
Increment total_infected_counter and current_infected_counter by 1.
def _resolve_states(self):
"""
Every person in the simulation falls into 1 of 4 states at any given time:
1. Dead
2. Alive and not infected
3. Currently infected
4. Newly Infected
States 1 and 2 need no resolving, but State 3 will resolve by either dying or surviving the disease, and State 4 will resolve
by turning from newly infected to currently infected.
This method will be called at the end of each time step. All states must be resolved before the next time step can begin.
"""
# Iterate through each person in the population
for person in self.population:
# We only need to worry about the people that are still alive
if person.is_alive:
# CASE: Person was infected this round. We need to stochastically determine if they die or recover from the disease
# Check if person is_infected
if person.is_infected:
# Generate a random number
random_number = np.random.random()
# If random_number is >= (1 - self.mortality_rate), set the person to dead and increment the simulation's death
# counter
if random_number >= (1 - self.mortality_rate):
# Set is_alive and in_infected both to False
person.is_alive = False
person.is_infected = False
# Don't forget to increment self.dead_counter, and decrement self.current_infected_counter
self.dead_counter += 1
self.current_infected_counter -= 1
else:
# CASE: They survive the disease and recover. Set is_infected to False and has_been_infected to True
person.is_infected = False
person.has_been_infected = True
# Don't forget to decrement self.current_infected_counter!
self.current_infected_counter -= 1
# CASE: Person was newly infected during this round, and needs to be set to infected before the start of next round
elif person.newly_infected:
# Set is_infected to True, newly_infected to False, and increment both self.current_infected_counter and
# self.total_infected_counter
person.is_infected = True
person.newly_infected = False
self.current_infected_counter += 1
self.total_infected_counter += 1
Simulation._resolve_states = _resolve_states
Iterate through each person in the population
If the person is alive and infected, call self.infected_interaction() and pass in this infected person
Once we have looped through every person, call self._resolve_states() to resolve all outstanding states and prepare for the next round.
Log the statistics from this round by calling self._log_time_step_statistics(). This function has been provided for you further down the notebook.
Increment self.current_time_step.
def _time_step(self): """ Compute 1 time step of the simulation. This function will make use of the helper methods we've created above.
The steps for a given time step are:
1. Iterate through each person in self.population.
- For each infected person, call infected_interaction() and pass in that person.
2. Use _resolve_states() to resolve all states for the newly infected and the currently infected.
3. Increment self.current_time_step by 1.
"""
# Iterate through each person in the population
for person in self.population:
# Check only for people that are alive and infected
if person.is_alive and person.is_infected:
# Call self.infecteed_interaction() and pass in this infected person
self.infected_interaction(person)
# Once we've made it through the entire population, call self._resolve_states()
self._resolve_states()
# Now, we're almost done with this time step. Log summary statistics, and then increment self.current_time_step by 1.
self._log_time_step_statistics()
self.current_time_step += 1
Simulation._time_step = _time_step
def _log_time_step_statistics(self, write_to_file=False):
# Gets the current number of dead,
# CASE: Round 0 of simulation, need to create and Structure DataFrame
# if self.time_step_statistics_df == None:
# import pandas as pd
# self.time_step_statistics_df = pd.DataFrame()
# # col_names = ['Time Step', 'Currently Infected', "Total Infected So Far" "Alive", "Dead"]
# # self.time_step_statistics_df.columns = col_names
# # CASE: Any other round
# else:
# Compute summary statistics for currently infected, alive, and dead, and append them to time_step_snapshots_df
row = {
"Time Step": self.current_time_step,
"Currently Infected": self.current_infected_counter,
"Total Infected So Far": self.total_infected_counter,
"Alive": len(self.population) - self.dead_counter,
"Dead": self.dead_counter
}
self.time_step_statistics_df = self.time_step_statistics_df.append(row, ignore_index=True)
if write_to_file:
self.time_step_statistics_df.to_csv("simulation.csv", mode='w+')
Simulation._log_time_step_statistics = _log_time_step_statistics
Start a for loop that runs self.total_time_steps number of times.
Display a message telling the user the time step that it is currently working on.
Call self._time_step()
Once the simuluation has finished, write the DataFrame containing the summary statistics from each step to a csv file.
def run(self): """ The main function of the simulation. This will run the simulation starting at time step 0, calculating and logging the results of each time step until the final time_step is reached. """
for _ in tqdm(range(self.total_time_steps)):
# Print out the current time step
print("Beginning Time Step {}".format(self.current_time_step))
# Call our `_time_step()` function
self._time_step()
# Simulation is over--log results to a file by calling _log_time_step_statistics(write_to_file=True)
self._log_time_step_statistics(write_to_file=True)
Simulation.run = run
create a simulation with the following parameters:
Population size of 2000 Disease name is Ebola r0 value of 2 Mortality rate of 0.5 20 time steps Vaccination Rate of 0.85 50 initial infected
sim = Simulation(2000, "Ebola", 2, 0.5, 20, .85, 50)