Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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:

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

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()
share|improve this question
 
Hi - Please provide a high level OR/SCM description, to give a context of what you are trying to achieve. –  BHM Jul 7 at 20:16
 
I tried to give some context and change some of the code. I hope is a little more clear now. –  Jdash Jul 9 at 0:39

Know someone who can answer? Share a link to this question via email, Google+, Twitter, or Facebook.

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.