Jekyll2018-01-20T15:29:42+00:00http://supplybi.github.io/supplybi.github.io/supplybi.github.io/Supplychainpy Python library for supply chain analysisBlog for supplychainpyIASquare UX Consultation2018-01-16T00:00:00+00:002018-01-16T00:00:00+00:00http://supplybi.github.io/supplybi.github.io/supplybi.github.io/2018/01/16/IAsquare-Meeting<p>First of all, Happy New Year! It’s been a while.</p>
<p>I recently met with the London-based UX agency IAsquare. The team, Anthony, George and Denia are UX professionals who have advised and conducted user testing for some big names in technology. I was very grateful when they offered their time to assist me in improving the UX/UI of supplychainpy. <!--more --></p>
<p>I gave a presentation covering the following:</p>
<ul>
<li>Introduction
<ul>
<li>what is supplychainpy and what problems does it solve?</li>
</ul>
</li>
<li>Features</li>
<li>Demonstration
<ul>
<li>library API</li>
<li>reporting suite</li>
</ul>
</li>
<li>Roadmap</li>
</ul>
<h2 id="feedback">Feedback</h2>
<p>After some time spent interacting with the application and asking questions, the team had some feedback:</p>
<h3 id="the-colour-theme">The Colour Theme</h3>
<p>Arguably, the elephant in the room. I thought this would be a contentious topic and I expected to be questioned furiously about my choice of a solarised theme. I live in the terminal, and during the demonstration, my terminal’s solarised theme tipped everyone off that I am a fan.</p>
<p><img src="/supplybi.github.io/images/shortage.jpg" class="img-fluid img-responsive" /></p>
<p>The original motivation for using the solarised theme in the reporting suite was to provide an application that looked different to the typical analytics application. It was clear that the solarised colour palette would not be to everyone’s liking but it would be distinct and may break up the visual monotony of an analysts day. However, there are rules in the UX/UI world, and I wanted to make sure I wasn’t breaking too many.</p>
<p>To my surprise, Anthony commented that it is okay and that dark themes are the most difficult to get right. He could not point to a specific area in which I have made a horrible mistake. While not high praise, it also was not a scathing criticism, so I will take the win. Although, I do plan to provide the ability to switch between this theme and a lighter one. I should probably shut up now, because “a promise is a debt” as my mum used to tell me. <em>(As I write this I have just realised that my mum probably got this quote from somewhere, sure enough, Google has shown me that my childhood was a lie. It is a quote from Robert W. Service, The Cremation of Sam McGee)</em>.</p>
<h3 id="the-recommendation-generator-and-traffic-lights">The recommendation generator and Traffic Lights</h3>
<p>The recommendation generator for the SKU view was a big win for the Team.</p>
<p><img src="/supplybi.github.io/images/traffic_lights.jpg" class="img-fluid img-responsive" /></p>
<p>The natural language feedback offered as a summary to the analysis and charts presented on the screen made the:</p>
<blockquote>
<p>“… application communicate complex information in plain English. I can understand what to look for in the data and charts, based on the recommendations.”
— Anthony Ioannidis, <em>Managing Director, IASquare</em></p>
</blockquote>
<p>Anthony pointed out that it would be beneficial to move the inventory profile recommendation to dashboard view.</p>
<p>The traffic light system for the recommendation feed was also a hit. Although, the difficulty distinguishing between red and amber was raised. The suggestion was made to add traffic lights to the recommendation in the “SKU” view.</p>
<h3 id="tiling-and-shadowing-the-poor-mans-material-design">Tiling and Shadowing (the poor man’s Material Design)</h3>
<p>Apparently, while attempting to mimic the ‘Material Design’ aesthetic, I had gone retro (retro chic maybe?). I was told it would be better to remove the harsh shadowing effect on the tiles (the poor man’s Material Design) and go with a flat theme.</p>
<p>I may just employ the help of bootstrap to save me from myself and give me that sweet, sweet Material Design look. Failing that, I will accept defeat and go flat.</p>
<h3 id="hidden-sidebar-navigation">Hidden Sidebar Navigation</h3>
<p>To reveal the sidebar navigation, a user is required to click the ‘SUPPLYCHAINPY’ logo at the top of the dashboard; this is not clear without prior knowledge (possibly from a gif in the docs). I was told to change the logo to a hamburger button. The hamburger button is a universal UI element, and I should embrace it.</p>
<h3 id="updating-the-charts">Updating the Charts</h3>
<p>George and Denia pointed out that while the charts were helpful, the ability to drill down would enhance the UX. The graphs were initially created using the Flotr and d3.js charting library. I used d3.js at first because I was learning the library and did not know how difficult it was. Well, I soon discovered, and I opted to use Flotr to move faster. The DC.js library provides the drill down functionality, and I will be exploring this option.</p>
<h3 id="chatbot">Chatbot</h3>
<p>Anthony pointed out that if I was not planning on going the full distance to make the chatbot a more competent bot (ouch!), with the ability to understand a much more expansive lexicon, then I should consider changing the chatbot to a natural language query builder instead. The builder would provide all the benefits of using natural language with none of the ambiguity and frustration of a rudimentary chatbot.</p>
<h3 id="easy-download-configuration-and-deployment">Easy Download, Configuration and Deployment</h3>
<p>To give the presentation, I set up a few cloud instances courtesy of Digital Ocean. I gave a demonstration of the dashboard using the cloud servers so that everyone could test from their laptops; I also gave a demo of the client-side application. It was unanimous, downloading, configuring and initialising across all platforms needs to be much more comfortable.</p>
<p>After the meeting, I started working on the download configuration and deployment feedback point immediately. While the library is a long way from being feature complete, It doesn’t hurt to make life easier for an early adopter, who may want to test the library or maybe even contribute (a man can dream).</p>
<p>Python is a bit tricky to package for cross-platform deployment. I am currently stuck trying to build an installable for Windows using ‘Py2exe’ after my initial success with ‘Py2app’ for macOS.</p>
<p>In addition to working towards a one-click install across macOS, Linux and Windows, I have also introduced a simple GUI for configuring the application removing the reliance on the command-line. I love the CLI, but I understand it may be an unnecessary hindrance.</p>
<p><img src="/supplybi.github.io/images/config_gui.jpg" class="img-fluid img-responsive" /></p>
<p>Now you can type ‘supplychainpy’ into the CLI and you will be presented with a GUI configuration screen.</p>
<h2 id="back-to-the-editor">Back to the Editor</h2>
<p>I appreciated the feedback from the IAsquare team. Every one of the team’s feedback points supported a clear UX/UI improvement and an actionable deliverable for the reporting suite. All that is left is for me is to get back to the editor and deliver. Thank you, IAsquare.</p>First of all, Happy New Year! It’s been a while. I recently met with the London-based UX agency IAsquare. The team, Anthony, George and Denia are UX professionals who have advised and conducted user testing for some big names in technology. I was very grateful when they offered their time to assist me in improving the UX/UI of supplychainpy.Dash the Bot2016-11-01T00:00:00+00:002016-11-01T00:00:00+00:00http://supplybi.github.io/supplybi.github.io/supplybi.github.io/2016/11/01/Dash<p>The world is going Bot crazy; our AI overlords are just around the corner.
To ingratiate ourselves and be on the right side of history we almost avoided pressing a Bot into our service,
but we couldn’t resist. Zero1, HAL, Skynet don’t judge us… C’mon everyone else is doing it. <!--more --></p>
<p>Meet Dash (data, analysis, service, handler… just kidding it is an abbreviation of the word dashboard) our reporting Bot as of release 0.0.4.
Okay, “Bot” is a bit of a stretch, it’s not a full-on Bot. Dash does not have an opinion on your colleagues, the football league or the latest Netflix Original.
Dash can, however, use natural language to query the analysed data, provide insights and serve up hyperlinks as answers to inquiries.
Below is a Gif of Dash in action:</p>
<p><img src="/supplybi.github.io/images/dash.gif" class="img-fluid img-responsive" /></p>
<h2 id="interface-and-lexicon">Interface and Lexicon</h2>
<p>Dash can perform many tasks competently if spoken to correctly. While more development for less constrained interaction is in progress, currently Dash understands
several commands and questions about the analysis of which Dash is the custodian. For example, ask Dash:</p>
<ul>
<li>“Which SKU has the highest revenue?”</li>
<li>“Which SKU generates the lowest revenue?”</li>
<li>“Which SKU has the largest excess?”</li>
<li>“Which SKU has the largest shortage?”</li>
</ul>
<p>Hopefully, a pattern is emerging. The basic premise is that Dash can be asked to retrieve data for any keyword found in the reporting suite at SKU level and as an aggregate across the inventory profile.
Other commands include:</p>
<ul>
<li>“Show {<em>replace with SKU ID</em>}”: retrieves a hyperlink to the analysis of the SKU requested.</li>
<li>“Describe {<em>replace with SKU ID</em>}”: retrieves a breakdown of the SKUs main statistics (in development).</li>
</ul>
<p>The link to the ‘Dash Lexicon’ page provides a useful guide to interacting with Dash. The aim in future releases is to make this guide redundant.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The reporting suite has a digital custodian called Dash. Dash is a Bot of sorts and aims to satisfy your need to query the reports quickly using natural language.
Although Dash cannot yet gossip with you about the weather, the interface does provide a method of querying the analysis using natural language which is easier to remember than SQL or MDX.
Obviously, the solution provided is not as robust as those domain specific languages (DSL) but serves a more constrained purpose in a simple to understand way.
Dash is very much in the early stages of development. Dash doesn’t know military strategy or who Sarah Conor is. However, Dash can tell you which SKUs are overstocked; so there’s that.</p>The world is going Bot crazy; our AI overlords are just around the corner. To ingratiate ourselves and be on the right side of history we almost avoided pressing a Bot into our service, but we couldn’t resist. Zero1, HAL, Skynet don’t judge us… C’mon everyone else is doing it.Using Analytic Hierarchy with Supplychainpy2016-10-26T00:00:00+00:002016-10-26T00:00:00+00:00http://supplybi.github.io/supplybi.github.io/supplybi.github.io/2016/10/26/Analytic-Hierarchy<h2 id="introduction">Introduction</h2>
<p>Thomas Saaty (Saaty, 1990) developed the Analytic Hierarchy Process (AHP). The AHP structures a decision problem by
comparing a set of similar alternative options (e.g. alternative haulage vehicles, suppliers, warehouse assets etc.)
against different criterion (e.g. safety, reliability, maintainability and cost) to identify the superior choice in a logically consistent manner.<!--more --></p>
<p>The criterion for evaluation can be qualitative or quantitative. Relative scales and pairwise comparisons as ratios are used to derive weights and priorities.
The resulting weights are used to obtain a score for each option in regards to a criterion, combining the scores for a global for all the criteria.</p>
<p>In summary, the AHP, therefore, requires the decision maker to:</p>
<ul>
<li>State clearly the objective.
<ul>
<li>e.g. Select a new haulage vehicle for addition to the fleet.</li>
</ul>
</li>
<li>Define the criteria.
<ul>
<li>e.g. style, reliability, comfort and fuel economy</li>
</ul>
</li>
<li>Shortlist the alternatives options.
<ul>
<li>e.g. Scania, Iveco, Navistar and Volvo</li>
</ul>
</li>
</ul>
<h2 id="implementation">Implementation</h2>
<p>The AHP requires several steps which can be summarised as:</p>
<ol>
<li>Computing the eigenvector for the criteria.</li>
<li>Computing the matrices for the alternative options</li>
<li>Computing the eigenvector for the alternative options</li>
<li>Ranking the alternative option.</li>
</ol>
<h3 id="computing-the-eigenvector-for-the-criteria">Computing the Eigenvector for the Criteria</h3>
<p>Creating a pairwise comparison matrix allows for the expression of the relative importance of one criterion over another (Haas and Meixner, 2005).
The array is populated using relative scores measured on a scale from 1 to 9. The table below represents that ‘fundamental scale’ used in scoring the criterion
and is taken directly from Saaty (1990, pp15).</p>
<table>
<thead>
<tr>
<th>Importance</th>
<th> Definition </th>
<th>Explanation </th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Equal importance </td>
<td>Two activities contribute equally to the objective</td>
</tr>
</tbody>
<tbody>
<tr>
<td>3</td>
<td>Moderate importance of one over another </td>
<td>Experience and judgement strongly favor one activity over another</td>
</tr>
</tbody>
<tbody>
<tr>
<td>5</td>
<td>Essential or strong importance Very strong importance </td>
<td>Experience and judgement strongly favor one activity over another</td>
</tr>
</tbody>
<tbody>
<tr>
<td>7</td>
<td>Very strong importance </td>
<td>An activity is strongly favored and dominance demonstrated in practice</td>
</tr>
</tbody>
<tbody>
<tr>
<td>2,4,6,8</td>
<td>Intermediary values between adjacent judgments </td>
<td>when compromise is needed</td>
</tr>
</tbody>
<tbody>
<tr>
<td>9</td>
<td>Extreme importance </td>
<td>highest possible order of affirmation</td>
</tr>
</tbody>
</table>
<h3 id="pairwise-comparison-matrix">Pairwise Comparison Matrix</h3>
<p>To compute the eigenvector for the criteria, the matrix <script type="math/tex">A</script> of size <script type="math/tex">m \times m</script> is required where <script type="math/tex">m</script> is the number of criteria.
Matrix <script type="math/tex">A</script> is populated with values for <script type="math/tex">a_{ij}</script> where <script type="math/tex">a_{ij}</script> denotes the entry for the <script type="math/tex">i</script>th row and <script type="math/tex">j</script>th column:</p>
<script type="math/tex; mode=display">% <![CDATA[
\therefore
A_{m \times m}= \left( \begin{array} \\
a_{11} & \cdots & a_{1j} \\
\vdots & \ddots & \vdots \\
a_{i1} & \cdots & a_{ij} \\
\end{array} \right ) %]]></script>
<p>Let’s walk through an illustrative example, continuing with the decision we proposed in the summary of the AHP in the second paragraph of the introduction.</p>
<p>We are an international logistics company, and we are making the decision between several alternative options for a new vehicle for our fleet of vehicles responsible for trunking between national distribution centres (NDC), based on the criteria:</p>
<ul>
<li>style</li>
<li>reliability</li>
<li>fuel economy</li>
<li>comfort</li>
</ul>
<p>Based on our judgement fuel economy is the most important for the Euro touring vehicles in our fleet,
followed closely by reliability because maintenance costs are a b…bother and these bad boys will be putting in some mileage. Comfort is a concern because driving several hours between stops should feel comfortable.
We covet reliability over style and are not too fussed about what the other drivers think of the rig, so style is a distant consideration at fourth place (look we’ll just get a bunch of flames or my little pony decals to pimp the ride). So based on these priorities we can make the following judgement:</p>
<ol>
<li>Fuel economy is 7 times more important than style.</li>
<li>Reliability is 2 times less important than Fuel economy.</li>
<li>Fuel economy is 7 times more important than comfort (#harsh).</li>
<li>Fuel economy is 9 times more important than style.</li>
</ol>
<p>Below the array matrix for the criteria scores the criteria in the following order top to bottom left to right: fuel economy, reliability, comfort and style.</p>
<script type="math/tex; mode=display">% <![CDATA[
{\bf A}=\left[ \begin{array} \\
1/1 & 2/1 & 7/1 & 9/1 \\
0 & 1/1 & 5/1 & 5/1 \\
0 & 0 & 1/1 & 5/1\\
0 & 0 & 0 & 1/1 \\
\end{array}\right ] %]]></script>
<p>For each entry where we compare <script type="math/tex">i</script> (row) to <script type="math/tex">j</script> (column), if <script type="math/tex">a_{ij} > 1</script> then the <script type="math/tex">i</script>th criterion is more important that <script type="math/tex">j</script>th criterion, while if <script type="math/tex">% <![CDATA[
a_{ij} <1 %]]></script> then the <script type="math/tex">j</script>the criterion is more important.
Now we add the reciprocal scores and complete our pairwise matrix.</p>
<script type="math/tex; mode=display">% <![CDATA[
{\bf A}=\left[ \begin{array} \\
1/1 & 2/1 & 7/1 & 9/1 \\
1/2 & 1/1 & 5/1 & 5/1 \\
1/7 & 1/5 & 1/1 & 5/1\\
1/9 & 1/5 & 1/5 & 1/1 \\
\end{array}\right ] %]]></script>
<p>Any given entry <script type="math/tex">a_{ij}</script> should satisfy the following constraint <script type="math/tex">a_{ij} \cdot a_{ji} = 1</script>.
For example the score for <script type="math/tex">fuel</script> <script type="math/tex">economy</script> against <script type="math/tex">reliability</script> or <script type="math/tex">a_{1,2} = \frac{2}{1}</script>,
implying as we stated above that fuel economy is more important than reliability.
The reciprocal is reliability against fuel economy, scored at entry <script type="math/tex">a_{2,1} = \frac{1}{2}</script> the reciprocal of the previous rating.
This scoring is consistent and meets the constraint as <script type="math/tex">2\times0.5=1</script>.</p>
<p>Also, notice how <script type="math/tex">a_{jj}</script> always equal 1, this is because each criterion is as important as itself.
Now we convert to decimals:</p>
<script type="math/tex; mode=display">% <![CDATA[
{\bf A} =\left[ \begin{array} \\
1/1 & 2/1 & 7/1 & 9/1 \\
1/2 & 1/1 & 5/1 & 5/1 \\
1/7 & 1/5 & 1/1 & 5/1\\
1/9 & 1/5 & 1/5 & 1/1 \\
\end{array}\right ] = \left[ \begin{array} \\
1.0 & 2.0 & 7.0 & 9.0 \\
0.5 & 1.0 & 5.0 & 5.0 \\
0.1429 & 0.2 & 1.0 & 5.0\\
0.1111 & 0.2 & 0.2 & 1.0 \\
\end{array}\right ] %]]></script>
<p>Calculate the ranking by squaring the pairwise matrix:</p>
<script type="math/tex; mode=display">% <![CDATA[
\left[ \begin{array} \\
1.0 & 2.0 & 7.0 & 9.0 \\
0.5 & 1.0 & 5.0 & 5.0 \\
0.1429 & 0.2 & 1.0 & 5.0\\
0.1111 & 0.2 & 0.2 & 1.0 \\
\end{array}\right ] \cdot \left[ \begin{array} \\
1.0 & 2.0 & 7.0 & 9.0 \\
0.5 & 1.0 & 5.0 & 5.0 \\
0.1429 & 0.2 & 1.0 & 5.0\\
0.1111 & 0.2 & 0.2 & 1.0 \\
\end{array}\right ] = \left[ \begin{array} \\
1.0 & 4.0 & 49.0 & 81.0 \\
0.25 & 1.0 & 25.0 & 25. \\
0.02 & 0.04 & 1.0 & 25.0 \\
0.012 & 0.04 & 0.04 & 1.0 \\
\end{array}\right ] %]]></script>
<p>We now sum each row <script type="math/tex">a_{1j} \cdot\cdot\cdot a_{mj}</script> and then sum the row totals.</p>
<script type="math/tex; mode=display">% <![CDATA[
{\bf A}= \left[
\begin{array}{cccc}
1.0 & 4.0 & 49.0 & 81.0 \\
0.25 & 1.0 & 25.0 & 25.0 \\
0.02 & 0.04 & 1.0 & 25.0 \\
0.012 & 0.04 & 0.04 & 1.0 \\
\end{array}
\right]
\left[
\begin{array}{c}
135 \\
51.25 \\
26.06 \\
1.092
\end{array}
\right] %]]></script>
<p>The total for the sum of each row is 213.402. We normalise by dividing the sum of each row by the total 213.402 and we get our eigenvector (<script type="math/tex">w</script>) for the criteria:</p>
<script type="math/tex; mode=display">w = \left[
\begin{array}{c}
0.6326\\
0.2402\\
0.1221\\
0.0051
\end{array}
\right]</script>
<p>If you consider how we prioritised the criteria, it makes sense that <code class="highlighter-rouge">fuel economy</code> represented by the first value in the array scores higher than all the others and <code class="highlighter-rouge">style</code> (at the bottom) the least.
Several iterations of squaring the pairwise matrix and computing an eigenvector are necessary until the current solution no longer differs from the previous iteration.</p>
<p>Now we move onto computing the matrices for the alternative options.
You can begin to see why the AHP can become onerous when calculated by hand or in a spreadsheet.</p>
<h3 id="computing-the-matrices-for-the-alternative-options">Computing the Matrices for the Alternative Options</h3>
<p>For each criterion (style, reliability, comfort and fuel-economy), we construct a pairwise comparison matrix scoring each alternative option (Scania, Iveco, Navistar, Volvo) against each other. These comparisons are for illustration and are not indicative of the manufacturer’s product.</p>
<p>Reliability:</p>
<script type="math/tex; mode=display">% <![CDATA[
{\bf B^{(1)}}=\left[ \begin{array} \\
1/1 & 1/3 & 3/1 & 1/7 \\
3/1 & 1/1 & 5/1 & 1/5 \\
1/3 & 1/5 & 1/1 & 1/5\\
7/1 & 5/1 & 5/1 & 1/1 \\
\end{array}\right ] %]]></script>
<p>Comfort:</p>
<script type="math/tex; mode=display">% <![CDATA[
{\bf B^{(2)}}=\left[ \begin{array} \\
1/1 & 5/1 & 5/1 & 1/7 \\
1/5 & 1/1 & 2/1 & 1/7 \\
1/3 & 1/5 & 1/1 & 1/5 \\
7/1 & 7/1 & 5/1 & 1/1 \\
\end{array}\right ] %]]></script>
<p>Style:</p>
<script type="math/tex; mode=display">% <![CDATA[
{\bf B^{(3)}}=\left[ \begin{array} \\
1/1 & 1/3 & 5/1 & 1/5 \\
3/1 & 1/1 & 2/1 & 3/1 \\
1/3 & 1/5 & 1/1 & 1/5 \\
5/1 & 1/3 & 5/1 & 1/1 \\
\end{array}\right ] %]]></script>
<p>We compute the eigenvector for each as we did before for the criteria <script type="math/tex">B^{n}</script> is the pairwise matrix and <script type="math/tex">S^{n}</script> the corresponding eigenvector:</p>
<script type="math/tex; mode=display">% <![CDATA[
{\bf B^{(1)}}=\left[\begin{array}\\
1.0 & 25.0 & 25.0 & 0.02\\
0.04 & 1.0 & 4.0 & 0.02 \\
0.111 & 0.04 & 1.0 & 0.04 \\
49.0 & 49.0 & 25.0 & 1.0
\end{array}\right]
\;
{\bf s^{(1)}}= \left[
\begin{array}{c}
0.0692 \\
0.2394 \\
0.0081 \\
0.6832
\end{array}
\right] %]]></script>
<script type="math/tex; mode=display">% <![CDATA[
{\bf B^{(2)}}=\left[\begin{array}\\
1.0 & 0.111 & 9.0 & 0.02 \\
9. 0 & 1.0 & 25.0 & 0.04 \\
0.111 & 0.04 & 1.0 & 0.04 \\
49. 0 & 25.0 & 25.0 & 1.0
\end{array}\right]
\;
{\bf s^{(2)}}=\left[
\begin{array}{c}
0.2815\\
0.0279\\
0.0066\\
0.6841
\end{array}
\right] %]]></script>
<script type="math/tex; mode=display">% <![CDATA[
{\bf B^{(3)}}=\left[\begin{array}\\
1.0 & 0.111& 25.0 & 0.04 \\
9.0 & 1.0 & 4.0 & 9.0 \\
0.111 & 0.04 & 1.0 & 0.04 \\
25.0 & 0.111 & 25.0 & 1.0
\end{array}\right]
\;
{\bf s^{(3)}}=\left[
\begin{array}{c}
0.2578 \\
0.2267 \\
0.0117 \\
0.5038
\end{array}
\right] %]]></script>
<p>Fuel-economy is treated slightly differently since the values are in mpg an objective value.
If we accept that the following mpg for each vehicle: Scania 11 mpg, Iveco 9 mpg, Navistar 10 mpg, Volvo 12 mpg, we can normalise these values and create a ranking:</p>
<script type="math/tex; mode=display">{\bf s^{(4)}}=\left[
\begin{array}{c}
11 \\
9 \\
10 \\
12
\end{array}
\right]=\left[
\begin{array}{c}
0.3009 \\
0.2389 \\
0.2124 \\
0.2478
\end{array}
\right]</script>
<h3 id="computing-the-eigenvector-for-the-alternative-options">Computing the eigenvector for the alternative options</h3>
<p>Now we can compute the eigenvector for the alternatives by combining them into a matrix and multplying by the criteria weight <script type="math/tex">w</script> computed earlier. So <script type="math/tex">S = [s^{(1)},s^{(2)},s^{(3)},s^{(4)}]</script>.</p>
<script type="math/tex; mode=display">% <![CDATA[
{\bf S}= \left[
\begin{array}{cccc}
0.0692 & 0.2815 & 0.2578 & 0.3009 \\
0.2394 & 0.0279 & 0.2267 & 0.2389 \\
0.0081 & 0.0066 & 0.0117 & 0.2124 \\
0.6832 & 0.6841 & 0.5038 & 0.2478
\end{array}
\right] %]]></script>
<p><script type="math/tex">S</script> is multiplied by the previously calculated weights for the criteria <script type="math/tex">\therefore S\cdot w = v</script> where <script type="math/tex">v=</script> the global vector for all scores.</p>
<script type="math/tex; mode=display">% <![CDATA[
{\bf v}= \left[
\begin{array}{cccc}
0.0692 & 0.2815 & 0.2578 & 0.3009 \\
0.2394 & 0.0279 & 0.2267 & 0.2389 \\
0.0081 & 0.0066 & 0.0117 & 0.2124 \\
0.6832 & 0.6841 & 0.5038 & 0.2478
\end{array}
\right] \cdot \left[
\begin{array}{c}
0.6326\\
0.2402\\
0.1221\\
0.0051
\end{array}
\right] %]]></script>
<p>The resulting vector can be ranked and will indicate the best choice:</p>
<script type="math/tex; mode=display">{\bf \therefore v}= \left[
\begin{array}{c}
0.2154\\
0.2054\\
0.0114\\
0.5679
\end{array}
\right]</script>
<h3 id="ranking-the-alternative-options">Ranking the Alternative Options</h3>
<p>The clear winner, when ranked, is the Volvo (nb: remember the initial order of the comparisons was Scania, Iveco, Navistar and Volvo).</p>
<p>The cost of each vehicle may also be taken into consideration using a cost-benefit ratio.</p>
<h2 id="ahp-using-supplychainpy">AHP using Supplychainpy</h2>
<p>As of release 0.0.4, Supplychainpy will have the facility for computing the AHP of a given set of criteria and alternative options. In the libraries implementation, the objective (quantitative) criteria are identified separately from the subjective scores. The differentiation allows the construct of normalised weights based on raw values as opposed to scores in keeping with Saaty (1990)
Only the scores for the comparison of <script type="math/tex">i</script> to <script type="math/tex">j</script> are required, the reciprocals are computed as part fo the function.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">lorry_cost</span> <span class="o">=</span> <span class="p">{</span><span class="s">'scania'</span><span class="p">:</span> <span class="mi">55000</span><span class="p">,</span> <span class="s">'iveco'</span><span class="p">:</span> <span class="mi">79000</span><span class="p">,</span> <span class="s">'volvo'</span><span class="p">:</span> <span class="mi">59000</span><span class="p">,</span> <span class="s">'navistar'</span><span class="p">:</span> <span class="mi">66000</span><span class="p">}</span>
<span class="n">criteria</span> <span class="o">=</span> <span class="p">(</span><span class="s">'style'</span><span class="p">,</span> <span class="s">'reliability'</span><span class="p">,</span> <span class="s">'comfort'</span><span class="p">,</span> <span class="s">'fuel_economy'</span><span class="p">)</span>
<span class="n">criteria_scores</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">7</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">9</span> <span class="o">/</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">]</span>
<span class="n">options</span> <span class="o">=</span> <span class="p">(</span><span class="s">'scania'</span><span class="p">,</span> <span class="s">'iveco'</span><span class="p">,</span> <span class="s">'navistar'</span><span class="p">,</span> <span class="s">'volvo'</span> <span class="p">)</span>
<span class="n">option_scores</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">'style'</span><span class="p">:</span> <span class="p">[</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">),</span>
<span class="p">(</span><span class="mi">3</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span> <span class="o">/</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">),</span>
<span class="p">(</span><span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">],</span>
<span class="s">'reliability'</span><span class="p">:</span> <span class="p">[</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">3</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">7</span><span class="p">),</span>
<span class="p">(</span><span class="mi">3</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">),</span>
<span class="p">(</span><span class="mi">7</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">],</span>
<span class="s">'comfort'</span><span class="p">:</span> <span class="p">[</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">7</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">7</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">5</span><span class="p">),</span>
<span class="p">(</span><span class="mi">7</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">7</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">],</span>
<span class="s">'fuel_economy'</span><span class="p">:</span> <span class="p">(</span><span class="mi">11</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">lorry_decision</span> <span class="o">=</span> <span class="n">analytical_hierarchy_process</span><span class="p">(</span><span class="n">criteria</span><span class="o">=</span><span class="n">criteria</span><span class="p">,</span>
<span class="n">criteria_scores</span><span class="o">=</span><span class="n">criteria_scores</span><span class="p">,</span>
<span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">,</span>
<span class="n">option_scores</span><span class="o">=</span><span class="n">option_scores</span><span class="p">,</span>
<span class="n">quantitative_criteria</span><span class="o">=</span><span class="p">(</span><span class="s">'fuel_economy'</span><span class="p">,),</span>
<span class="n">item_cost</span><span class="o">=</span><span class="n">lorry_cost</span><span class="p">)</span>
</code></pre></div></div>
<p>generates the following results:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{'analytical_hierarchy': {'iveco': 0.20541585500041709, 'scania': 0.21539971200341132, 'volvo': 0.5677817531137912, 'navistar': 0.011402679882380324}, 'cost_benefit_ratios': {'iveco': 0.67345198031782316, 'scania': 1.0143368256160643, 'volvo': 2.4924656619741006, 'navistar': 0.044746880144492483}
</code></pre></div></div>
<h2 id="conclusion">Conclusion</h2>
<p>The Analytic Hierarchy Process (AHP) provides a systematic way of structuring decisions using objective and subjective criterion using weights and priorities. Implementing the AHP can be summarised in four steps:</p>
<ol>
<li>Computing the eigenvector for the criteria.</li>
<li>Computing the matrices for the alternative options</li>
<li>Computing the eigenvector for the alternative options</li>
<li>Ranking the alternative option.</li>
</ol>
<p>The supplychainpy library (as of release 0.0.4) supports this computation.</p>
<h2 id="references">References</h2>
<p>Saaty, T.L., 1990. How to make a decision: the analytic hierarchy process. <em>European journal of operational research</em>, 48(1), pp.9-26.</p>
<p>Haas, R. and Meixner, O., 2005. An illustrated guide to the analytic hierarchy process. <em>Institute of Marketing & Innovation, University of Natural Resources and Applied Life Sciences</em>, Vienna, pp.10-13.</p>Introduction Thomas Saaty (Saaty, 1990) developed the Analytic Hierarchy Process (AHP). The AHP structures a decision problem by comparing a set of similar alternative options (e.g. alternative haulage vehicles, suppliers, warehouse assets etc.) against different criterion (e.g. safety, reliability, maintainability and cost) to identify the superior choice in a logically consistent manner.Recommendations2016-10-05T00:00:00+00:002016-10-05T00:00:00+00:00http://supplybi.github.io/supplybi.github.io/supplybi.github.io/2016/10/05/Recommendations<p>The reporting suite provides charts and tables, while also calling out key metrics using slates in the HUD (please see: <a href="/2016/09/21/Reporting-Suite-The-HUD.html">HUD post</a>).
While data visualisations are a useful way to gain insight into data, in unfamiliar domains natural language annotation may also be of use.
The recommendations in the reporting suite provide some contextual analysis and in some cases make observations that may be counter-intuitive.
<!--more --></p>
<h2 id="recommendation-panel">Recommendation Panel</h2>
<p>The recommendations generated as part of the detailed breakdown for a
specific stock keeping unit (SKU), use KPI’s and data from the analysis to provide a comparative understanding of the SKU in the context of the current inventory profile. The recommendation focuses on the main metrics that may point to specific issues to take into consideration.
The image below shows the recommendations as a panel within the ‘SKU view’:</p>
<p><img src="/supplybi.github.io/images/rec_panel.png" class="img-fluid img-responsive" /></p>
<h2 id="recommendation-feed">Recommendation Feed</h2>
<p>Although each SKU in the inventory profile will have a recommendation
auto-generated during the analysis, stepping through each SKU view would be tedious.
Navigating to the recommendation feed provides a searchable feed of all the recommendations as stacked panels,
including a proposal for the entire inventory profile. The Gif
below shows the recommendation feed and search functionality:</p>
<p><img src="/supplybi.github.io/images/rec_feed.gif" class="img-fluid img-responsive" /></p>
<h2 id="traffic-lights">Traffic Lights</h2>
<p>Each recommendation also contains a traffic light indicator. The traffic lights can be summarised as:</p>
<ul>
<li>Green - quantity-on-hand (QOH) is capable of satisfying demand based on historical analysis and service level set in analysis.</li>
<li>Amber - QOH is below reorder level, expecting a purchase order to be raised.</li>
<li>Red - QOH is below the quantity apportioned for safety (buffer stock) and future demand is consuming safety stock.</li>
<li>White - QOH is below 50% of quantity apportioned for safety stock and the inability to meet demand from QOH is highly probable.</li>
</ul>
<p>The traffic light indicates the probability of stockout and not necessarily the ‘health’ of the SKU in question. An SKU may, for instance, receive a green indicator when overstocked. Although the excess stock is detrimental to the inventory profile, increasing the additional working capital tied up in the SKU, exposure to the risk of obsolescence and shrinkage, the SKU is not at any immediate risk of not meeting expected demand. The issues as mentioned earlier are more medium to long-term. The traffic light system provides an indicator for SKUs requiring immediate remedial action.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The recommendations panels and feed provide supporting contextual information and insight using natural language.
The recommendations assist in understanding an SKU in the context of the inventory profile. The recommendation feed aggregates all recommendations, including a profile-wide recommendation, and provides a searchable list of stacked panels for retrieving recommendations quickly.
The panels in the feed also have a traffic light system indicating the ability of the SKU to satisfy demand from the current quantity on hand. While not indicating the whole health of the SKU, the traffic light system indicates which SKUs are in immediate need of attention.</p>The reporting suite provides charts and tables, while also calling out key metrics using slates in the HUD (please see: HUD post). While data visualisations are a useful way to gain insight into data, in unfamiliar domains natural language annotation may also be of use. The recommendations in the reporting suite provide some contextual analysis and in some cases make observations that may be counter-intuitive.The HUD2016-09-21T00:00:00+00:002016-09-21T00:00:00+00:00http://supplybi.github.io/supplybi.github.io/supplybi.github.io/2016/09/21/Reporting-Suite:-The-HUD<p>The critical role of the reporting feature in the supplychainpy library, is to call out important information quickly.
The reports use what we like to refer to as a “Heads up Display” (HUD) to highlight key values and KPIs. <!--more -->
As seen below, the HUD is a row of boxes (Slates), that sits on top of the main charts, analysis and tabular breakdowns.</p>
<p><img src="/supplybi.github.io/images/HUD.png" class="img-fluid img-responsive" /></p>
<p>The following image shows the HUD in situ:</p>
<p><img src="/supplybi.github.io/images/shortage.jpg" class="img-fluid img-responsive" /></p>
<p>The content inside each slate is dynamic with hyperlinks for SKUs, classifications and warnings which lead to further analysis.
Clicking the SKU <code class="highlighter-rouge">KR202-225</code>, navigates to a page which presents facts and figures related only to this SKU.
The page devoted to the SKU highlights metrics such as <code class="highlighter-rouge">inventory turns</code> and <code class="highlighter-rouge">safety stock</code>, as well as presenting different forecasts.</p>
<p>The HUD is an important feature for quickly indicating the state of the inventory profile or object it summarises.
There are a few other quick indicators implemented, one of which is the icon to the right of the SKU title at the top of the page.
In the case of SKU <code class="highlighter-rouge">KR202-225</code>, the icon indicates a shortage of stock. A future post will provide more in-depth coverage of this feature.</p>The critical role of the reporting feature in the supplychainpy library, is to call out important information quickly. The reports use what we like to refer to as a “Heads up Display” (HUD) to highlight key values and KPIs.Reporting Feature For Supplychainpy2016-08-01T00:00:00+00:002016-08-01T00:00:00+00:00http://supplybi.github.io/supplybi.github.io/supplybi.github.io/2016/08/01/Dashboard-for-supplychainpy<h2 id="what-to-expect-from-the-reporting-feature-in-release-004">What to Expect from the Reporting Feature in Release 0.0.4?</h2>
<p><del>Release 0.0.4 is set officially for August 31, 2016</del> Release 0.0.4 is still in development (“the best-laid schemes of mice and men…” Please read the forum post on the <a href="https://groups.google.com/d/msg/supplychainpy/TwQG9IZgjvU/3au925dlBwAJ">delay</a>). The likelihood is that that the sneak peek offered in the Gif below
will change. The reporting function is deployed using a web interface and built with Flask, jinja, jQuery, javascript and SQLalchemy and powered by the supplychainpy library (Python and Cython).<!--more -->
The Reports are launched using the command line, with the data source and database location supplied as arguments.
A GUI interface is launched offering the option to specify the port for the reports run on the local server.</p>
<p>The Gif below shows the current state of the reports (using <a href="https://github.com/KevinFasusi/supplychainpy/blob/master/data1.csv">dummy data</a> to create the dynamic content):</p>
<p><img src="/supplybi.github.io/images/supp.gif" class="img-fluid img-responsive" /></p>
<p>There is still a lot to do, and subsequent versions will provide improvements. Importantly, the
reports are supposed to be smaller in scale than large sprawling enterprise packages. The reports are a supplement and not a replacement for more comprehensive solutions.</p>
<h2 id="why-add-a-reporting-feature-to-supplychainpy">Why Add a Reporting Feature to Supplychainpy?</h2>
<p>There are three main reasons for adding a reporting feature to supplychainpy:</p>
<ol>
<li>To provide the ability to visualise data and spot trends, allowing analysts to get a “feel” for their data.</li>
<li>To provide a set of generic default reports, to showcase some general uses cases and highlight the capabilities of the library.</li>
<li>To provide more choice.</li>
</ol>
<p>Enterprise Resource Planning (ERP) tools often come with some reporting functionality. Unfortunately, teams are
rarely satisfied with the suite of reporting features provided. Sometimes adoption of a suite of reports fails
due to poor integration with existing practices, inadequate requirements analysis or simply a team’s refusal to adopt the system.
Any one or even all of those previously mentioned can be reasons to abandon a suite of reports.</p>
<h2 id="wider-discontent">Wider Discontent</h2>
<p>Anecdotally the possibility for dissatisfaction with reports increases with significant changes in an organisation’ structure. Mergers and acquisitions or the installation of new information technology infrastructure can
hinder the value of the incumbent reporting system. In environments like these the data warehouse and a spreadsheet, rightly or wrongly, becomes king.
Against this backdrop, supplychainpy offers more choice and flexibility for analysts to supplement their existing reporting.
Most reporting tools are mandated from the top down, and change requests or additional features are mired in bureaucracy.
The inflexibility of such systems can stifle the usefulness of analytical tools, making it a challenge to derive value.</p>
<p>In a recent report featured in MIT Sloan Management Review, exploring the work behind analytics success, the decline in competitive advantage derived from analytics is one of five key
findings. The review states:</p>
<blockquote>
<p>Competitive advantage with analytics is waning. The percentage of companies that report obtaining a competitive
advantage with analytics has declined significantly over the past two years. Increased market adoption of analytics
levels the playing field and makes it more difficult for companies to keep their edge.
— Ransbotham <em>et al.</em> (2016)</p>
</blockquote>
<p>Supplychainpy reports provide an alternative for an analyst from conventional tools and a springboard to increase competitive advantages.
In release 0.0.4 Supplychainpy will debut the reporting feature.</p>
<h2 id="plans-for-future-releases">Plans for Future Releases</h2>
<p>Currently, the reporting feature is still under development. Planned development and modifications before debut release include currency conversion, inventory policy generator and general formatting and presentation. An Assistant application directly in Excel is also in development. A full list enhancements are listed on the issues page on <a href="https://github.com/KevinFasusi/supplychainpy/issues">GitHub</a>.</p>
<h3 id="supplychainpy-excel-assistant">Supplychainpy Excel Assistant</h3>
<p>A brief word on the supplychainpy Excel Assistant, Excel is treated like an unloved child by reporting suites and ERP tools. In an attempt to make Excel a first class citizen in your workflow, a supplychainpy Assistant is also in development. This Assistant is an Excel add-in and allows an analyst quickly to transfer some of the default reporting analysis over to their Excel workbook. The Assistant reduces the immediate need to
know how to use a library such as openpyxl or xlwings, although the Assistant is not a panacea. For better control and analysis, learning to use a python library explicitly for interacting with Excel is best.</p>
<h2 id="references">References</h2>
<p>Ransbotham, S., Kiron, D., Prentice, P. K., 2016. Beyond the hype: The hard work behind analytics success. <em>MIT Sloan Management Review</em> 57 (3).</p>What to Expect from the Reporting Feature in Release 0.0.4? Release 0.0.4 is set officially for August 31, 2016 Release 0.0.4 is still in development (“the best-laid schemes of mice and men…” Please read the forum post on the delay). The likelihood is that that the sneak peek offered in the Gif below will change. The reporting function is deployed using a web interface and built with Flask, jinja, jQuery, javascript and SQLalchemy and powered by the supplychainpy library (Python and Cython).Release 0.0.2 Deep Dive2016-04-16T00:00:00+00:002016-04-16T00:00:00+00:00http://supplybi.github.io/supplybi.github.io/supplybi.github.io/2016/04/16/Release-0.0.2<!--<div class="well container-fluid well-sm ">
<p>Update. The incorrect use of lambdas assigned to variables, has been corrected in release 0.0.4. Please read the post
Pep8 Violations Galore: The not so good, the quite bad and the seriously ugly.</p>
</div> -->
<p>Release 0.0.2 sees the introduction of the Monte Carlo simulation as a feature of the library. Using historical data supplied from a <em>.csv</em>, probable transactions are simulated. The simulation uses normally distributed random demand,
to decrement stock over a given period. Inventory movement trigger consumption of stock and affect the transactions for
opening stock, closing stock, backlog and purchase orders. <!--more --></p>
<p>Running the Monte Carlo simulation is as simple as:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">decimal</span> <span class="kn">import</span> <span class="n">Decimal</span><br data-jekyll-commonmark-ghpages="" /><span class="kn">from</span> <span class="nn">supplychainpy</span> <span class="kn">import</span> <span class="n">simulate</span><br data-jekyll-commonmark-ghpages="" /><span class="kn">from</span> <span class="nn">supplychainpy</span> <span class="kn">import</span> <span class="n">model_inventory</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">orders_analysis</span> <span class="o">=</span> <span class="n">model_inventory</span><span class="o">.</span><span class="n">analyse_orders_abcxyz_from_file</span><span class="p">(</span><span class="n">file_path</span><span class="o">=</span><span class="s">"data.csv"</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">z_value</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="mf">1.28</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_cost</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="mi">5000</span><span class="p">),</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">file_type</span><span class="o">=</span><span class="s">"csv"</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">sim</span> <span class="o">=</span> <span class="n">simulate</span><span class="o">.</span><span class="n">run_monte_carlo</span><span class="p">(</span><span class="n">orders_analysis</span><span class="o">=</span><span class="n">orders_analysis</span><span class="o">.</span><span class="n">orders</span><span class="p">,</span> <span class="n">runs</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">period_length</span><span class="o">=</span><span class="mi">12</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">sim_window</span> <span class="o">=</span> <span class="n">simulate</span><span class="o">.</span><span class="n">summarize_window</span><span class="p">(</span><span class="n">simulation_frame</span><span class="o">=</span><span class="n">sim</span><span class="p">,</span> <span class="n">period_length</span><span class="o">=</span><span class="mi">12</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">sim_frame</span> <span class="o">=</span> <span class="n">simulate</span><span class="o">.</span><span class="n">summarise_frame</span><span class="p">(</span><span class="n">sim_window</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">optimised</span> <span class="o">=</span> <span class="n">simulate</span><span class="o">.</span><span class="n">optimise_service_level</span><span class="p">(</span><span class="n">service_level</span><span class="o">=</span><span class="mf">90.0</span><span class="p">,</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">frame_summary</span><span class="o">=</span><span class="n">sim_frame</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">orders_analysis</span><span class="o">=</span><span class="n">orders_analysis</span><span class="o">.</span><span class="n">orders</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">runs</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">percentage_increase</span><span class="o">=</span><span class="mf">3.0</span><span class="p">)</span></code></pre></figure>
<h2 id="running-the-simulation">Running the Simulation</h2>
<p>Before running the simulation, an analysis of the inventory profile is required. The series supplied by the <em>.csv</em>
file, an example of the format is <a href="https://raw.githubusercontent.com/KevinFasusi/supplychainpy/master/supplychainpy/data.csv">here</a>.
A detailed breakdown of the <code class="highlighter-rouge">model_inventory.analyse_orders_abcxyz_from_file</code> function can be found in the release-0.0.1 deep dive.</p>
<p>Now that we have the demand profile analysed, we can run the Monte Carlo. The interface for the Monte Carlo is in the
<em>simulate.py</em> module. In the <code class="highlighter-rouge">simulate.run_monte_carlo</code> function, <code class="highlighter-rouge">simulation</code> instantiates <code class="highlighter-rouge">SetupMonteCarlo</code> class.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">decimal</span> <span class="kn">import</span> <span class="n">Decimal</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="kn">from</span> <span class="nn">supplychainpy</span> <span class="kn">import</span> <span class="n">simulate</span><br data-jekyll-commonmark-ghpages="" /><span class="kn">from</span> <span class="nn">supplychainpy</span> <span class="kn">import</span> <span class="n">model_inventory</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">orders_analysis</span> <span class="o">=</span> <span class="n">model_inventory</span><span class="o">.</span><span class="n">analyse_orders_abcxyz_from_file</span><span class="p">(</span><span class="n">file_path</span><span class="o">=</span><span class="s">"data.csv"</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">z_value</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="mf">1.28</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_cost</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="mi">5000</span><span class="p">),</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">file_type</span><span class="o">=</span><span class="s">"csv"</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">sim</span> <span class="o">=</span> <span class="n">simulate</span><span class="o">.</span><span class="n">run_monte_carlo</span><span class="p">(</span><span class="n">orders_analysis</span><span class="o">=</span><span class="n">orders_analysis</span><span class="o">.</span><span class="n">orders</span><span class="p">,</span> <span class="n">runs</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">period_length</span><span class="o">=</span><span class="mi">12</span><span class="p">)</span></code></pre></figure>
<h3 id="generate-a-random-normal-distribution-for-demand">Generate a Random Normal Distribution for Demand</h3>
<p>The <code class="highlighter-rouge">generate_normal_random_distribution</code> method, generates the normally distributed random demand for each sku.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">generate_normal_random_distribution</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">period_length</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /><span class="s">""" Generates the random demand for a given sku.<br data-jekyll-commonmark-ghpages="" /> For each sku a set of random demands are calculated based on the normal distribution of <br data-jekyll-commonmark-ghpages="" /> demand for this product.<br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> Args:<br data-jekyll-commonmark-ghpages="" /> period_length (int): length of window e.g. 12 weeks for a quarter etc.<br data-jekyll-commonmark-ghpages="" /> <br data-jekyll-commonmark-ghpages="" /> Returns:<br data-jekyll-commonmark-ghpages="" /> list: A list of randomly generated demand.<br data-jekyll-commonmark-ghpages="" /> <br data-jekyll-commonmark-ghpages="" /> Raises:<br data-jekyll-commonmark-ghpages="" /> ValueError:<br data-jekyll-commonmark-ghpages="" />"""</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">orders_normal_distribution</span> <span class="o">=</span> <span class="p">{}</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">random_orders_generator</span> <span class="o">=</span> <span class="p">[]</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">final_random_orders_generator</span> <span class="o">=</span> <span class="p">[]</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">for</span> <span class="n">sku</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_analysed_orders</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">period_length</span><span class="p">):</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">nrd_orders</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">normal</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="n">sku</span><span class="o">.</span><span class="n">average_orders</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">scale</span><span class="o">=</span><span class="n">sku</span><span class="o">.</span><span class="n">standard_deviation</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">size</span><span class="o">=</span><span class="n">sku</span><span class="o">.</span><span class="n">order_count</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <br data-jekyll-commonmark-ghpages="" /> <span class="n">random_orders_generator</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">nrd_orders</span><span class="p">))</span><span class="o">.</span><span class="n">tolist</span><span class="p">())</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">orders_normal_distribution</span><span class="p">[</span><span class="n">sku</span><span class="o">.</span><span class="n">sku_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">random_orders_generator</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">random_orders_generator</span> <span class="o">=</span> <span class="p">[]</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">final_random_orders_generator</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">orders_normal_distribution</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">return</span> <span class="n">final_random_orders_generator</span></code></pre></figure>
<h2 id="build-transaction-summary">Build Transaction Summary</h2>
<p>The function <code class="highlighter-rouge">simulation.build_window</code> is a generator that yields the <code class="highlighter-rouge">simulation_window.MonteCarloWindow</code> object.
The <code class="highlighter-rouge">run_monte_carlo</code> method is displayed below.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">run_monte_carlo</span><span class="p">(</span><span class="n">orders_analysis</span><span class="p">:</span> <span class="nb">list</span><span class="p">,</span> <span class="n">runs</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">period_length</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">12</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">Transaction_report</span> <span class="o">=</span> <span class="p">[]</span><br data-jekyll-commonmark-ghpages="" /> <span class="c"># add shortage cost,</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">runs</span><span class="p">):</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">simulation</span> <span class="o">=</span> <span class="n">monte_carlo</span><span class="o">.</span><span class="n">SetupMonteCarlo</span><span class="p">(</span><span class="n">analysed_orders</span><span class="o">=</span><span class="n">orders_analysis</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">random_demand</span> <span class="o">=</span> <span class="n">simulation</span><span class="o">.</span><span class="n">generate_normal_random_distribution</span><span class="p">(</span><span class="n">period_length</span><span class="o">=</span><span class="n">period_length</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">for</span> <span class="n">sim_window</span> <span class="ow">in</span> <span class="n">simulation</span><span class="o">.</span><span class="n">build_window</span><span class="p">(</span><span class="n">random_normal_demand</span><span class="o">=</span><span class="n">random_demand</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">period_length</span><span class="o">=</span><span class="n">period_length</span><span class="p">):</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">sim_dict</span> <span class="o">=</span> <span class="p">{</span><span class="s">"index"</span><span class="p">:</span> <span class="s">"{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">index</span><span class="p">),</span> <br data-jekyll-commonmark-ghpages="" /> <span class="s">"period"</span><span class="p">:</span> <span class="s">"{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">position</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"sku_id"</span><span class="p">:</span> <span class="n">sim_window</span><span class="o">.</span><span class="n">sku_id</span><span class="p">,</span> <br data-jekyll-commonmark-ghpages="" /> <span class="s">"opening_stock"</span><span class="p">:</span> <span class="s">"{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">opening_stock</span><span class="p">)),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"demand"</span><span class="p">:</span> <span class="s">"{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">demand</span><span class="p">)),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"closing_stock"</span><span class="p">:</span> <span class="s">"{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">closing_stock</span><span class="p">)),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"delivery"</span><span class="p">:</span> <span class="s">"{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">purchase_order_receipt_qty</span><span class="p">)),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"backlog"</span><span class="p">:</span> <span class="s">"{:.0f}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">backlog</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"po_raised"</span><span class="p">:</span> <span class="s">"{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">po_number_raised</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"po_received"</span><span class="p">:</span> <span class="s">"{}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">po_number_received</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"po_quantity"</span><span class="p">:</span> <span class="s">"{:.0f}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">purchase_order_raised_qty</span><span class="p">)),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"shortage_cost"</span><span class="p">:</span> <span class="s">"{:.0f}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">shortage_cost</span><span class="p">)),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"revenue"</span><span class="p">:</span> <span class="s">"{:.0f}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">revenue</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"quantity_sold"</span><span class="p">:</span> <span class="s">"{:0.0f}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">sold</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"shortage_units"</span><span class="p">:</span> <span class="s">"{:.0f}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sim_window</span><span class="o">.</span><span class="n">shortage_units</span><span class="p">)}</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">Transaction_report</span><span class="o">.</span><span class="n">append</span><span class="p">([</span><span class="n">sim_dict</span><span class="p">])</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="k">return</span> <span class="n">Transaction_report</span><br data-jekyll-commonmark-ghpages="" /> </code></pre></figure>
<p>Most of the transaction logic for the inventory occurs in the <code class="highlighter-rouge">build_window</code> generator. The generator iterates over each SKU in the list
of <code class="highlighter-rouge">UncertainDemand</code> objects containing the inventory analysis.</p>
<p>Much of the logic presented below is expressed as lambdas in the <code class="highlighter-rouge">build_window</code> generator and assigned to attributes in
the <code class="highlighter-rouge">sim_window</code> a <code class="highlighter-rouge">MonteCarloWindow</code> object.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c"># instantiate sim_window</span><br data-jekyll-commonmark-ghpages="" /><span class="n">sim_window</span> <span class="o">=</span> <span class="n">simulation_window</span><span class="o">.</span><span class="n">MonteCarloWindow</span></code></pre></figure>
<p>The full method is <a href="https://github.com/KevinFasusi/supplychainpy/blob/master/supplychainpy/simulations/monte_carlo.py">here</a>.
Below all the transaction logic in the <code class="highlighter-rouge">build_window</code> method is walked through case by case.
The calculations below share a similar implementation, using lambda functions to assign a value to attributes of the
<code class="highlighter-rouge">sim_window</code> object (an instance of the <code class="highlighter-rouge">MonteCarloWindow</code> class).</p>
<h3 id="opening-stock">Opening Stock</h3>
<p>The <code class="highlighter-rouge">sim_window.opening_stock</code> is set to reorder-level (safety sock + lead-time demand):</p>
<div>
$$
RL = LT \times D + Z \times \sigma \times \sqrt{LT}
$$
</div>
<p>Where: Z = service level, LT = Lead-time, D = Demand.</p>
<p>Opening stock is set at the reorder level in the first period for each SKU. In subsequently the
the opening stock is the previous closing stock.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">closing_stock</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">opening_stock</span><span class="p">,</span> <span class="n">orders</span><span class="p">,</span> <span class="n">deliveries</span><span class="p">,</span> <span class="n">backlog</span><span class="p">:</span> <span class="n">Decimal</span><span class="p">(</span><br data-jekyll-commonmark-ghpages="" /> <span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="n">opening_stock</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">orders</span><span class="p">))</span> <span class="o">+</span> <span class="n">Decimal</span><span class="p">(</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">deliveries</span><span class="p">))</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">backlog</span><span class="p">)</span> <span class="k">if</span> <span class="n">Decimal</span><span class="p">((</span><span class="n">Decimal</span><span class="p">(</span><span class="n">opening_stock</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">orders</span><span class="p">))</span> <span class="o">+</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">Decimal</span><span class="p">(</span><span class="n">deliveries</span><span class="p">))</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">backlog</span><span class="p">)</span> <span class="o">></span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">0</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">sim_window</span><span class="o">.</span><span class="n">closing_stock</span> <span class="o">=</span> <span class="n">closing_stock</span><span class="p">(</span><span class="n">opening_stock</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">opening_stock</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">orders</span><span class="o">=</span><span class="n">demand</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">deliveries</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">purchase_order_receipt_qty</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">backlog</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">backlog</span><span class="p">)</span></code></pre></figure>
<h3 id="closing-stock">Closing Stock</h3>
<p>The <code class="highlighter-rouge">sim_window.closing stock</code> is the non-negative result of the sum of \( demand \) (orders) and \( backlog \)
subtracted from the sum of \( opening \ stock \) and \( deliveries \). If the result is negative, the absolute value
of the negative closing stock will be added to backlog value.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">sim_window</span><span class="o">.</span><span class="n">closing_stock</span> <span class="o">=</span> <span class="n">closing_stock</span><span class="p">(</span><span class="n">opening_stock</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">opening_stock</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">orders</span><span class="o">=</span><span class="n">demand</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">deliveries</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">purchase_order_receipt_qty</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">backlog</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">backlog</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">closing_stock</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">opening_stock</span><span class="p">,</span> <span class="n">orders</span><span class="p">,</span> <span class="n">deliveries</span><span class="p">,</span> <span class="n">backlog</span><span class="p">:</span> <span class="n">Decimal</span><span class="p">(</span><br data-jekyll-commonmark-ghpages="" /> <span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="n">opening_stock</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">orders</span><span class="p">))</span> <span class="o">+</span> <span class="n">Decimal</span><span class="p">(</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">deliveries</span><span class="p">))</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">backlog</span><span class="p">)</span> <span class="k">if</span> <span class="n">Decimal</span><span class="p">((</span><span class="n">Decimal</span><span class="p">(</span><span class="n">opening_stock</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">orders</span><span class="p">))</span> <span class="o">+</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">Decimal</span><span class="p">(</span><span class="n">deliveries</span><span class="p">))</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">backlog</span><span class="p">)</span> <span class="o">></span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">0</span></code></pre></figure>
<h3 id="backlog">Backlog</h3>
<p>The current backlog for any given period is calculated as the absolute value of \( (demand + opening \ stock) - orders \),
if the result is less than 0. The following lambda expression is used to assign a value to <code class="highlighter-rouge">sim_window.backlog</code> in
the <code class="highlighter-rouge">build_window</code>.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">backlog</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">opening_stock</span><span class="p">,</span> <span class="n">deliveries</span><span class="p">,</span> <span class="n">demand</span><span class="p">:</span> <span class="n">Decimal</span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><br data-jekyll-commonmark-ghpages="" /><span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="n">opening_stock</span> <span class="o">+</span> <span class="n">deliveries</span><span class="p">))</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">demand</span><span class="p">)))</span> <span class="k">if</span> \<br data-jekyll-commonmark-ghpages="" /><span class="n">Decimal</span><span class="p">((</span><span class="n">opening_stock</span> <span class="o">+</span> <span class="n">deliveries</span><span class="p">))</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">demand</span><span class="p">)</span> <span class="o"><</span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">0</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">sim_window</span><span class="o">.</span><span class="n">backlog</span> <span class="o">=</span> <span class="n">backlog</span><span class="p">(</span><span class="n">opening_stock</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">opening_stock</span><span class="p">,</span> <span class="n">deliveries</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">purchase_order_receipt_qty</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">demand</span><span class="o">=</span><span class="n">demand</span><span class="p">)</span> <span class="o">+</span> <span class="n">previous_backlog</span></code></pre></figure>
<h3 id="shortages">Shortages</h3>
<p>Shortages in units are quantity of unmet demand in a given period; excluding earlier backlog.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"> <span class="n">shortages</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">opening_stock</span><span class="p">,</span> <span class="n">orders</span><span class="p">,</span> <span class="n">deliveries</span><span class="p">:</span> <span class="nb">abs</span><span class="p">(</span><br data-jekyll-commonmark-ghpages="" /> <span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="n">opening_stock</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">orders</span><span class="p">))</span> <span class="o">+</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">deliveries</span><span class="p">))</span> <span class="k">if</span> \<br data-jekyll-commonmark-ghpages="" /> <span class="p">((</span><span class="n">Decimal</span><span class="p">(</span><span class="n">opening_stock</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">orders</span><span class="p">))</span> <span class="o">+</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">deliveries</span><span class="p">))</span> <span class="o"><</span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">0</span><br data-jekyll-commonmark-ghpages="" /> <br data-jekyll-commonmark-ghpages="" /> <span class="n">sim_window</span><span class="o">.</span><span class="n">shortage_units</span> <span class="o">=</span> <span class="n">shortages</span><span class="p">(</span><span class="n">opening_stock</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">opening_stock</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">orders</span><span class="o">=</span><span class="n">demand</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">deliveries</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">purchase_order_receipt_qty</span><span class="p">)</span></code></pre></figure>
<h3 id="purchase-order-quantity">Purchase order quantity</h3>
<p>The amount raised on the purchase order, is based on the EOQ and lead time demand. In future, the purchase order amount
raised will use the forecasting method used. More information will be available, like simulated forecast accuracy,
when implemented in the simulation. Currently, the lambda function assigning the value looks like:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">po_qty</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">eoq</span><span class="p">,</span> <span class="n">reorder_lvl</span><span class="p">,</span> <span class="n">backlog</span><span class="p">,</span> <span class="n">cls_stock</span><span class="p">:</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">eoq</span><span class="p">)</span> <span class="o">+</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">backlog</span><span class="p">)</span> <span class="o">+</span> <span class="n">Decimal</span><span class="p">(</span><br data-jekyll-commonmark-ghpages="" /> <span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="n">reorder_lvl</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">cls_stock</span><span class="p">)))</span> <span class="k">if</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">eoq</span><span class="p">)</span> <span class="o">+</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">backlog</span><span class="p">)</span> <span class="o">+</span> <span class="n">Decimal</span><span class="p">(</span><br data-jekyll-commonmark-ghpages="" /> <span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="n">reorder_lvl</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">cls_stock</span><span class="p">)))</span> <span class="o">></span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">0</span><br data-jekyll-commonmark-ghpages="" /> <br data-jekyll-commonmark-ghpages="" /><span class="n">po_qty_raised</span> <span class="o">=</span> <span class="n">po_qty</span><span class="p">(</span><span class="n">eoq</span><span class="o">=</span><span class="n">sku</span><span class="o">.</span><span class="n">economic_order_qty</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_lvl</span><span class="o">=</span><span class="n">sku</span><span class="o">.</span><span class="n">reorder_level</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">backlog</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">backlog</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">cls_stock</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">closing_stock</span><span class="p">)</span></code></pre></figure>
<p>When a purchase order amount is above zero, a purchase order is raised. Logging the makes it easier to follow the transactions over the simulated period.</p>
<h3 id="units-sold">Units Sold</h3>
<p>To calculate the revenue generated the units sold for each period are calculate. Oversight in this release means that
the retail price is an omitted parameter, only the taking unit cost. The <code class="highlighter-rouge">_units_sold</code> method,
is a private attribute.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">units_sold</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_units_sold</span><span class="p">(</span><span class="n">backlog</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">backlog</span><span class="p">,</span> <span class="n">opening_stock</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">opening_stock</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">delivery</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">purchase_order_receipt_qty</span><span class="p">,</span> <span class="n">demand</span><span class="o">=</span><span class="n">sim_window</span><span class="o">.</span><span class="n">demand</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <br data-jekyll-commonmark-ghpages="" /> <br data-jekyll-commonmark-ghpages="" /> <span class="k">def</span> <span class="nf">_units_sold</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">backlog</span><span class="p">,</span> <span class="n">opening_stock</span><span class="p">,</span> <span class="n">delivery</span><span class="p">,</span> <span class="n">demand</span><span class="p">):</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="c"># check if opening_stock + closing_stock = 0</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">if</span> <span class="nb">int</span><span class="p">(</span><span class="n">opening_stock</span><span class="p">)</span> <span class="o">+</span> <span class="nb">int</span><span class="p">(</span><span class="n">delivery</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">sold</span> <span class="o">=</span> <span class="mf">0.00</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">else</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">sold</span> <span class="o">=</span> <span class="p">(</span><span class="n">opening_stock</span> <span class="o">+</span> <span class="n">delivery</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">demand</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">backlog</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <br data-jekyll-commonmark-ghpages="" /> <span class="k">if</span> <span class="n">sold</span> <span class="o"><</span> <span class="mi">0</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">units_sold</span> <span class="o">=</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">demand</span><span class="p">)</span> <span class="o">+</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">backlog</span><span class="p">)</span> <span class="o">-</span> <span class="n">Decimal</span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="n">sold</span><span class="p">))</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">else</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">units_sold</span> <span class="o">=</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">sold</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <br data-jekyll-commonmark-ghpages="" /> <span class="k">return</span> <span class="n">units_sold</span></code></pre></figure>
<h2 id="conclusion">Conclusion</h2>
<p>All releases so far have been planning releases. There are still issues surrounding implementation and speed.
Using Cython for some of the EOQ and simulation logic helped to speed things up. Python is not normally the go-to
language of choice for programming simulations, so ultimately there is a trade-off. Some C++ wrappers are in development.
However, Python is preferable if more Pythonic code and better implementation can reduce the bottlenecks.</p>
<p>Release-0.0.3 will not have a deep-dive. The new package was a result of testing for cross platform compilation of the
Cython modules, on Windows, Mac, and Linux. To target a Windows machine compile the library from source using:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">python setup.py build_ext <span class="nt">-i</span></code></pre></figure>
<p>A second post, continuing the release-0.0.2 deep dive, will continue with the <code class="highlighter-rouge">summarize_window</code>, <code class="highlighter-rouge">summarise.frame</code>
and <code class="highlighter-rouge">optimise_service_level</code> methods.</p>Release 0.0.2 sees the introduction of the Monte Carlo simulation as a feature of the library. Using historical data supplied from a .csv, probable transactions are simulated. The simulation uses normally distributed random demand, to decrement stock over a given period. Inventory movement trigger consumption of stock and affect the transactions for opening stock, closing stock, backlog and purchase orders.Release 0.0.1 Deep Dive2016-04-16T00:00:00+00:002016-04-16T00:00:00+00:00http://supplybi.github.io/supplybi.github.io/supplybi.github.io/2016/04/16/Release-0.0.1<div class="well container-fluid well-sm ">
<p>Deep dives will be a recurring post, for every release with a substantial feature update. The purpose of these deep
dives, is to assist developers seeking to contribute. The hope is that by explaining the library internals in detail,
warts and all, a contributor can better focus their contribution.</p>
</div>
<!--more -->
<p>The core api can be found in the namespaces:</p>
<ul>
<li>demand</li>
<li>production</li>
<li>inventory</li>
<li>simulation</li>
<li>distribution</li>
</ul>
<p>The core codebase is non-public. The auxiliary functions and API can be found in the modules like <code class="highlighter-rouge">model_inventory</code>
which contains the <code class="highlighter-rouge">analyse_orders_from_file_col</code>, <code class="highlighter-rouge">analyse_orders</code> etc. Please see the official <a href="http://supplychainpy.readthedocs.org/en/latest/py-modindex.html">documentation</a>
for further information.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>├── __init__.py
├── __init__.pyc
├── data.csv
├── data_cleansing.py
├── data_col.csv
├── demand
│ ├── __init__.py
│ ├── __init__.pyc
│ ├── __pycache__
│ │ ├── __init__.cpython-35.pyc
│ │ ├── abc_xyz.cpython-35.pyc
│ │ ├── analyse_uncertain_demand.cpython-35.pyc
│ │ ├── economic_order_quantity.cpython-35.pyc
│ │ ├── forecast_demand.cpython-35.pyc
│ │ ├── summarise_analysis.cpython-35.pyc
│ │ └── summarise_demand.cpython-35.pyc
│ ├── abc_xyz.py
│ ├── abc_xyz.pyc
│ ├── analyse_uncertain_demand.py
│ ├── economic_order_quantity.py
│ ├── economic_order_quantity.pyc
│ ├── eoq.c
│ ├── eoq.cp35-win_amd64.pyd
│ ├── eoq.cpython-35m-darwin.so
│ ├── eoq.cpython-35m-x86_64-linux-gnu.so
│ ├── eoq.pyx
│ ├── forecast_demand.py
│ ├── summarise_analysis.py
│ └── summarise_demand.py
├── model_demand.py
├── model_distribution.py
├── model_inventory.py
├── model_production.py
├── model_warehouse.py
├── orders_analysis.txt
</code></pre></div></div>
<h1 id="analyse-orders">Analyse Orders</h1>
<p>Inventory analysis requires a <em>.csv</em> file, an example of the format is
<a href="https://raw.githubusercontent.com/KevinFasusi/supplychainpy/master/supplychainpy/data.csv">here</a>. In this example the
<em>.csv</em> file contains the monthly demand for 32 stock keeping units (SKUs), over a 12 month period. The function <code class="highlighter-rouge">analyse_orders_abcxyz_from_file</code> in
the <code class="highlighter-rouge">model_inventory</code> module is used to analyse the inventory profile.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">supplychainpy.model_inventory</span> <span class="kn">import</span> <span class="n">analyse_orders_abcxyz_from_file</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">orders_transactions</span> <span class="o">=</span> <span class="n">analyse_orders_abcxyz_from_file</span><span class="p">(</span><span class="n">file_path</span><span class="o">=</span><span class="s">"data.csv"</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">z_value</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="mf">1.28</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_cost</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="mi">5000</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">file_type</span><span class="o">=</span><span class="s">"csv"</span><span class="p">)</span></code></pre></figure>
<p>The function returns an <code class="highlighter-rouge">ABCXYZ</code> object which also contains an <code class="highlighter-rouge">orders attribute</code> which is a list of <code class="highlighter-rouge">UncertainDemand</code>
objects. These objects contain the summary for each individual sku, while the <code class="highlighter-rouge">ABCXYZ</code> object contains the summary
analysis for the whole inventory profile. The attributes for <code class="highlighter-rouge">ABCXYZ</code> can be seen in the Pycharm debugger.</p>
<p><img src="/supplybi.github.io/images/orders_analysis_variables.png" alt="orders analysis variables" /></p>
<p>Retrieving the summary from this method is not consistent with the rest of the library. There are currently two ways
to retrieve the summary analysis for each SKU (there can only be <a href="https://youtu.be/SnJt9p-sHho?t=44s">one</a> :-p). The first involves using
the objects public instance variables:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">supplychainpy.model_inventory</span> <span class="kn">import</span> <span class="n">analyse_orders_abcxyz_from_file</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">abc</span> <span class="o">=</span> <span class="n">analyse_orders_abcxyz_from_file</span><span class="p">(</span><span class="n">file_path</span><span class="o">=</span><span class="s">"data.csv"</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">z_value</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="mf">1.28</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_cost</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="mi">5000</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">file_type</span><span class="o">=</span><span class="s">"csv"</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="k">for</span> <span class="n">sku</span> <span class="ow">in</span> <span class="n">abc</span><span class="o">.</span><span class="n">orders</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">print</span><span class="p">(</span><span class="s">"Sku: {} "</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"Economic Order Quantity: {:.0f}"</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">" Sku Revenue: {:.0f} "</span><br data-jekyll-commonmark-ghpages="" /> <span class="s">"ABCXYZ Classification: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sku</span><span class="o">.</span><span class="n">sku_id</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">sku</span><span class="o">.</span><span class="n">economic_order_qty</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">sku</span><span class="o">.</span><span class="n">revenue</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">sku</span><span class="o">.</span><span class="n">abcxyz_classification</span><span class="p">))</span></code></pre></figure>
<p>resulting in:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Sku: KR202-209 Economic Order Quantity: 1311 Sku Revenue: 6942800 ABCXYZ Classification: CZ
Sku: KR202-210 Economic Order Quantity: 1405 Sku Revenue: 7900000 ABCXYZ Classification: CY
Sku: KR202-211 Economic Order Quantity: 1224 Sku Revenue: 6900000 ABCXYZ Classification: CZ
Sku: KR202-212 Economic Order Quantity: 1317 Sku Revenue: 10000000 ABCXYZ Classification: BY
Sku: KR202-213 Economic Order Quantity: 981 Sku Revenue: 6700000 ABCXYZ Classification: CY
Sku: KR202-214 Economic Order Quantity: 1170 Sku Revenue: 10000000 ABCXYZ Classification: BY
</code></pre></div></div>
<div class="well container-fluid well-sm">
<p>A glaring oversight is apparent as of this post. The revenue value is generated using the unit_cost and not a value representive of
a standard or retail price. This will be changed in release-0.0.4</p>
</div>
<p>The second way is by iterating over the orders list and calling the <code class="highlighter-rouge">UncertainDemand.orders_summary</code> method.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">supplychainpy.model_inventory</span> <span class="kn">import</span> <span class="n">analyse_orders_abcxyz_from_file</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="n">abc</span> <span class="o">=</span> <span class="n">analyse_orders_abcxyz_from_file</span><span class="p">(</span><span class="n">file_path</span><span class="o">=</span><span class="s">"data.csv"</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">z_value</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="mf">1.28</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_cost</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="mi">5000</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">file_type</span><span class="o">=</span><span class="s">"csv"</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><span class="k">for</span> <span class="n">sku</span> <span class="ow">in</span> <span class="n">abc</span><span class="o">.</span><span class="n">orders</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">print</span><span class="p">(</span><span class="n">sku</span><span class="o">.</span><span class="n">orders_summary</span><span class="p">())</span></code></pre></figure>
<p>resulting in:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{'ABC_XYZ_Classification': 'AX', 'reorder_quantity': '258', 'revenue': '2090910.44',
'average_order': '539', 'reorder_level': '813', 'economic_order_quantity': '277', 'sku': 'RR381-33',
'demand_variability': '0.052', 'economic_order_variable_cost': '29557.61',
'standard_deviation': '28', 'safety_stock': '51'}
</code></pre></div></div>
<p>In release-0.0.4 this will be resolved, as holding on to the object will incur a cost in performance at scale.</p>
<p>The formulas for the calculations are <a href="http://supplychainpy.readthedocs.org/en/latest/calculations.html">here</a>.
The doc string for the class and functions are <a href="http://supplychainpy.readthedocs.org/en/latest/supplychainpy.demand.html#module-supplychainpy.demand.analyse_uncertain_demand">here</a>.
Most of the methods in the <code class="highlighter-rouge">UncertainDemand</code> class are non-public, as signified by the leading underscore.
The constructor for <code class="highlighter-rouge">UncertainDemand</code> does all the work, along with the <code class="highlighter-rouge">EconomicOrderQuantity</code> and <code class="highlighter-rouge">ABCXYZ</code> class.
All three are instantiated and called from within the <code class="highlighter-rouge">analyse_orders_abcxyz_from_file</code> function.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">analyse_orders_abcxyz_from_file</span><span class="p">(</span><span class="n">file_path</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">z_value</span><span class="p">:</span> <span class="n">Decimal</span><span class="p">,</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_cost</span><span class="p">:</span> <span class="n">Decimal</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">file_type</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">FileFormats</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">name</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">period</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s">"month"</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">length</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">12</span><span class="p">)</span> <span class="o">-></span> <span class="n">AbcXyz</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <br data-jekyll-commonmark-ghpages="" /> <span class="n">analysed_orders_collection</span> <span class="o">=</span> <span class="p">[]</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">item_list</span> <span class="o">=</span> <span class="p">{}</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="k">if</span> <span class="n">_check_extension</span><span class="p">(</span><span class="n">file_path</span><span class="o">=</span><span class="n">file_path</span><span class="p">,</span> <span class="n">file_type</span><span class="o">=</span><span class="n">file_type</span><span class="p">):</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">if</span> <span class="n">file_type</span> <span class="o">==</span> <span class="n">FileFormats</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">name</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">,</span> <span class="s">'r'</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">item_list</span> <span class="o">=</span> <span class="p">(</span><span class="n">data_cleansing</span><span class="o">.</span><span class="n">clean_orders_data_row</span><span class="p">(</span><span class="n">f</span><span class="p">))</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">elif</span> <span class="n">file_type</span> <span class="o">==</span> <span class="n">FileFormats</span><span class="o">.</span><span class="n">csv</span><span class="o">.</span><span class="n">name</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">item_list</span> <span class="o">=</span> <span class="n">data_cleansing</span><span class="o">.</span><span class="n">clean_orders_data_row_csv</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">length</span><span class="o">=</span><span class="n">length</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">else</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">raise</span> <span class="nb">Exception</span><span class="p">(</span><span class="s">"Incorrect file type specified."</span> <br data-jekyll-commonmark-ghpages="" /> <span class="s">"Please specify 'csv' or 'text' for the file_type parameter."</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="k">for</span> <span class="n">sku</span> <span class="ow">in</span> <span class="n">item_list</span><span class="p">:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">orders</span> <span class="o">=</span> <span class="p">{}</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">sku_id</span><span class="p">,</span> <span class="n">unit_cost</span><span class="p">,</span> <span class="n">lead_time</span> <span class="o">=</span> <span class="n">sku</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">"sku id"</span><span class="p">),</span> <span class="n">sku</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">"unit cost"</span><span class="p">),</span> <span class="n">sku</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">"lead time"</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">orders</span><span class="p">[</span><span class="s">'demand'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sku</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">"demand"</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">total_orders</span> <span class="o">=</span> <span class="mi">0</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">for</span> <span class="n">order</span> <span class="ow">in</span> <span class="n">orders</span><span class="p">[</span><span class="s">'demand'</span><span class="p">]:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">total_orders</span> <span class="o">+=</span> <span class="nb">int</span><span class="p">(</span><span class="n">order</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">analysed_orders</span> <span class="o">=</span> <span class="n">analyse_uncertain_demand</span><span class="o">.</span><span class="n">UncertainDemand</span><span class="p">(</span><span class="n">orders</span><span class="o">=</span><span class="n">orders</span><span class="p">,</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">sku</span><span class="o">=</span><span class="n">sku_id</span><span class="p">,</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">lead_time</span><span class="o">=</span><span class="n">lead_time</span><span class="p">,</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">unit_cost</span><span class="o">=</span><span class="n">unit_cost</span><span class="p">,</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_cost</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="n">reorder_cost</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">z_value</span><span class="o">=</span><span class="n">Decimal</span><span class="p">(</span><span class="n">z_value</span><span class="p">))</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">average_orders</span> <span class="o">=</span> <span class="n">analysed_orders</span><span class="o">.</span><span class="n">average_orders</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_quantity</span> <span class="o">=</span> <span class="n">analysed_orders</span><span class="o">.</span><span class="n">fixed_order_quantity</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">eoq</span> <span class="o">=</span> <span class="n">economic_order_quantity</span><span class="o">.</span><span class="n">EconomicOrderQuantity</span><span class="p">(</span><span class="n">total_orders</span><span class="o">=</span><span class="nb">float</span><span class="p">(</span><span class="n">total_orders</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_quantity</span><span class="o">=</span><span class="nb">float</span><span class="p">(</span><span class="n">reorder_quantity</span><span class="p">),</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">holding_cost</span><span class="o">=</span><span class="nb">float</span><span class="p">(</span><span class="mf">0.25</span><span class="p">),</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">reorder_cost</span><span class="o">=</span><span class="nb">float</span><span class="p">(</span><span class="n">reorder_cost</span><span class="p">),</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">average_orders</span><span class="o">=</span><span class="n">average_orders</span><span class="p">,</span> <br data-jekyll-commonmark-ghpages="" /> <span class="n">unit_cost</span><span class="o">=</span><span class="nb">float</span><span class="p">(</span><span class="n">unit_cost</span><span class="p">))</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">analysed_orders</span><span class="o">.</span><span class="n">economic_order_qty</span> <span class="o">=</span> <span class="n">eoq</span><span class="o">.</span><span class="n">economic_order_quantity</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">analysed_orders</span><span class="o">.</span><span class="n">economic_order_variable_cost</span> <span class="o">=</span> <span class="n">eoq</span><span class="o">.</span><span class="n">minimum_variable_cost</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">analysed_orders_collection</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">analysed_orders</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="k">del</span> <span class="n">analysed_orders</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">del</span> <span class="n">eoq</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">del</span> <span class="n">sku</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">abc</span> <span class="o">=</span> <span class="n">AbcXyz</span><span class="p">(</span><span class="n">analysed_orders_collection</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">abc</span><span class="o">.</span><span class="n">percentage_revenue</span><span class="p">()</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">abc</span><span class="o">.</span><span class="n">cumulative_percentage_revenue</span><span class="p">()</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">abc</span><span class="o">.</span><span class="n">abc_classification</span><span class="p">()</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">abc</span><span class="o">.</span><span class="n">xyz_classification</span><span class="p">()</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">a</span> <span class="o">=</span> <span class="n">summarise_demand</span><span class="o">.</span><span class="n">AnalyseOrdersSummary</span><span class="p">(</span><span class="n">abc</span><span class="o">.</span><span class="n">orders</span><span class="p">)</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">abc</span><span class="o">.</span><span class="n">abcxyz_summary</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">classification_summary</span><span class="p">()</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">return</span> <span class="n">abc</span></code></pre></figure>
<div class="well container-fluid well-sm">
<p>The naming conventions used for the <strong>analyse</strong> methods are cumbersome <strong>analyse_orders_abcxyz_from_file</strong>,
for example, is not beautiful (as is mandated by Pythonistas across the globe). In release 0.0.4 these will be changing to
<strong>describe_inventory</strong> I like the <em>describe</em> convention, it is also found in Julia where
the result is a gamut of descriptive statistics.</p>
</div>Deep dives will be a recurring post, for every release with a substantial feature update. The purpose of these deep dives, is to assist developers seeking to contribute. The hope is that by explaining the library internals in detail, warts and all, a contributor can better focus their contribution.