.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "auto_examples/113_partition_models.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        Click :ref:`here <sphx_glr_download_auto_examples_113_partition_models.py>`
        to download the full example code

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_auto_examples_113_partition_models.py:


Partition Models
================

Partition models, (a.k.a. partition curves) define the separation of a unit operation / process.

In the one dimensional case, the Partition Number (PN) is represents the probability that a particle will
report to the defined reference stream.

Consider a desliming cyclone that aims to separate a slurry at 150 micron.  The reference stream is defined as
the Underflow (UF), since that is the "stream of value" in our simple example.

..  Admonition:: TODO

    Add a reference to partition curves.

.. GENERATED FROM PYTHON SOURCE LINES 18-34

.. code-block:: default

    from functools import partial

    import numpy as np
    import pandas as pd
    import plotly
    import plotly.graph_objects as go
    from scipy.interpolate import PchipInterpolator

    from elphick.mass_composition import MassComposition
    from elphick.mass_composition.datasets.sample_data import size_by_assay
    from elphick.mass_composition.flowsheet import Flowsheet
    from elphick.mass_composition.utils.partition import napier_munn
    from elphick.mass_composition.utils.pd_utils import calculate_partition

    # sphinx_gallery_thumbnail_number = -1








.. GENERATED FROM PYTHON SOURCE LINES 35-39

Create a mass-composition object
--------------------------------

We get some demo data in the form of a pandas DataFrame

.. GENERATED FROM PYTHON SOURCE LINES 40-45

.. code-block:: default


    df_data: pd.DataFrame = size_by_assay()
    mc_feed: MassComposition = MassComposition(df_data, name='size sample')
    print(mc_feed)





.. rst-class:: sphx-glr-script-out

 .. code-block:: none


    size sample
    <xarray.Dataset> Size: 336B
    Dimensions:   (size: 6)
    Coordinates:
      * size      (size) object 48B [0.85, 2.0) [0.5, 0.85) ... [0.0, 0.045)
    Data variables:
        mass_wet  (size) float64 48B 3.3 9.9 26.5 2.5 8.8 49.0
        mass_dry  (size) float64 48B 3.3 9.9 26.5 2.5 8.8 49.0
        H2O       (size) float64 48B 0.0 0.0 0.0 0.0 0.0 0.0
        Fe        (size) float64 48B 64.15 64.33 64.52 62.65 62.81 55.95
        SiO2      (size) float64 48B 2.04 2.05 1.84 2.88 2.12 6.39
        Al2O3     (size) float64 48B 2.68 2.23 2.19 3.32 2.25 6.34
    Attributes:
        mc_name:            size sample
        mc_vars_mass:       ['mass_wet', 'mass_dry']
        mc_vars_chem:       ['Fe', 'SiO2', 'Al2O3']
        mc_vars_attrs:      []
        mc_interval_edges:  {'size': {'left': 'retained', 'right': 'passing'}}




.. GENERATED FROM PYTHON SOURCE LINES 46-51

Define and Apply the Partition
------------------------------

We partially initialise the partition function
The dim argument is added to inform the split method which dimension to apply the function/split to

.. GENERATED FROM PYTHON SOURCE LINES 51-54

.. code-block:: default


    part_cyclone = partial(napier_munn, d50=0.150, ep=0.1, dim='size')








.. GENERATED FROM PYTHON SOURCE LINES 55-56

Separate the object using the defined partitions.  UF = Underflow, OF = Overflow

.. GENERATED FROM PYTHON SOURCE LINES 56-64

.. code-block:: default


    mc_uf, mc_of = mc_feed.split_by_partition(partition_definition=part_cyclone, name_1='underflow', name_2='overflow')
    fs: Flowsheet = Flowsheet().from_streams([mc_feed, mc_uf, mc_of])

    fig = fs.table_plot(table_pos='left',
                         sankey_color_var='Fe', sankey_edge_colormap='copper_r', sankey_vmin=50, sankey_vmax=70)
    fig






.. raw:: html

    <div class="output_subarea output_html rendered_html output_result">
    <div>                        <script type="text/javascript">window.PlotlyConfig = {MathJaxConfig: 'local'};</script>
            <script charset="utf-8" src="https://cdn.plot.ly/plotly-2.32.0.min.js"></script>                <div id="1f0d95fc-31fe-4812-8b88-82162d6c0fea" class="plotly-graph-div" style="height:100%; width:100%;"></div>            <script type="text/javascript">                                    window.PLOTLYENV=window.PLOTLYENV || {};                                    if (document.getElementById("1f0d95fc-31fe-4812-8b88-82162d6c0fea")) {                    Plotly.newPlot(                        "1f0d95fc-31fe-4812-8b88-82162d6c0fea",                        [{"cells":{"align":"left","fill":{"color":[["whitesmoke","lightgray","whitesmoke","whitesmoke","lightgray","whitesmoke","whitesmoke","lightgray","whitesmoke","whitesmoke","lightgray","whitesmoke","whitesmoke","lightgray","whitesmoke","whitesmoke","lightgray","whitesmoke","whitesmoke","lightgray","whitesmoke"]]},"format":["%s",".0f",".0f",".1f",".2f",".2f",".2f"],"values":[["size sample","underflow","overflow"],[100.0,47.100386539247616,52.89961346075239],[100.0,47.100386539247616,52.89961346075239],[0.0,0.0,0.0],[60.09245000000001,62.59462297332386,57.864582790364324],[4.14753,2.8549562088436793,5.298402779800789],[4.277159999999999,3.099484076163923,5.325730823145969]]},"columnwidth":[2,1,1,1,1,1,1],"header":{"align":"center","fill":{"color":"cornflowerblue"},"font":{"color":"black","size":12},"values":["name","mass_wet","mass_dry","H2O","Fe","SiO2","Al2O3"]},"type":"table","domain":{"x":[0.0,0.36000000000000004],"y":[0.0,1.0]}},{"link":{"color":["rgba(155, 98, 62, 255)","rgba(116, 73, 46, 255)","rgba(191, 121, 77, 255)"],"customdata":["\u003cbr \u002f\u003emass_wet: 100\u003cbr \u002f\u003emass_dry: 100\u003cbr \u002f\u003eH2O: 0.0\u003cbr \u002f\u003eFe: 60.09\u003cbr \u002f\u003eSiO2: 4.15\u003cbr \u002f\u003eAl2O3: 4.28\u003cbr \u002f\u003e","\u003cbr \u002f\u003emass_wet: 47\u003cbr \u002f\u003emass_dry: 47\u003cbr \u002f\u003eH2O: 0.0\u003cbr \u002f\u003eFe: 62.59\u003cbr \u002f\u003eSiO2: 2.85\u003cbr \u002f\u003eAl2O3: 3.10\u003cbr \u002f\u003e","\u003cbr \u002f\u003emass_wet: 53\u003cbr \u002f\u003emass_dry: 53\u003cbr \u002f\u003eH2O: 0.0\u003cbr \u002f\u003eFe: 57.86\u003cbr \u002f\u003eSiO2: 5.30\u003cbr \u002f\u003eAl2O3: 5.33\u003cbr \u002f\u003e"],"hovertemplate":"\u003cb\u003e\u003ci\u003e%{label}\u003c\u002fi\u003e\u003c\u002fb\u003e\u003cbr \u002f\u003eSource: %{source.customdata}\u003cbr \u002f\u003eTarget: %{target.customdata}\u003cbr \u002f\u003e%{customdata}","label":["size sample","underflow","overflow"],"source":[0,1,1],"target":[1,2,3],"value":[100.0,47.100386539247616,52.89961346075239]},"node":{"color":["blue","green","blue","blue"],"customdata":["0","1","2","3"],"label":["0","1","2","3"],"line":{"color":"black","width":0.5},"pad":15,"thickness":20},"type":"sankey","domain":{"x":[0.4600000000000001,1.0],"y":[0.0,1.0]}}],                        {"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"title":{"text":"Flowsheet\u003cbr\u003e\u003csup\u003eBalanced: True, Edge Status OK: True\u003c\u002fsup\u003e"},"font":{"size":12}},                        {"responsive": true}                    )                };                            </script>        </div>
    </div>
    <br />
    <br />

.. GENERATED FROM PYTHON SOURCE LINES 65-66

We'll now get the partition data from the objects

.. GENERATED FROM PYTHON SOURCE LINES 66-70

.. code-block:: default


    df_partition: pd.DataFrame = mc_feed.calculate_partition(ref=mc_uf)
    df_partition





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    /home/runner/work/mass-composition/mass-composition/elphick/mass_composition/mass_composition.py:1386: FutureWarning:

    The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.



.. raw:: html

    <div class="output_subarea output_html rendered_html output_result">
    <div>
    <style scoped>
        .dataframe tbody tr th:only-of-type {
            vertical-align: middle;
        }

        .dataframe tbody tr th {
            vertical-align: top;
        }

        .dataframe thead th {
            text-align: right;
        }
    </style>
    <table border="1" class="dataframe">
      <thead>
        <tr style="text-align: right;">
          <th></th>
          <th>da</th>
          <th>PN</th>
        </tr>
        <tr>
          <th>size</th>
          <th></th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>[0.85, 2.0)</th>
          <td>1.303840</td>
          <td>0.999997</td>
        </tr>
        <tr>
          <th>[0.5, 0.85)</th>
          <td>0.651920</td>
          <td>0.995995</td>
        </tr>
        <tr>
          <th>[0.15, 0.5)</th>
          <td>0.273861</td>
          <td>0.795960</td>
        </tr>
        <tr>
          <th>[0.075, 0.15)</th>
          <td>0.106066</td>
          <td>0.381583</td>
        </tr>
        <tr>
          <th>[0.045, 0.075)</th>
          <td>0.058095</td>
          <td>0.266972</td>
        </tr>
        <tr>
          <th>[0.0, 0.045)</th>
          <td>0.020855</td>
          <td>0.194771</td>
        </tr>
      </tbody>
    </table>
    </div>
    </div>
    <br />
    <br />

.. GENERATED FROM PYTHON SOURCE LINES 71-72

Create an interpolator from the data.  As a Callable, the spline can be used to split a MassComposition object.

.. GENERATED FROM PYTHON SOURCE LINES 72-77

.. code-block:: default


    da = np.linspace(0.01, df_partition.index.right.max(), num=500)
    spline_partition = PchipInterpolator(x=df_partition.sort_index()['da'], y=df_partition.sort_index()['PN'])
    pn_extracted = spline_partition(da)








.. GENERATED FROM PYTHON SOURCE LINES 78-79

Plot the extracted data, and the spline on the input partition curve to visually validate.

.. GENERATED FROM PYTHON SOURCE LINES 79-94

.. code-block:: default


    pn_original = part_cyclone(da) / 100

    fig = go.Figure(go.Scatter(x=da, y=pn_original, name='Input Partition', line=dict(width=5, color='DarkSlateGrey')))
    fig.add_trace(go.Scatter(x=df_partition['da'], y=df_partition['PN'], name='Extracted Partition Data', mode='markers',
                             marker=dict(size=12, color='red', line=dict(width=2, color='DarkSlateGrey'))))
    fig.add_trace(
        go.Scatter(x=da, y=pn_extracted, name='Extracted Partition Curve', line=dict(width=2, color='red', dash='dash')))

    fig.update_xaxes(type="log")
    fig.update_layout(title='Partition Round Trip Check', xaxis_title='da', yaxis_title='PN', yaxis_range=[0, 1.05])

    # noinspection PyTypeChecker
    plotly.io.show(fig)




.. raw:: html
    :file: images/sphx_glr_113_partition_models_001.html





.. GENERATED FROM PYTHON SOURCE LINES 95-98

There are differences in the re-created partition at the coarser sizes.  It would be interesting to
investigate if up-sampling in advance of partition generation would reduce this difference.  Alternatively,
the `napier_munn` parameteric partition function could be fitted to reduce the difference.

.. GENERATED FROM PYTHON SOURCE LINES 100-104

Pandas Function
---------------

The same functionality is available in pandas

.. GENERATED FROM PYTHON SOURCE LINES 104-109

.. code-block:: default


    df_partition_2: pd.DataFrame = mc_feed.data.to_dataframe().pipe(calculate_partition, df_ref=mc_uf.data.to_dataframe(),
                                                                    col_mass_dry='mass_dry')
    df_partition_2






.. raw:: html

    <div class="output_subarea output_html rendered_html output_result">
    <div>
    <style scoped>
        .dataframe tbody tr th:only-of-type {
            vertical-align: middle;
        }

        .dataframe tbody tr th {
            vertical-align: top;
        }

        .dataframe thead th {
            text-align: right;
        }
    </style>
    <table border="1" class="dataframe">
      <thead>
        <tr style="text-align: right;">
          <th></th>
          <th>da</th>
          <th>PN</th>
        </tr>
        <tr>
          <th>size</th>
          <th></th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>[0.85, 2.0)</th>
          <td>1.303840</td>
          <td>0.999997</td>
        </tr>
        <tr>
          <th>[0.5, 0.85)</th>
          <td>0.651920</td>
          <td>0.995995</td>
        </tr>
        <tr>
          <th>[0.15, 0.5)</th>
          <td>0.273861</td>
          <td>0.795960</td>
        </tr>
        <tr>
          <th>[0.075, 0.15)</th>
          <td>0.106066</td>
          <td>0.381583</td>
        </tr>
        <tr>
          <th>[0.045, 0.075)</th>
          <td>0.058095</td>
          <td>0.266972</td>
        </tr>
        <tr>
          <th>[0.0, 0.045)</th>
          <td>0.020855</td>
          <td>0.194771</td>
        </tr>
      </tbody>
    </table>
    </div>
    </div>
    <br />
    <br />

.. GENERATED FROM PYTHON SOURCE LINES 110-111

.. code-block:: default

    pd.testing.assert_frame_equal(df_partition, df_partition_2)








.. rst-class:: sphx-glr-timing

   **Total running time of the script:** ( 0 minutes  0.496 seconds)


.. _sphx_glr_download_auto_examples_113_partition_models.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example


    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: 113_partition_models.py <113_partition_models.py>`

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: 113_partition_models.ipynb <113_partition_models.ipynb>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_