Skip to main content
Code Review

Return to Question

deleted 693 characters in body; edited tags; edited title
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238

Python Code Review, Inventory Simulationsimulation using Pandas DataframeDataFrame

I've been learning pythonPython like for a year and started learning a little about pandas DataFrameDataFrame. I made this little program to practice all the concepts and would like to hear your suggestions for improvement.

Here i try to give some context before diving into code:

Finally the code:

import matplotlib.pyplot as plt
import numpy as np
from pandas import DataFrame

# The distribution factory
def make_distribution(function,*pars):
 def distribution():
 return function(*pars)
 return distribution

def make_data(periods=52, 
 initial_inventory = 10, 
 demand_dist = make_distribution(np.random.normal,2,1),
 lead_time_dist = make_distribution(np.random.triangular,1,2,3),
 policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}):
 """ Return a Pandas dataFrame that contains the details of the inventory simulation.

 Keyword arguments:
 periods -- numbers of periods of the simulation (default 52 weeks)
 initial_inventory -- initial inventory for the simulation
 demand_dist -- distribution of the demand (default triangular min=1, mode=2, max=3) 
 lead_time_dist -- distribution of the lead time (default triangular min=1, mode=2, max=3)
 policy -- dict that contains the policy specs (default = {'method':'Qs', 'arguments': {'Q':3,'s':5}})
 """

 # Create zero-filled Dataframe
 period_lst = np.arange(periods) # index
 header = ['initial_inv_pos', 'initial_net_inv', 'demand', 'final_inv_pos', 
 'final_net_inv', 'lost_sales', 'avg_inv', 'order', 'lead_time'] # columns
 df = DataFrame(index = period_lst, columns = header).fillna(0)

 # Create a list that will store each period order
 order_list = [Order(quantity=0, lead_time=0, sent=False) for x in range(periods)] 

 # Fill DataFrame
 for period in period_lst:
 if period == 0:
 df['initial_inv_pos'][period] = initial_inventory
 df['initial_net_inv'][period] = initial_inventory
 else:
 df['initial_inv_pos'][period] = df['final_inv_pos'][period-1] + order_list[period - 1].quantity
 df['initial_net_inv'][period] = df['final_net_inv'][period-1] + pending_order(order_list, period)
 df['demand'][period] = int(demand_dist())
 df['final_inv_pos'][period] = df['initial_inv_pos'][period] - df['demand'][period]
 order_list[period].quantity, order_list[period].lead_time, order_list[period].sent = placeorder(df['final_inv_pos'][period], policy, lead_time_dist, period)
 df['final_net_inv'][period] = df['initial_net_inv'][period] - df['demand'][period]
 if df['final_net_inv'][period] < 0:
 df['lost_sales'][period] = abs(df['final_net_inv'][period])
 df['final_net_inv'][period] = 0
 else:
 df['lost_sales'][period] = 0
 df['avg_inv'][period] = 0
 df['order'][period] = order_list[period].quantity
 df['lead_time'][period] = order_list[period].lead_time 
 
 return df
 
 def placeorder(final_inv_pos, policy, lead_time_dist, period):
 """Place the order acording the inventory policy: 
 
 Keywords arguments:
 final_inv_pos -- final inventory position of period
 policy -- chosen policy Reorder point (Qs, Ss) or Periodic Review (RS, Rss)
 lead_time_dist -- distribution of lead time
 period -- actual period
 """
 
 lead_time = int(lead_time_dist())
 
 # Qs = if we hit the reorder point s, order Q units
 if policy['method'] == 'Qs' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['Q'], lead_time, True
 # Ss = if we hit the reorder point s, order S - final inventory pos
 elif policy['method'] == 'Ss' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RS = if we hit the review period and the reorder point S, order S - final inventory pos
 elif policy['method'] == 'RS' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['S']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RSs = if we hit the review period and the reorder point s, order S - final inventory pos
 elif policy['method'] == 'RSs' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # If the conditions arent satisfied, do not order
 else:
 df['initial_inv_pos'][period] = df['final_inv_pos'][period-1] + order_list[period - 1].quantity
 df['initial_net_inv'][period] = df['final_net_inv'][period-1] + pending_order(order_list, period)
 df['demand'][period] = int(demand_dist())
 df['final_inv_pos'][period] = df['initial_inv_pos'][period] - df['demand'][period]
 order_list[period].quantity, order_list[period].lead_time, order_list[period].sent = placeorder(df['final_inv_pos'][period], policy, lead_time_dist, period)
 df['final_net_inv'][period] = df['initial_net_inv'][period] - df['demand'][period]
 if df['final_net_inv'][period] < 0:
 df['lost_sales'][period] = abs(df['final_net_inv'][period])
 df['final_net_inv'][period] = 0
 else:
 df['lost_sales'][period] = 0
 df['avg_inv'][period] = 0
 df['order'][period] = order_list[period].quantity
 df['lead_time'][period] = order_list[period].lead_time 
 return df
def placeorder(final_inv_pos, policy, lead_time_dist, period):
 """Place the order acording the inventory policy: 
 Keywords arguments:
 final_inv_pos -- final inventory position of period
 policy -- chosen policy Reorder point (Qs, Ss) or Periodic Review (RS, Rss)
 lead_time_dist -- distribution of lead time
 period -- actual period
 """
 lead_time = int(lead_time_dist())
 # Qs = if we hit the reorder point s, order Q units
 if policy['method'] == 'Qs' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['Q'], lead_time, True
 # Ss = if we hit the reorder point s, order S - final inventory pos
 elif policy['method'] == 'Ss' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RS = if we hit the review period and the reorder point S, order S - final inventory pos
 elif policy['method'] == 'RS' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['S']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
  # RSs = if we hit the review period and the reorder point s, order S - final inventory pos
 elif policy['method'] == 'RSs' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, Falselead_time, True
 # If the conditions arent satisfied, do not order
 else:
 return 0, 0, False
def pending_order(order_list, period):
 """Return the order that arrives in actual period"""
 indices = [i for i, order in enumerate(order_list) if order.sent == True]
 sum = 0
 for i in indices:
 if period - (i + order_list[i].lead_time +1) == 0: 
 sum += order_list[i].quantity
 return sum
class +=Order(object):
 order_list[i] """Object that stores basic data of an order"""
 def __init__(self, quantity, lead_time, sent):
 self.quantity = quantity
 self.lead_time = lead_time
 returnself.sent sum= sent # True if the order is already sent
def make_plot(df, policy, period):
 #Plot
 plt.rcParams['figure.figsize'] = 15,4 #define the fig size
 fig = plt.figure()
 ax = fig.add_subplot(111)
 
 y1 = df['final_inv_pos']
 l1, = plt.plot(y1, 'k', linewidth=1.2, drawstyle='steps', label='Final Inv')
 if policy['method'] == 'Qs':
 title = 'Simulation Policy = (Q: {Q}, s: {s})'.format(**policy['arguments'])
 y2 = policy['arguments']['s']*np.ones(period)
 l2, = plt.plot(y2, 'r:', label='Reorder point')
 elif policy['method'] == 'Ss':
 #TODO
 pass
 
 class Order(object):
 """Object that stores basic data of an order"""
 def __init__(self, quantity, lead_time, sent):
 self.quantity = quantity
 self.lead_timet = lead_time
  selfax.sent = sent # True if the order is already sentset_title(title)
 
 def make_plotax.tick_params(dfaxis='both', policywhich='major', periodlabelsize=8):
 #Plot
 plt.rcParams['figurexticks(np.figsize'] = 15,4 #define the fig sizearange(period))
 fig = plt.figureylim(bottom=0) ax = figplt.add_subplotlegend(111loc='best', prop={'size':10})
 plt.xlabel("Periods")
 y1 =plt.ylabel("Inventory df['final_inv_pos']Level")
 l1, = plt.plotshow(y1, 'k', linewidth=1.2, drawstyle='steps', label='Final Inv')
 
 if policy['method'] ==
def 'Qs'simulate():
 #parameters of simulation
 title =Qs_policy 'Simulation Policy = (Q: {Q}'method':'Qs', s'arguments': {s})'.format(**policy['arguments'])
 y2 = policy['arguments']['s']*np.ones(period)
 l2, = plt.plot(y2, 'r'Q':'3, label='Reorder point')
 elif policy['method'] == 'Ss''s':
 #TODO
 pass
 5}}
 tdemand_dist = ax.set_titlemake_distribution(title)
 
 axnp.tick_params(axis='both'random.normal, which='major'3, labelsize=81)
 lead_time_dist = plt.xticksmake_distribution(np.arange(period))
 plt.ylim(bottom=0) 
 pltrandom.legend(loc='best'triangular, prop={'size':10})
 plt.xlabel("Periods")
 plt.ylabel("Inventory Level")
 plt.show(1,4,5)
 period = 52
 
 def simulate():
 #parameters of simulation
 Qs_policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}
 demand_dist = make_distribution(np.random.normal,3,1)
 lead_time_dist = make_distribution(np.random.triangular,1,4,5)
 period = 52
 
 df = make_data(period,10,demand_dist,lead_time_dist,Qs_policy)
 #df.to_csv("out.csv")
 
 make_plot(df, Qs_policy, period)
 
 make_plot(df, Qs_policy, period)
if __name__ == '__main__':
 simulate()

Python Code Review, Inventory Simulation using Pandas Dataframe

I've been learning python like for a year and started learning a little about pandas DataFrame. I made this little program to practice all the concepts and would like to hear your suggestions for improvement.

Here i try to give some context before diving into code:

Finally the code:

import matplotlib.pyplot as plt
import numpy as np
from pandas import DataFrame

# The distribution factory
def make_distribution(function,*pars):
 def distribution():
 return function(*pars)
 return distribution

def make_data(periods=52, 
 initial_inventory = 10, 
 demand_dist = make_distribution(np.random.normal,2,1),
 lead_time_dist = make_distribution(np.random.triangular,1,2,3),
 policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}):
 """ Return a Pandas dataFrame that contains the details of the inventory simulation.

 Keyword arguments:
 periods -- numbers of periods of the simulation (default 52 weeks)
 initial_inventory -- initial inventory for the simulation
 demand_dist -- distribution of the demand (default triangular min=1, mode=2, max=3) 
 lead_time_dist -- distribution of the lead time (default triangular min=1, mode=2, max=3)
 policy -- dict that contains the policy specs (default = {'method':'Qs', 'arguments': {'Q':3,'s':5}})
 """

 # Create zero-filled Dataframe
 period_lst = np.arange(periods) # index
 header = ['initial_inv_pos', 'initial_net_inv', 'demand', 'final_inv_pos', 
 'final_net_inv', 'lost_sales', 'avg_inv', 'order', 'lead_time'] # columns
 df = DataFrame(index = period_lst, columns = header).fillna(0)

 # Create a list that will store each period order
 order_list = [Order(quantity=0, lead_time=0, sent=False) for x in range(periods)] 

 # Fill DataFrame
 for period in period_lst:
 if period == 0:
 df['initial_inv_pos'][period] = initial_inventory
 df['initial_net_inv'][period] = initial_inventory
 else:
 df['initial_inv_pos'][period] = df['final_inv_pos'][period-1] + order_list[period - 1].quantity
 df['initial_net_inv'][period] = df['final_net_inv'][period-1] + pending_order(order_list, period)
 df['demand'][period] = int(demand_dist())
 df['final_inv_pos'][period] = df['initial_inv_pos'][period] - df['demand'][period]
 order_list[period].quantity, order_list[period].lead_time, order_list[period].sent = placeorder(df['final_inv_pos'][period], policy, lead_time_dist, period)
 df['final_net_inv'][period] = df['initial_net_inv'][period] - df['demand'][period]
 if df['final_net_inv'][period] < 0:
 df['lost_sales'][period] = abs(df['final_net_inv'][period])
 df['final_net_inv'][period] = 0
 else:
 df['lost_sales'][period] = 0
 df['avg_inv'][period] = 0
 df['order'][period] = order_list[period].quantity
 df['lead_time'][period] = order_list[period].lead_time 
 
 return df
 
 def placeorder(final_inv_pos, policy, lead_time_dist, period):
 """Place the order acording the inventory policy: 
 
 Keywords arguments:
 final_inv_pos -- final inventory position of period
 policy -- chosen policy Reorder point (Qs, Ss) or Periodic Review (RS, Rss)
 lead_time_dist -- distribution of lead time
 period -- actual period
 """
 
 lead_time = int(lead_time_dist())
 
 # Qs = if we hit the reorder point s, order Q units
 if policy['method'] == 'Qs' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['Q'], lead_time, True
 # Ss = if we hit the reorder point s, order S - final inventory pos
 elif policy['method'] == 'Ss' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RS = if we hit the review period and the reorder point S, order S - final inventory pos
 elif policy['method'] == 'RS' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['S']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RSs = if we hit the review period and the reorder point s, order S - final inventory pos
 elif policy['method'] == 'RSs' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # If the conditions arent satisfied, do not order
 else:
 return 0, 0, False
 
 def pending_order(order_list, period):
 """Return the order that arrives in actual period"""
 indices = [i for i, order in enumerate(order_list) if order.sent == True]
 sum = 0
 for i in indices:
 if period - (i + order_list[i].lead_time +1) == 0: 
 sum += order_list[i].quantity
 
 return sum
 
 
 class Order(object):
 """Object that stores basic data of an order"""
 def __init__(self, quantity, lead_time, sent):
 self.quantity = quantity
 self.lead_time = lead_time
  self.sent = sent # True if the order is already sent
 
 def make_plot(df, policy, period):
 #Plot
 plt.rcParams['figure.figsize'] = 15,4 #define the fig size
 fig = plt.figure() ax = fig.add_subplot(111)
 
 y1 = df['final_inv_pos']
 l1, = plt.plot(y1, 'k', linewidth=1.2, drawstyle='steps', label='Final Inv')
 
 if policy['method'] == 'Qs':
 title = 'Simulation Policy = (Q: {Q}, s: {s})'.format(**policy['arguments'])
 y2 = policy['arguments']['s']*np.ones(period)
 l2, = plt.plot(y2, 'r:', label='Reorder point')
 elif policy['method'] == 'Ss':
 #TODO
 pass
 
 t = ax.set_title(title)
 
 ax.tick_params(axis='both', which='major', labelsize=8)
 plt.xticks(np.arange(period))
 plt.ylim(bottom=0) 
 plt.legend(loc='best', prop={'size':10})
 plt.xlabel("Periods")
 plt.ylabel("Inventory Level")
 plt.show()
 
 
 def simulate():
 #parameters of simulation
 Qs_policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}
 demand_dist = make_distribution(np.random.normal,3,1)
 lead_time_dist = make_distribution(np.random.triangular,1,4,5)
 period = 52
 
 df = make_data(period,10,demand_dist,lead_time_dist,Qs_policy)
 #df.to_csv("out.csv")
 
 make_plot(df, Qs_policy, period)
 
 if __name__ == '__main__':
 simulate()

Inventory simulation using Pandas DataFrame

I've been learning Python like for a year and started learning a little about pandas DataFrame. I made this little program to practice all the concepts and would like to hear your suggestions for improvement.

import matplotlib.pyplot as plt
import numpy as np
from pandas import DataFrame
# The distribution factory
def make_distribution(function,*pars):
 def distribution():
 return function(*pars)
 return distribution
def make_data(periods=52, 
 initial_inventory = 10, 
 demand_dist = make_distribution(np.random.normal,2,1),
 lead_time_dist = make_distribution(np.random.triangular,1,2,3),
 policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}):
 """ Return a Pandas dataFrame that contains the details of the inventory simulation.
 Keyword arguments:
 periods -- numbers of periods of the simulation (default 52 weeks)
 initial_inventory -- initial inventory for the simulation
 demand_dist -- distribution of the demand (default triangular min=1, mode=2, max=3) 
 lead_time_dist -- distribution of the lead time (default triangular min=1, mode=2, max=3)
 policy -- dict that contains the policy specs (default = {'method':'Qs', 'arguments': {'Q':3,'s':5}})
 """
 # Create zero-filled Dataframe
 period_lst = np.arange(periods) # index
 header = ['initial_inv_pos', 'initial_net_inv', 'demand', 'final_inv_pos', 
 'final_net_inv', 'lost_sales', 'avg_inv', 'order', 'lead_time'] # columns
 df = DataFrame(index = period_lst, columns = header).fillna(0)
 # Create a list that will store each period order
 order_list = [Order(quantity=0, lead_time=0, sent=False) for x in range(periods)] 
 # Fill DataFrame
 for period in period_lst:
 if period == 0:
 df['initial_inv_pos'][period] = initial_inventory
 df['initial_net_inv'][period] = initial_inventory
 else:
 df['initial_inv_pos'][period] = df['final_inv_pos'][period-1] + order_list[period - 1].quantity
 df['initial_net_inv'][period] = df['final_net_inv'][period-1] + pending_order(order_list, period)
 df['demand'][period] = int(demand_dist())
 df['final_inv_pos'][period] = df['initial_inv_pos'][period] - df['demand'][period]
 order_list[period].quantity, order_list[period].lead_time, order_list[period].sent = placeorder(df['final_inv_pos'][period], policy, lead_time_dist, period)
 df['final_net_inv'][period] = df['initial_net_inv'][period] - df['demand'][period]
 if df['final_net_inv'][period] < 0:
 df['lost_sales'][period] = abs(df['final_net_inv'][period])
 df['final_net_inv'][period] = 0
 else:
 df['lost_sales'][period] = 0
 df['avg_inv'][period] = 0
 df['order'][period] = order_list[period].quantity
 df['lead_time'][period] = order_list[period].lead_time 
 return df
def placeorder(final_inv_pos, policy, lead_time_dist, period):
 """Place the order acording the inventory policy: 
 Keywords arguments:
 final_inv_pos -- final inventory position of period
 policy -- chosen policy Reorder point (Qs, Ss) or Periodic Review (RS, Rss)
 lead_time_dist -- distribution of lead time
 period -- actual period
 """
 lead_time = int(lead_time_dist())
 # Qs = if we hit the reorder point s, order Q units
 if policy['method'] == 'Qs' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['Q'], lead_time, True
 # Ss = if we hit the reorder point s, order S - final inventory pos
 elif policy['method'] == 'Ss' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RS = if we hit the review period and the reorder point S, order S - final inventory pos
 elif policy['method'] == 'RS' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['S']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
  # RSs = if we hit the review period and the reorder point s, order S - final inventory pos
 elif policy['method'] == 'RSs' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # If the conditions arent satisfied, do not order
 else:
 return 0, 0, False
def pending_order(order_list, period):
 """Return the order that arrives in actual period"""
 indices = [i for i, order in enumerate(order_list) if order.sent == True]
 sum = 0
 for i in indices:
 if period - (i + order_list[i].lead_time +1) == 0: 
 sum += order_list[i].quantity
 return sum
class Order(object):
  """Object that stores basic data of an order"""
 def __init__(self, quantity, lead_time, sent):
 self.quantity = quantity
 self.lead_time = lead_time
 self.sent = sent # True if the order is already sent
def make_plot(df, policy, period):
 #Plot
 plt.rcParams['figure.figsize'] = 15,4 #define the fig size
 fig = plt.figure()
 ax = fig.add_subplot(111)
 
 y1 = df['final_inv_pos']
 l1, = plt.plot(y1, 'k', linewidth=1.2, drawstyle='steps', label='Final Inv')
 if policy['method'] == 'Qs':
 title = 'Simulation Policy = (Q: {Q}, s: {s})'.format(**policy['arguments'])
 y2 = policy['arguments']['s']*np.ones(period)
 l2, = plt.plot(y2, 'r:', label='Reorder point')
 elif policy['method'] == 'Ss':
 #TODO
 pass
 
 t = ax.set_title(title)
 
 ax.tick_params(axis='both', which='major', labelsize=8)
 plt.xticks(np.arange(period))
 plt.ylim(bottom=0) plt.legend(loc='best', prop={'size':10})
 plt.xlabel("Periods")
 plt.ylabel("Inventory Level")
 plt.show()
 

def simulate():
 #parameters of simulation
 Qs_policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}
 demand_dist = make_distribution(np.random.normal,3,1)
 lead_time_dist = make_distribution(np.random.triangular,1,4,5)
 period = 52
 
 df = make_data(period,10,demand_dist,lead_time_dist,Qs_policy)
 #df.to_csv("out.csv")
 
 make_plot(df, Qs_policy, period)
if __name__ == '__main__':
 simulate()
added 2233 characters in body
Source Link

inventory position = on hand inventory + inventory on order – committed / backorders

inventory position = on hand inventory + inventory on order – committed / backorders

inventory position = on hand inventory + inventory on order

added 2233 characters in body
Source Link

Here i try to give some context before diving into code:

GOAL

This program should plot how the inventory levels behave according some initials conditions: number of periods, demand distribution, lead time distribution and the chosen control policy (see types of policy).

DEFINITIONS AND FORMULAS

inventory position = on hand inventory + inventory on order – committed / backorders

lead time = the amount of time between the placing of an order and the receipt of the goods ordered.

NOTATION

s reorder point, Q order quantity, R review period, S order-up-to level

TYPES OF POLICY

We use four different base policy types:

  1. (s, Q)-policy. Whenever the inventory position reaches the reorder point s or drops below this point, an order of size Q is triggered.
  2. (s, S)-policy. Under this policy, the size of the order – triggered whenever the inventory position reaches or drops below the reorder point s – equals the difference between the order-up-to level S and the current inventory position.
  3. (R, S)-policy. The review period R indicates the length of the interval in-between two reviews. At these points of time, the inventory position is observed and the difference between the inventory position and the order-up-to level S is ordered.
  4. (R, s, S)-policy. Every R intervals the inventory position is checked. An order is triggered only if the inventory position has reached or dropped below the reorder point s. Then the order size equals the difference between the order-up-to level S and the current inventory position.

Finally the code:

import matplotlib.pyplot as plt
import numpy as np
from pandas import DataFrame

# The distribution factory
def make_distribution(function,*pars):
 def distribution():
 return function(*pars)
 return distribution

def make_data(periods=52, 
 initial_inventory = 10, 
 demand_dist = make_distribution(np.random.normal,2,1),
 lead_time_dist = make_distribution(np.random.triangular,1,2,3),
 policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}):
 """ Return a Pandas dataFrame that contains the details of the inventory simulation.

 Keyword arguments:
 periods -- numbers of periods of the simulation (default 52 weeks)
 initial_inventory -- initial inventory for the simulation
 demand_dist -- distribution of the demand (default triangular min=1, mode=2, max=3) 
 lead_time_dist -- distribution of the lead time (default triangular min=1, mode=2, max=3)
 policy -- dict that contains the policy specs (default = {'method':'Qs', 'arguments': {'Q':3,'s':5}})
 """

 # Create zero-filled Dataframe
 period_lst = np.arange(periods) # index
 header = ['initial_inv_pos', 'initial_net_inv', 'demand', 'final_inv_pos', 
 'final_net_inv', 'lost_sales', 'avg_inv', 'order', 'lead_time'] # columns
 df = DataFrame(index = period_lst, columns = header).fillna(0)

 # Create a list that will store each period order
 order_list = [Order(quantity=0, lead_time=0, sent=False) for x in range(periods)] 

 # Fill DataFrame
 for period in period_lst:
 if period == 0:
 df['initial_inv_pos'][period] = initial_inventory
 df['initial_net_inv'][period] = initial_inventory
 else:
 df['initial_inv_pos'][period] = df['final_inv_pos'][period-1] + order_list[period - 1].quantity
 df['initial_net_inv'][period] = df['final_net_inv'][period-1] + pending_order(order_list, period)
 df['demand'][period] = int(demand_dist())
 df['final_inv_pos'][period] = df['initial_inv_pos'][period] - df['demand'][period]
 order_list[period].quantity, order_list[period].lead_time, order_list[period].sent = placeorder(df['final_inv_pos'][period], policy, lead_time_dist, period)
 df['final_net_inv'][period] = df['initial_net_inv'][period] - df['demand'][period]
 if df['final_net_inv'][period] < 0:
 df['lost_sales'][period] = abs(df['final_net_inv'][period])
 df['final_net_inv'][period] = 0
 else:
 df['lost_sales'][period] = 0
 df['avg_inv'][period] = 0
 df['order'][period] = order_list[period].quantity
 df['lead_time'][period] = order_list[period].lead_time 
 
 return df
 
 def placeorder(final_inv_pos, policy, lead_time_dist, period):
 """Place the order acording the inventory policy: 
 
 Keywords arguments:
 final_inv_pos -- final inventory position of period
 policy -- chosen policy Reorder point (Qs, Ss) or Periodic Review (RS, Rss)
 lead_time_dist -- distribution of lead time
 period -- actual period
 """
 
 lead_time = int(lead_time_dist())
 
 # Qs = if we hit the reorder point s, order Q units
 if policy['method'] == 'Qs' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['Q'], lead_time, True
 # Ss = if we hit the reorder point s, order S - final inventory pos
 elif policy['method'] == 'Ss' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RS = if we hit the review period and the reorder point S, order S - final inventory pos
 elif policy['method'] == 'RS' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['S']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RSs = if we hit the review period and the reorder point s, order S - final inventory pos
 elif policy['method'] == 'RSs' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # If the conditions arent satisfied, do not order
 else:
 df['lost_sales'][period] = 0
 df['avg_inv'][period] = 0
 df['order'][period] = order_list[period].quantity
 df['lead_time'][period] = order_list[period].lead_time 
 return df
def placeorder(final_inv_pos, policy, lead_time_dist, period):
 """Place the order acording the inventory policy: 
 Keywords arguments:
 final_inv_pos -- final inventory position of period
 policy -- chosen policy Reorder point (Qs, Ss) or Periodic Review (RS, Rss)
 lead_time_dist -- distribution of lead time
 period -- actual period
 """
 lead_time = int(lead_time_dist())
 # Qs = if we hit the reorder point s, order Q units
 if policy['method'] == 'Qs' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['Q'], lead_time, True
 # Ss = if we hit the reorder point s, order S - final inventory pos
 elif policy['method'] == 'Ss' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RS = if we hit the review period and the reorder point S, order S - final inventory pos
 elif policy['method'] == 'RS' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['S']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RSs = if we hit the review period and the reorder point s, order S - final inventory pos
 elif policy['method'] == 'RSs' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # If the conditions arent satisfied, do not order
 else:
 return 0, 0, False
def pending_order(order_list, period):
 """Return the order that arrives in actual period"""
 indices = [i for i, order in enumerate(order_list) if order.sent == True]
 sum = 0
 for i in indices:
 if period - (i + order_list[i].lead_time +1) == 0: 
 sum += order_list[i].quantity
 return sum
class Order(object):
 """Object that stores basic data of an order"""
 def __init__(self, quantity, lead_time, sent):
 self.quantity = quantity
 self.lead_time = lead_time
 self.sent = sent # True if the order is already sent
def make_plot(df, policy, period):
 #Plot
 plt.rcParams['figure.figsize'] = 15,4 #define the fig size
 fig = plt.figure()
 ax = fig.add_subplot(111)
 
 y1def =pending_order(order_list, df['final_inv_pos']period):
 l1, = plt.plot(y1, 'k', linewidth=1.2,"""Return drawstyle='steps',the label='Finalorder Inv')
that arrives in actual period"""
 indices = [i for i, order in enumerate(order_list) if policy['method']order.sent == 'Qs':True]
 titlesum = 'Simulation0
 Policy = (Q: {Q}, s: {s})'.format(**policy['arguments'])
 for i in indices:
 y2 = policy['arguments']['s']*np.ones(period)
 if l2,period =- plt.plot(y2,i 'r:',+ label='Reorderorder_list[i].lead_time point'+1) == 0: 
 elif policy['method'] == 'Ss':
 pass sum += order_list[i].quantity
 
 t = ax.set_title(title) return sum
 
 ax.tick_params(axis='both', which='major', labelsize=8)
 plt.xticks(np.arange(period))
 plt.ylim(bottom=0) 
 plt.legend(loc='best', prop={'size':10})
 plt.xlabel("Periods")
 plt.ylabel("Inventory Level")
 plt.show()
 

def simulate class Order(object):
 #parameters """Object that stores basic data of simulationan order"""
 Qs_policy = {'method':'Qs'def __init__(self, 'arguments':quantity, {'Q':3lead_time,'s' sent):5}}
 demand_dist = make_distribution(np.random self.normal,3,1)quantity = quantity
 lead_time_dist = make_distribution(np.random self.triangular,1,4,5)lead_time = lead_time
 period self.sent = 52sent # True if the order is already sent
 
 df =def make_datamake_plot(period,10df,demand_dist policy,lead_time_dist period):
 #Plot
 plt.rcParams['figure.figsize'] = 15,Qs_policy4 #define the fig size
 fig = plt.figure()
 #df ax = fig.to_csvadd_subplot("out111)
 
 y1 = df['final_inv_pos']
 l1, = plt.csv"plot(y1, 'k', linewidth=1.2, drawstyle='steps', label='Final Inv')
 
 if policy['method'] == 'Qs':
 title = 'Simulation Policy = (Q: {Q}, s: {s})'.format(**policy['arguments'])
 y2 = policy['arguments']['s']*np.ones(period)
 l2, = plt.plot(y2, 'r:', label='Reorder point')
 elif policy['method'] == 'Ss':
 #TODO
 pass
 
 t = ax.set_title(title)
 
 ax.tick_params(axis='both', which='major', labelsize=8)
 plt.xticks(np.arange(period))
 plt.ylim(bottom=0) 
 plt.legend(loc='best', prop={'size':10})
 plt.xlabel("Periods")
 plt.ylabel("Inventory Level")
 plt.show()
 
 
 def simulate():
 #parameters of simulation
 Qs_policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}
 demand_dist = make_distribution(np.random.normal,3,1)
 lead_time_dist = make_distribution(np.random.triangular,1,4,5)
 period = 52
 
 df = make_data(period,10,demand_dist,lead_time_dist,Qs_policy)
 #df.to_csv("out.csv")
 
 make_plot(df, Qs_policy, period)

if __name__ == '__main__':
 simulate()
import matplotlib.pyplot as plt
import numpy as np
from pandas import DataFrame
# The distribution factory
def make_distribution(function,*pars):
 def distribution():
 return function(*pars)
 return distribution
def make_data(periods=52, 
 initial_inventory = 10, 
 demand_dist = make_distribution(np.random.normal,2,1),
 lead_time_dist = make_distribution(np.random.triangular,1,2,3),
 policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}):
 """ Return a Pandas dataFrame that contains the details of the inventory simulation.
 Keyword arguments:
 periods -- numbers of periods of the simulation (default 52 weeks)
 initial_inventory -- initial inventory for the simulation
 demand_dist -- distribution of the demand (default triangular min=1, mode=2, max=3) 
 lead_time_dist -- distribution of the lead time (default triangular min=1, mode=2, max=3)
 policy -- dict that contains the policy specs (default = {'method':'Qs', 'arguments': {'Q':3,'s':5}})
 """
 # Create zero-filled Dataframe
 period_lst = np.arange(periods) # index
 header = ['initial_inv_pos', 'initial_net_inv', 'demand', 'final_inv_pos', 
 'final_net_inv', 'lost_sales', 'avg_inv', 'order', 'lead_time'] # columns
 df = DataFrame(index = period_lst, columns = header).fillna(0)
 # Create a list that will store each period order
 order_list = [Order(quantity=0, lead_time=0, sent=False) for x in range(periods)] 
 # Fill DataFrame
 for period in period_lst:
 if period == 0:
 df['initial_inv_pos'][period] = initial_inventory
 df['initial_net_inv'][period] = initial_inventory
 else:
 df['initial_inv_pos'][period] = df['final_inv_pos'][period-1] + order_list[period - 1].quantity
 df['initial_net_inv'][period] = df['final_net_inv'][period-1] + pending_order(order_list, period)
 df['demand'][period] = int(demand_dist())
 df['final_inv_pos'][period] = df['initial_inv_pos'][period] - df['demand'][period]
 order_list[period].quantity, order_list[period].lead_time, order_list[period].sent = placeorder(df['final_inv_pos'][period], policy, lead_time_dist, period)
 df['final_net_inv'][period] = df['initial_net_inv'][period] - df['demand'][period]
 if df['final_net_inv'][period] < 0:
 df['lost_sales'][period] = abs(df['final_net_inv'][period])
 df['final_net_inv'][period] = 0
 else:
 df['lost_sales'][period] = 0
 df['avg_inv'][period] = 0
 df['order'][period] = order_list[period].quantity
 df['lead_time'][period] = order_list[period].lead_time 
 return df
def placeorder(final_inv_pos, policy, lead_time_dist, period):
 """Place the order acording the inventory policy: 
 Keywords arguments:
 final_inv_pos -- final inventory position of period
 policy -- chosen policy Reorder point (Qs, Ss) or Periodic Review (RS, Rss)
 lead_time_dist -- distribution of lead time
 period -- actual period
 """
 lead_time = int(lead_time_dist())
 # Qs = if we hit the reorder point s, order Q units
 if policy['method'] == 'Qs' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['Q'], lead_time, True
 # Ss = if we hit the reorder point s, order S - final inventory pos
 elif policy['method'] == 'Ss' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RS = if we hit the review period and the reorder point S, order S - final inventory pos
 elif policy['method'] == 'RS' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['S']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RSs = if we hit the review period and the reorder point s, order S - final inventory pos
 elif policy['method'] == 'RSs' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # If the conditions arent satisfied, do not order
 else:
 return 0, 0, False
def pending_order(order_list, period):
 """Return the order that arrives in actual period"""
 indices = [i for i, order in enumerate(order_list) if order.sent == True]
 sum = 0
 for i in indices:
 if period - (i + order_list[i].lead_time +1) == 0: 
 sum += order_list[i].quantity
 return sum
class Order(object):
 """Object that stores basic data of an order"""
 def __init__(self, quantity, lead_time, sent):
 self.quantity = quantity
 self.lead_time = lead_time
 self.sent = sent # True if the order is already sent
def make_plot(df, policy, period):
 #Plot
 plt.rcParams['figure.figsize'] = 15,4 #define the fig size
 fig = plt.figure()
 ax = fig.add_subplot(111)
 
 y1 = df['final_inv_pos']
 l1, = plt.plot(y1, 'k', linewidth=1.2, drawstyle='steps', label='Final Inv')

 if policy['method'] == 'Qs':
 title = 'Simulation Policy = (Q: {Q}, s: {s})'.format(**policy['arguments'])
 y2 = policy['arguments']['s']*np.ones(period)
 l2, = plt.plot(y2, 'r:', label='Reorder point')
 elif policy['method'] == 'Ss':
 pass
 
 t = ax.set_title(title)
 
 ax.tick_params(axis='both', which='major', labelsize=8)
 plt.xticks(np.arange(period))
 plt.ylim(bottom=0) 
 plt.legend(loc='best', prop={'size':10})
 plt.xlabel("Periods")
 plt.ylabel("Inventory Level")
 plt.show()
 

def simulate():
 #parameters of simulation
 Qs_policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}
 demand_dist = make_distribution(np.random.normal,3,1)
 lead_time_dist = make_distribution(np.random.triangular,1,4,5)
 period = 52
 
 df = make_data(period,10,demand_dist,lead_time_dist,Qs_policy)
 #df.to_csv("out.csv")
 
 make_plot(df, Qs_policy, period)
if __name__ == '__main__':
 simulate()

Here i try to give some context before diving into code:

GOAL

This program should plot how the inventory levels behave according some initials conditions: number of periods, demand distribution, lead time distribution and the chosen control policy (see types of policy).

DEFINITIONS AND FORMULAS

inventory position = on hand inventory + inventory on order – committed / backorders

lead time = the amount of time between the placing of an order and the receipt of the goods ordered.

NOTATION

s reorder point, Q order quantity, R review period, S order-up-to level

TYPES OF POLICY

We use four different base policy types:

  1. (s, Q)-policy. Whenever the inventory position reaches the reorder point s or drops below this point, an order of size Q is triggered.
  2. (s, S)-policy. Under this policy, the size of the order – triggered whenever the inventory position reaches or drops below the reorder point s – equals the difference between the order-up-to level S and the current inventory position.
  3. (R, S)-policy. The review period R indicates the length of the interval in-between two reviews. At these points of time, the inventory position is observed and the difference between the inventory position and the order-up-to level S is ordered.
  4. (R, s, S)-policy. Every R intervals the inventory position is checked. An order is triggered only if the inventory position has reached or dropped below the reorder point s. Then the order size equals the difference between the order-up-to level S and the current inventory position.

Finally the code:

import matplotlib.pyplot as plt
import numpy as np
from pandas import DataFrame

# The distribution factory
def make_distribution(function,*pars):
 def distribution():
 return function(*pars)
 return distribution

def make_data(periods=52, 
 initial_inventory = 10, 
 demand_dist = make_distribution(np.random.normal,2,1),
 lead_time_dist = make_distribution(np.random.triangular,1,2,3),
 policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}):
 """ Return a Pandas dataFrame that contains the details of the inventory simulation.

 Keyword arguments:
 periods -- numbers of periods of the simulation (default 52 weeks)
 initial_inventory -- initial inventory for the simulation
 demand_dist -- distribution of the demand (default triangular min=1, mode=2, max=3) 
 lead_time_dist -- distribution of the lead time (default triangular min=1, mode=2, max=3)
 policy -- dict that contains the policy specs (default = {'method':'Qs', 'arguments': {'Q':3,'s':5}})
 """

 # Create zero-filled Dataframe
 period_lst = np.arange(periods) # index
 header = ['initial_inv_pos', 'initial_net_inv', 'demand', 'final_inv_pos', 
 'final_net_inv', 'lost_sales', 'avg_inv', 'order', 'lead_time'] # columns
 df = DataFrame(index = period_lst, columns = header).fillna(0)

 # Create a list that will store each period order
 order_list = [Order(quantity=0, lead_time=0, sent=False) for x in range(periods)] 

 # Fill DataFrame
 for period in period_lst:
 if period == 0:
 df['initial_inv_pos'][period] = initial_inventory
 df['initial_net_inv'][period] = initial_inventory
 else:
 df['initial_inv_pos'][period] = df['final_inv_pos'][period-1] + order_list[period - 1].quantity
 df['initial_net_inv'][period] = df['final_net_inv'][period-1] + pending_order(order_list, period)
 df['demand'][period] = int(demand_dist())
 df['final_inv_pos'][period] = df['initial_inv_pos'][period] - df['demand'][period]
 order_list[period].quantity, order_list[period].lead_time, order_list[period].sent = placeorder(df['final_inv_pos'][period], policy, lead_time_dist, period)
 df['final_net_inv'][period] = df['initial_net_inv'][period] - df['demand'][period]
 if df['final_net_inv'][period] < 0:
 df['lost_sales'][period] = abs(df['final_net_inv'][period])
 df['final_net_inv'][period] = 0
 else:
 df['lost_sales'][period] = 0
 df['avg_inv'][period] = 0
 df['order'][period] = order_list[period].quantity
 df['lead_time'][period] = order_list[period].lead_time 
 
 return df
 
 def placeorder(final_inv_pos, policy, lead_time_dist, period):
 """Place the order acording the inventory policy: 
 
 Keywords arguments:
 final_inv_pos -- final inventory position of period
 policy -- chosen policy Reorder point (Qs, Ss) or Periodic Review (RS, Rss)
 lead_time_dist -- distribution of lead time
 period -- actual period
 """
 
 lead_time = int(lead_time_dist())
 
 # Qs = if we hit the reorder point s, order Q units
 if policy['method'] == 'Qs' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['Q'], lead_time, True
 # Ss = if we hit the reorder point s, order S - final inventory pos
 elif policy['method'] == 'Ss' and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RS = if we hit the review period and the reorder point S, order S - final inventory pos
 elif policy['method'] == 'RS' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['S']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # RSs = if we hit the review period and the reorder point s, order S - final inventory pos
 elif policy['method'] == 'RSs' and \
 period%policy['arguments']['R'] == 0 and \
 final_inv_pos <= policy['arguments']['s']:
 return policy['arguments']['S'] - final_inv_pos, lead_time, True
 # If the conditions arent satisfied, do not order
 else:
 return 0, 0, False
 
 def pending_order(order_list, period):
 """Return the order that arrives in actual period"""
 indices = [i for i, order in enumerate(order_list) if order.sent == True]
 sum = 0
 for i in indices:
 if period - (i + order_list[i].lead_time +1) == 0: 
  sum += order_list[i].quantity
 
  return sum
 
 
  class Order(object):
  """Object that stores basic data of an order"""
 def __init__(self, quantity, lead_time, sent):
  self.quantity = quantity
  self.lead_time = lead_time
 self.sent = sent # True if the order is already sent
 
 def make_plot(df, policy, period):
 #Plot
 plt.rcParams['figure.figsize'] = 15,4 #define the fig size
 fig = plt.figure()
  ax = fig.add_subplot(111)
 
 y1 = df['final_inv_pos']
 l1, = plt.plot(y1, 'k', linewidth=1.2, drawstyle='steps', label='Final Inv')
 
 if policy['method'] == 'Qs':
 title = 'Simulation Policy = (Q: {Q}, s: {s})'.format(**policy['arguments'])
 y2 = policy['arguments']['s']*np.ones(period)
 l2, = plt.plot(y2, 'r:', label='Reorder point')
 elif policy['method'] == 'Ss':
 #TODO
 pass
 
 t = ax.set_title(title)
 
 ax.tick_params(axis='both', which='major', labelsize=8)
 plt.xticks(np.arange(period))
 plt.ylim(bottom=0) 
 plt.legend(loc='best', prop={'size':10})
 plt.xlabel("Periods")
 plt.ylabel("Inventory Level")
 plt.show()
 
 
 def simulate():
 #parameters of simulation
 Qs_policy = {'method':'Qs', 'arguments': {'Q':3,'s':5}}
 demand_dist = make_distribution(np.random.normal,3,1)
 lead_time_dist = make_distribution(np.random.triangular,1,4,5)
 period = 52
 
 df = make_data(period,10,demand_dist,lead_time_dist,Qs_policy)
 #df.to_csv("out.csv")
 
 make_plot(df, Qs_policy, period)

if __name__ == '__main__':
 simulate()
added 1649 characters in body
Source Link
Loading
Source Link
Loading
lang-py

AltStyle によって変換されたページ (->オリジナル) /