Mineral Endmember Decomposition
A common task when working with mineral chemistry data is to take measured compositions and decompose these into relative proportions of mineral endmember compositions. pyrolite includes some utilities to achieve this and a limited mineral database for looking up endmember compositions.
import numpy as np
import pandas as pd
from pyrolite.mineral.mindb import get_mineral
from pyrolite.mineral.normative import endmember_decompose
First we’ll start with a composition of an unknown olivine:
We can break this down into olivine endmebmers using the
endmember_decompose()
function:
ed = endmember_decompose(
pd.DataFrame(comp).T, endmembers="olivine", order=1, molecular=True
)
ed
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[0.57294068 nan nan 0.10480888]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
_df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[0.42705932 0.29485884 0.29750712 0.31249035]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
_df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[ nan 0.70514116 nan nan]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
_df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
Equally, if you knew the likely endmembers beforehand, you could specify a list of endmembers:
ed = endmember_decompose(
pd.DataFrame(comp).T, endmembers=["forsterite", "fayalite"], order=1, molecular=True
)
ed
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[0.57294068 nan]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
_df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[0.42705932 0.29485884]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
_df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[ nan 0.70514116]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
_df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
We can check this by recombining the components with these proportions. We can first lookup the compositions for our endmembers:
em = pd.DataFrame([get_mineral("forsterite"), get_mineral("fayalite")])
em.loc[:, ~(em == 0).all(axis=0)] # columns not full of zeros
First we have to convert these element-based compositions to oxide-based compositions:
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[66.66666667 nan]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
_df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[33.33333333 33.33333333]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
_df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[ nan 66.66666667]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
_df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
These can now be used with our endmember proportions to regenerate a composition:
recombined = pd.DataFrame(ed.values.flatten() @ emvalues).T.pyrochem.to_weight()
recombined
To make sure these compositions are within 0.01 percent:
assert np.allclose(recombined.values, comp.values, rtol=10**-4)
Total running time of the script: (0 minutes 0.214 seconds)