-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AIOCG plots #57
base: develop
Are you sure you want to change the base?
AIOCG plots #57
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"name":"AIOCG","axes":{"x":"(2Ca+5Fe+2Mn)+(2Ca+5Fe+2Mn+Mg+Si) molar","y":"K/(K+Na+0.5Ca) molar"},"fields":{"K":{"name":["K"],"poly":[[0,405.2],[103.2,405.2],[109.4,244.1],[51.6,235.0],[39.6,222.8],[45.6,194.4],[0,194.4]]},"KintNa":{"name":[""],"poly":[[0,194.4],[0,121.5],[145.7,121.5],[54.7,176.2],[45.6,194.4]]},"Na":{"name":["Na"],"poly":[[0,121.5],[145.7,121.5],[145.7,32.4],[157.9,0.0],[0,0.0]]},"NotAltered1":{"name":[""],"poly":[[291.6,145.9],[221.7,72.5],[294.7,30.5],[346.3,44.6],[352.3,54.7],[346.3,73.0],[334.1,101.3],[315.8,117.6]]},"NotAltered2":{"name":[""],"poly":[[291.6,145.9],[221.7,72.5],[294.7,30.5],[346.3,44.6],[352.3,54.7],[346.3,73.0],[334.1,101.3],[315.8,117.6]]},"Na-Ca-Fe-(Mg)":{"name":["Na-Ca-Fe-(Mg)"],"poly":[[145.7,121.5],[145.7,32.4],[157.9,0.0],[413,0.0],[400.8,48.7],[367.4,77.1],[352.3,117.6],[315.8,117.6],[334.1,101.3],[346.3,73.0],[352.3,54.7],[346.3,44.6],[294.7,30.5],[206.6,81.1]]},"KintK-Fe":{"name":[""],"poly":[[103.2,405.2],[109.4,244.1],[115.4,245.1],[197.5,223.5],[200.4,275.6],[194.4,405.2]]},"K-Fe":{"name":["K-Fe"],"poly":[[194.4,405.2],[200.4,275.6],[197.5,223.5],[200.4,222.8],[437.2,222.8],[431.2,328.1],[413,364.6],[413,405.2]]},"Ca-K-Fe":{"name":["Ca-K-Fe"],"poly":[[200.4,222.8],[291.6,145.9],[315.8,117.6],[352.3,117.6],[443.5,117.6],[437.2,222.8]]},"Ca-Fe-(Mg)":{"name":["Ca-Fe-(Mg)"],"poly":[[352.3,117.6],[367.4,77.1],[400.8,48.7],[413,0.0],[461.7,0.0],[445.4,70.8],[443.5,117.6]]},"Fe-rich_Ca-Fe":{"name":["Fe-rich Ca-Fe"],"poly":[[443.5,117.6],[445.4,70.8],[461.7,0.0],[607.4,0.0],[607.4,117.6]]},"Fe-rich_Ca-K-Fe":{"name":["Fe-rich Ca-K-Fe"],"poly":[[437.2,222.8],[443.5,117.6],[607.4,117.6],[607.4,222.8]]},"Fe-rich_K-Fe":{"name":["Fe-rich K-Fe"],"poly":[[413,405.2],[413,364.6],[431.2,328.1],[437.2,222.8],[607.4,222.8],[607.4,405.2]]}}} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,3 +127,26 @@ def WIP(df: pd.DataFrame): | |
|
||
""" | ||
return 2 * df.Na2O / 0.35 + df.MgO / 0.9 + 2 * df.K2O / 0.25 + df.CaO / 0.7 | ||
|
||
def AIOCG_xy(df: pd.DataFrame): | ||
NicolasPietteLauziere marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I think keeping the name to |
||
""" | ||
|
||
Alteration diagram coordinates for iron oxide-copper-gold mineralisation | ||
system. (molecular) [#ref_1]_ | ||
Returns X,Y coordinates to plot in discriminant diagram scaled to the | ||
400x600 dimension of the background | ||
|
||
References | ||
---------- | ||
.. [#ref_1] Montreuil J F, Corriveau L, Grunsky E C (2013). Compositional | ||
data analysis of hydrothermal alteration in IOCG systems, Great | ||
Bear magmatic zone, Canada: to each alteration type its own | ||
geochemical signature. Geochemistry: Exploration, Environment, | ||
Analysis 13:229-247. | ||
doi:`<http://dx.doi.org/10.1144/geochem2011-101>`__ | ||
""" | ||
x = (2*df.Ca+5*df.Fe+2*df.Mn)/(2*df.Ca+5*df.Fe+2*df.Mn+df.Mg+df.Si)*600 | ||
y = df.K/(df.K+df.Na+0.5*df.Ca)*400 | ||
return x, y | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
from ...util.plot.axes import init_axes | ||
from ...util.classification import AIOCG as AIOCGclassifier | ||
NicolasPietteLauziere marked this conversation as resolved.
Show resolved
Hide resolved
|
||
from ...util.meta import sphinx_doi_link, update_docstring_references, subkwargs | ||
from ...util.log import Handle | ||
|
||
logger = Handle(__name__) | ||
|
||
|
||
@update_docstring_references | ||
def AIOCG(ax=None, relim=True, color="k", **kwargs): | ||
""" | ||
Adds the AIOCG diagram from Montreuil et al. (2013) [#ref_1]_ to an axes. | ||
NOTE to user: | ||
x:y are scaled from 1:1 to 600:400 to account for plot dimension | ||
|
||
|
||
Parameters | ||
---------- | ||
ax : :class:`matplotlib.axes.Axes` | ||
Axes to add the template on to. | ||
relim : :class:`bool` | ||
Whether to relimit axes to fit the built in ranges for this diagram. | ||
color : :class:`str` | ||
Line color for the diagram. | ||
|
||
Returns | ||
------- | ||
ax : :class:`matplotlib.axes.Axes` | ||
|
||
References | ||
----------- | ||
.. [#ref_1] Montreuil J F, Corriveau L, and Potter E G (2015). Formation of | ||
albitite-hosted uranium within IOCG systems: the Southern Breccia, | ||
Great Bear magmatic zone, Northwest Territories, Canada. | ||
Mineralium Deposita, 50:293-325. | ||
doi:`<https://doi.org/10.1007/s00126-014-0530-7>`__ | ||
""" | ||
AIOCG_xlim, AIOCG_ylim = (0, 607.4), (0, 405.2) | ||
if ax is None: | ||
xlim, ylim = AIOCG_xlim, AIOCG_ylim | ||
else: | ||
# if the axes limits are not defaults, update to reflect the axes | ||
ax_defaults = (0, 1) | ||
ax_xlim, ax_ylim = ax.get_xlim(), ax.get_ylim() | ||
xlim, ylim = ( | ||
[ax_xlim, AIOCG_xlim][np.allclose(ax_xlim, ax_defaults)], | ||
[ax_ylim, AIOCG_ylim][np.allclose(ax_ylim, ax_defaults)], | ||
) | ||
ax = init_axes(ax=ax, **kwargs) | ||
|
||
aiocg = AIOCGclassifier() | ||
NicolasPietteLauziere marked this conversation as resolved.
Show resolved
Hide resolved
|
||
aiocg.add_to_axes(ax=ax, **kwargs) | ||
if relim: | ||
ax.set_xlim(xlim) | ||
ax.set_ylim(ylim) | ||
return ax | ||
|
||
|
||
AIOCG.__doc__ = AIOCG.__doc__.format(Montreuil2013=sphinx_doi_link("10.1007/s00126-014-0530-7")) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -289,6 +289,110 @@ def add_to_axes(self, ax=None, fill=False, axes_scale=100.0, labels=None, **kwar | |
ax.set_xlabel("$SiO_2$") | ||
return ax | ||
|
||
class AIOCG(PolygonClassifier): | ||
""" | ||
AIOCG Diagram classifier from Montreuil et al. (2013) [#ref_1]_. | ||
|
||
Parameters | ||
----------- | ||
name : :class:`str` | ||
A name for the classifier model. | ||
axes : :class:`list` | :class:`tuple` | ||
Names of the axes corresponding to the polygon coordinates. | ||
fields : :class:`dict` | ||
Dictionary describing indiviudal polygons, with identifiers as keys and | ||
dictionaries containing 'name' and 'fields' items. | ||
scale : :class:`float` | ||
Default maximum scale for the axes. Typically 100 (wt%) or 1 (fractional). | ||
xlim : :class:`tuple` | ||
Default x-limits for this classifier for plotting. | ||
ylim : :class:`tuple` | ||
Default y-limits for this classifier for plotting. | ||
|
||
References | ||
----------- | ||
.. [#ref_1] Montreuil J F, Corriveau L, and Potter E G (2015). Formation of | ||
albitite-hosted uranium within IOCG systems: the Southern Breccia, | ||
Great Bear magmatic zone, Northwest Territories, Canada. | ||
Mineralium Deposita, 50:293-325. | ||
doi:`<https://doi.org/10.1007/s00126-014-0530-7>`__ | ||
""" | ||
|
||
@update_docstring_references | ||
def __init__(self, **kwargs): | ||
src = pyrolite_datafolder(subfolder="models") / "AIOCG" / "config.json" | ||
|
||
with open(src, "r") as f: | ||
config = json.load(f) | ||
kw = dict(scale=100.0, xlim=[0, 607.4], ylim=[0, 405.2]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the scale should be |
||
kw.update(kwargs) | ||
poly_config = {**config, **kw} | ||
super().__init__(**poly_config) | ||
|
||
def add_to_axes(self, ax=None, fill=False, axes_scale=100.0, labels=None, **kwargs): | ||
""" | ||
Add the AIOCG fields from the classifier to an axis. | ||
|
||
Parameters | ||
---------- | ||
ax : :class:`matplotlib.axes.Axes` | ||
Axis to add the polygons to. | ||
fill : :class:`bool` | ||
Whether to fill the polygons. | ||
axes_scale : :class:`float` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the default scale for this is |
||
Maximum scale for the axes. Typically 100 (for wt%) or 1 (fractional). | ||
labels : :class:`str` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this classifier has different sets of labels, so if you do accept the |
||
Which labels to add to the polygons (e.g. for TAS, 'volcanic', 'intrusive' | ||
or the field 'ID'). | ||
|
||
Returns | ||
-------- | ||
ax : :class:`matplotlib.axes.Axes` | ||
""" | ||
# use and override the default add_to_axes | ||
ax = self._add_polygons_to_axes( | ||
ax=ax, fill=fill, axes_scale=axes_scale, **kwargs | ||
) | ||
rescale_by = 1.0 | ||
if axes_scale is not None: # rescale polygons to fit ax | ||
if not np.isclose(self.default_scale, axes_scale): | ||
rescale_by = axes_scale / self.default_scale | ||
if labels is not None: | ||
for k, cfg in self.fields.items(): | ||
if cfg["poly"]: | ||
verts = np.array(cfg["poly"]) * rescale_by | ||
x, y = get_centroid(matplotlib.patches.Polygon(verts)) | ||
label = cfg["name"][0] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For things which don't have names, you can leave the annotation step out: if label:
ax.annotate(
...
) |
||
ax.annotate( | ||
"\n".join(label.split()), | ||
xy=(x, y), | ||
ha="center", | ||
va="center", | ||
**subkwargs(kwargs, ax.annotate, matplotlib.text.Text) | ||
) | ||
|
||
|
||
# if cfg["poly"]: | ||
NicolasPietteLauziere marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# verts = np.array(cfg["poly"]) * rescale_by | ||
# x, y = get_centroid(matplotlib.patches.Polygon(verts)) | ||
# if "volc" in labels: # use the volcanic name | ||
# label = cfg["name"][0] | ||
# elif "intr" in labels: # use the intrusive name | ||
# label = cfg["name"][-1] | ||
# else: # use the field identifier | ||
# label = k | ||
# ax.annotate( | ||
# "\n".join(label.split()), | ||
# xy=(x, y), | ||
# ha="center", | ||
# va="center", | ||
# **subkwargs(kwargs, ax.annotate, matplotlib.text.Text) | ||
# ) | ||
|
||
|
||
ax.set_ylabel("$K/(K+Na+0.5Ca) molar$") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can probably leave out the molar in the plot axes labels, or at least move it outside the |
||
ax.set_xlabel("$(2Ca+5Fe+2Mn)+(2Ca+5Fe+2Mn+Mg+Si) molar$") | ||
return ax | ||
|
||
class PeralkalinityClassifier(object): | ||
def __init__(self): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -87,5 +87,19 @@ def test_WIP(self): | |
df.loc[:, "WIP"] = WIP(df) | ||
|
||
|
||
|
||
class TestAIOCG_xy(unittest.TestCase): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the function is renamed to |
||
"""Tests the AIOCG coordinate calculation.""" | ||
|
||
def setUp(self): | ||
self.cols = ["Si", "Ca", "Mg", "Mn", "Fe", "Ti", "Na", "K", "Al"] | ||
self.df = pd.DataFrame( | ||
{k: v for k, v in zip(self.cols, np.random.rand(len(self.cols), 10))} | ||
) | ||
|
||
def test_AIOCG_xy(self): | ||
df = self.df | ||
df.loc[:, "AIOCG_x"], df.loc[:, "AIOCG_y"] = AIOCG_xy(df) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the alteration function is updated to return a dataframe, this will need to be updated to reflect that. This should also check whether the molar proportions are within the range (0, 1) as expected. |
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import unittest | ||
import matplotlib.pyplot as plt | ||
from pyrolite.plot.templates.AIOCG import AIOCG | ||
|
||
|
||
class TestAIOCGPlot(unittest.TestCase): | ||
def setUp(self): | ||
pass | ||
|
||
def test_TAS_default(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These method names still refer to TAS; they should be updated - you can leave out the name if you prefer and just refer to what kind of test is being performed (e.g. |
||
fig, axes = plt.subplots(1) | ||
for ax in [None, axes]: | ||
with self.subTest(ax=ax): | ||
ax = AIOCG(ax) | ||
|
||
def test_TAS_relim(self): | ||
for relim in [True, False]: | ||
with self.subTest(relim=relim): | ||
ax = AIOCG(relim=relim) | ||
|
||
def tearDown(self): | ||
plt.close("all") | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A quick question about these coordinates - are they in pixels? It would be good to convert them back to molar proportions if so, and then just set the aspect of the axes on which the diagram is plotted to the
6-4
ratio which it's typically found in.