This page was generated from docs/examples/mineralML_mapping_ebsd.ipynb. Interactive online version: Binder badge.

Python Notebook Download

[1]:
""" Created on March 28, 2026 // @author: Sarah Shi """

import os
import numpy as np
import pandas as pd

import mineralML as mm

import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format = 'png'

mineralML Quickstart for Mapped EBSD and/or EDS Data

This notebook shows how to load and run your quantitative EDS and EBSD data through mineralML with an example Mount Hood andesite: MH0811b. These data were collected for the mineralML manuscript (find the preprint here: https://doi.org/10.31223/X53J2M) and the data are in the GitHub repository (https://github.com/sarahshi/mineralML/tree/main/docs/examples/Maps/MountHood_MH0811b). Please refer to the paper for more information about this sample. The previous example workbook highlights how EDS maps are processed. We will apply the same procedure here, but additionally highlight how the EBSD processing code for CTF files works in mineralML.

This is a seven step process:

  1. Load and plot a phase map directly from an EBSD CTF file.

  2. Load a directory containing all your CSVs of mapped chemical data with mm.load_df (or pd.read_csv directly). [Optional] Convert the input from element to oxide wt%.

  3. Predict the mineral class with mineralML and automatically plot the mineral phase map, mineral phase counts, and prediction score histograms.

  4. Plot EBSD and mineralML-generated EDS maps side-by-side.

  5. Plot prediction score map and individual oxide maps.

  6. Plot compositions of mapped minerals in various classification diagrams (ternary, quadrilateral).

  7. Plot chemical variation maps.

I have conveniently (I hope!) wrapped all of these bits into one function, called mm.run_map and mm.plot_ctf_phases.

We loaded in the mineralML Python package as mm. mineralML has trained machine learning models for classifying minerals. This implementation aims to get your electron microprobe or quantitative EDS compositions classified and processed. We remove some degrees of freedom to simplify the process as much as possible. The minerals considered for this study include: Amphibole, Apatite, Biotite, Calcite, Chlorite, Epidote, Feldspar (Alkali Feldspar and Plagioclase), Garnet, Glass, Kalsilite, Leucite, Melilite, Muscovite, Nepheline, Olivine, Oxide (Rhombohedral_Oxides including Hematite-Ilmenite, Spinel_Group including Magnetite-Spinel), Pyroxene (Clinopyroxene, Orthopyroxene, Na-Pyroxene), Quartz, Rutile, Serpentine, Titanite, Tourmaline, and Zircon.

CSV files containing your mapped data in oxide weight percentages (or converted to) is necessary. Find an example here. The necessary oxides are SiO\(_2\), TiO\(_2\), Al\(_2\)O\(_3\), FeO\(_t\), MnO, MgO, CaO, Na\(_2\)O, K\(_2\)O, Cr\(_2\)O\(_3\), P\(_2\)O\(_5\), and ZrO\(_2\) (if you are aiming to classify zircon). For the oxides not analyzed for specific minerals, the preprocessing will fill in the nan values as 0.

1. Load and plot a phase map from an EBSD CTF file

[2]:
# --- MH0811b Configs ---
mh_file_path = "Maps/MH0811b_EBSD_EDS.ctf" # path to the CTF file exported from AZtec

# Merge more verbose EBSD phase names into broader mineral groups, matching with mineralML naming convention
mh_merge_rules = {
    "Andesine": "Plagioclase", "Orthoclase": "Alkali_Feldspar", # feldspar endmembers to group names
    "Augite": "Clinopyroxene", "Enstatite": "Orthopyroxene", # pyroxene endmembers to group names
    "Magnetite": "Oxide", "Ilmenite": "Oxide", # Fe-Ti oxides to single oxide group
    "Quartz-new": "SiO2_Polymorph", "Cristobalite": "SiO2_Polymorph", # silica phases to single group
}

# Pin each mineral group to a consistent color across figures
mh_base_cols = {
    "Plagioclase": "#66C4C4", "Alkali_Feldspar": "#FEF7C2", "Feldspar_Miscibility_Gap": "#003D36",
    "Clinopyroxene": "#E57A7A", "Orthopyroxene": "#931D1D",
    "Oxide": "#2E2DCE", "Glass": "#F9C300",
    "Apatite": "#5B6768", "SiO2_Polymorph": "#CEC6CD",
    "Unindexed": "#FFFFFF" # white background for unindexed pixels
}
[3]:
# Plot the EBSD phase map from the CTF file. This parses the CTF header for
# grid dimensions and phase definitions, maps each pixel's phase ID to its
# name, applies rename_dict for partial case-insensitive matching, and plots
# a 2D categorical phase map with legend ordered by abundance.

mh_ebsd_fig, mh_ebsd_phase_map, _, _, _ = mm.plot_ctf_phases(mh_file_path, # load and plot the CTF phase map
                                                             rename_dict=mh_merge_rules, # apply the merge rules defined above
                                                             phase_colors=mh_base_cols, # apply the color scheme defined above
                                                             title=None, # suppress the auto-generated title
                                                             scalebar_um=100, # 100 µm scale bar, computed from CTF step size
                                                             )
../_images/examples_mineralML_mapping_ebsd_5_0.png
[4]:
# Plot a stacked horizontal bar showing area proportions of each phase,
# normalized to classified pixels only. Phases below min_frac are excluded,
# and each segment is annotated with its percentage by default.

fig = mm.plot_phase_proportions(mh_ebsd_phase_map, # input the EBSD phase map to compute proportions
                                title="MH0811b EBSD Phase Proportions", # provide a title
                                min_frac=0.0001, # set a minimum fraction threshold to exclude very rare phases
                                phase_colors=mh_base_cols, # apply the same color scheme as the phase map
                                annotate=True, # annotate each segment with its percentage
                                )
../_images/examples_mineralML_mapping_ebsd_6_0.png

The remaining steps are the same procedure detailed in the first mapping .ipynb on Read The Docs. We will go through it again here for reference, so we can make side by side figures.

2. Load and prepare EDS data for analysis

Here, we will work with EDS data that are in elemental weight percent. This means that we will have to do a conversion to oxide weight percent. The data directory is Maps/MountHood_MH0811b.

[5]:
# Find your directory of mapped mineral data, stored in Maps/MountHood_MH0811b.
# This code identifies any file with CSV and appends it to the map.

base = "Maps"
map_dirs = []
for root, subdirs, files in os.walk(base):
    # Skip any path that includes 'Ignore' in its folder names
    if "Ignore" in root.split(os.sep):
        continue

    if any(f.lower().endswith(".csv") for f in files):
        map_dirs.append(root)

print(map_dirs)
['Maps/09g3', 'Maps/MountHood_MH0811b']

3. Apply the trained neural network with mm.run_map

We will use mm.run_map which will return all you need!

[6]:
# Inspect the inputs and outputs of mm.run_map
help(mm.run_map)
Help on function run_map in module mineralML.mapping:

run_map(sample_input, renormalize=False, total_threshold=None, n_iterations=50, min_frac=1e-05, pred_score_threshold=0.6, units='element_wt%', top_k=None, phases=None, exclude_phases=None, phase_colors=None, bar_style='vertical', components_spec=None, remove_islands_flag=False, fill_holes_flag=False, hole_size=10, scalebar_um=None, pixel_size_um=None, scalebar_loc='lower left', scalebar_col='black', show=True)
    Load, convert, predict, and plot for one folder of CSV maps.
    Always computes mineral components and returns a full results dictionary.
    Use ``remove_islands_flag`` and ``fill_holes_flag`` to clean the phase map
    before plotting and downstream analysis.

    Parameters:
        sample_input (str | Path | dict): Directory path or a dict of oxide maps.
        renormalize (bool): If True, scale each pixel so oxides sum to 100 wt%.
            Applied after total masking.
        total_threshold (float | None): Pixels with oxide total below this
            value (wt%) are set to NaN before renormalization and prediction.
            Use this to mask epoxy/background.
        n_iterations (int): MC forward passes for prediction.
        min_frac (float): Minimum pixel fraction required to keep a phase.
        pred_score_threshold (float): Label NaN where max prediction score <
            threshold.
        units (str): Input format — 'element_wt%' or 'oxide_wt%'.
        top_k (int|None): Cap displayed phases after filtering.
        phases (list[str]|None): Explicit phases to plot (overrides auto-pick).
        exclude_phases (list[str]|None): Phases to remove from auto-pick.
        phase_colors (dict|None): Manual color mapping {PhaseName: HexColor}.
        bar_style (str): "vertical" for the default bar chart
            (``plot_phase_counts``), or "stacked" for a stacked horizontal
            bar (``plot_phase_proportions``).
        components_spec (dict|None): Custom mineral formula logic.
        remove_islands_flag (bool): If True, removes isolated pixel clusters
            smaller than 2 pixels (4-connected) from the phase map. Useful
            for cleaning up salt-and-pepper noise in the epoxy region.
        fill_holes_flag (bool): If True, fills enclosed background holes within
            continuous phase regions up to ``hole_size`` pixels. Useful for
            patching small gaps inside large mineral grains.
        hole_size (int): Maximum hole area (in pixels) to fill when
            ``fill_holes_flag`` is True.
        scalebar_um (float, optional): Length of the scale bar in micrometers.
        pixel_size_um (float): Physical size of a single pixel in micrometers.
        scalebar_loc (str): Location of the scale bar (e.g., 'lower left').
        scalebar_col (str): Color of the scale bar text/line.
        show (bool): If True, calls plt.show().

    Returns:
        result (dict): Dictionary with keys 'figs', 'shape', 'oxide_maps',
            'df_pred', 'mineral_map', 'pred_score_map', 'kept_phases',
            'component_maps', 'component_frames'. ``oxide_maps`` includes a
            ``'Total'`` key with the per-pixel oxide sum.

[7]:
# Here is our all in one function! Read the inputs and outputs provided above.

output = mm.run_map(next((s for s in map_dirs if 'MountHood_MH0811b' in s), None), # provide the directory of interest
                    renormalize=True, # optionally renormalize totals to 100 wt%
                    total_threshold=None, # optionally filter out total values below a given value, for when EDS picks up epoxy pixels
                    pred_score_threshold=0.6, # provide a prediction score threshold. here, i only want values with >= 0.6 prediction score
                    min_frac=0.001, # provide a minimum pixel fraction for the phase to be displayed
                    units='element_wt%', # provide the unit. can choose 'element_wt%' or 'oxide_wt%'
                    phases=mh_base_cols.keys(), # phases of interest
                    scalebar_um=100, # define size of scalebar desired, in microns
                    pixel_size_um=2.0, # define size of each pixel of scalebar, in microns
                    scalebar_loc='lower left', # specify location for scalebar
                    scalebar_col='black', # specify color for scalebar
                    phase_colors=mh_base_cols, # provide a color scheme for the phases, as a dictionary mapping phase names to color codes
                    )

[ok] MH0811b_singleframe_quant-Si Wt%.csv  →  Si  (494, 501)
[ok] MH0811b_singleframe_quant-Al Wt%.csv  →  Al  (494, 501)
[skip] no element token in: MH0811b_singleframe_quant-Sr Wt%.csv
[skip] no element token in: MH0811b_singleframe_quant-Cu Wt%.csv
[ok] MH0811b_singleframe_quant-Ni Wt%.csv  →  Ni  (494, 501)
[ok] MH0811b_singleframe_quant-Mn Wt%.csv  →  Mn  (494, 501)
[skip] no element token in: MH0811b_singleframe_quant-Cl Wt%.csv
[ok] MH0811b_singleframe_quant-Ti Wt%.csv  →  Ti  (494, 501)
[skip] no element token in: MH0811b_singleframe_quant-O Wt%.csv
[ok] MH0811b_singleframe_quant-Na Wt%.csv  →  Na  (494, 501)
[ok] MH0811b_singleframe_quant-P Wt%.csv  →  P  (494, 501)
[skip] no element token in: MH0811b_singleframe_quant-F Wt%.csv
[ok] MH0811b_singleframe_quant-K Wt%.csv  →  K  (494, 501)
[ok] MH0811b_singleframe_quant-Mg Wt%.csv  →  Mg  (494, 501)
[skip] no element token in: MH0811b_singleframe_quant-S Wt%.csv
[ok] MH0811b_singleframe_quant-Fe Wt%.csv  →  Fe  (494, 501)
[ok] MH0811b_singleframe_quant-Ca Wt%.csv  →  Ca  (494, 501)
[ok] MH0811b_singleframe_quant-Cr Wt%.csv  →  Cr  (494, 501)
mineralML: 247494 rows — 238609 classified by neural network, 8390 by empirical rules (Zircon: 0, SiO2 polymorph: 8390, Carbonate: 0), 495 skipped (invalid/empty)
../_images/examples_mineralML_mapping_ebsd_12_1.png
../_images/examples_mineralML_mapping_ebsd_12_2.png
../_images/examples_mineralML_mapping_ebsd_12_3.png
[8]:
# Plot a stacked horizontal bar showing area proportions of each phase,
# normalized to classified pixels only. Phases below min_frac are excluded,
# and each segment is annotated with its percentage by default.

fig = mm.plot_phase_proportions(output['mineral_map'], # input the EDS phase map to compute proportions
                                title="MH0811b EDS Phase Proportions", # provide a title
                                phases=mh_base_cols.keys(), # specify the phases to include
                                min_frac=0.0001, # set a minimum fraction threshold to exclude very rare phases
                                phase_colors=mh_base_cols, # apply the same color scheme as the phase map
                                annotate=True, # annotate each segment with its percentage
                                )
../_images/examples_mineralML_mapping_ebsd_13_0.png
[9]:
# Inspect what is in the outputs
output.keys()
[9]:
dict_keys(['figs', 'shape', 'oxide_maps', 'df_pred', 'mineral_map', 'pred_score_map', 'kept_phases', 'component_maps', 'component_frames'])

Let’s say you now want to work with these data in dataframe form rather than dictionary form. How would you do this? Access output['df_pred'] to retrieve the predictions dataframe.

[10]:
# Pull the dataframe of predictions
df_pred = output['df_pred']
display(df_pred)
SiO2 TiO2 Al2O3 FeOt MnO MgO CaO Na2O K2O Cr2O3 P2O5 ZrO2 Predict_Mineral Prediction_Score Prediction_Score_Sigma Second_Predict_Mineral Second_Prediction_Score Submineral
0 52.869693 0.089446 1.409243 21.357081 0.382089 21.732924 1.831746 0.069385 0.182859 0.000000 0.000000 NaN Orthopyroxene 0.954259 0.121515 Amphibole 0.045181 Enstatite
1 51.892819 0.591858 1.289309 22.414176 1.120519 20.739631 1.453647 0.155577 0.000000 0.000000 0.000000 NaN Orthopyroxene 0.926802 0.161589 Amphibole 0.072842 Enstatite
2 52.287424 0.306354 0.442857 22.273798 0.656842 21.441527 1.709473 0.313483 0.159156 0.326132 0.000000 NaN Orthopyroxene 0.937167 0.156033 Amphibole 0.053794 Enstatite
3 51.667645 0.468977 1.602369 23.606152 0.000000 20.711076 1.316344 0.364752 0.000000 0.000000 0.000000 NaN Orthopyroxene 0.868075 0.213352 Amphibole 0.130258 Enstatite
4 53.113961 0.000000 1.850691 22.954577 0.603992 19.687578 1.703375 0.000000 0.078555 0.007271 0.000000 NaN Orthopyroxene 0.955211 0.083039 Amphibole 0.041217 Enstatite
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
247489 75.658296 0.020106 2.377803 10.960863 0.761252 7.599581 0.830684 0.574031 0.352833 0.168437 0.288810 NaN Glass 0.999502 0.002028 Amphibole 0.000485 NaN
247490 52.781085 0.000000 1.084068 25.244632 0.585867 18.465076 1.386827 0.436795 0.000000 0.000000 0.015651 NaN Orthopyroxene 0.800574 0.286466 Amphibole 0.195758 Enstatite
247491 52.136743 0.332666 0.759727 23.384690 0.000000 21.442475 1.262017 0.000000 0.000000 0.573398 0.000000 NaN Orthopyroxene 0.955728 0.141057 Amphibole 0.043054 Enstatite
247492 52.276311 0.283907 1.219325 21.488206 0.775673 21.428622 1.410546 0.323923 0.107557 0.174155 0.000000 NaN Orthopyroxene 0.762849 0.309576 Amphibole 0.236592 Enstatite
247493 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN None NaN NaN NaN NaN NaN

247494 rows × 18 columns

4. Plot EBSD and mineralML-generated EDS phase maps side-by-side

Let’s plot the two phase maps side by side!

[11]:
# EBSD phase map
mh_ebsd_fig
[11]:
../_images/examples_mineralML_mapping_ebsd_18_0.png
[12]:
# EDS phase map
output['figs'][0]
[12]:
../_images/examples_mineralML_mapping_ebsd_19_0.png

We can now examine the phase maps produced by the two methods and compare their performance. See the preprint for a more detailed description of these maps.

5. Plot oxide concentration maps and prediction score maps

Let’s plot the original oxide maps loaded from the directory. We can examine how well the predicted phase map matches some of the observations made in oxide space. We have a handy function for doing so, with mm.plot_oxide_map.

[13]:
fig, ax = mm.plot_oxide_map(
    output, # take the output from run_map
    oxide_name='SiO2', # specify the oxide of interest
    scalebar_um=50, # define size of scalebar desired, in microns
    pixel_size_um=2, # define size of each pixel of scalebar, in microns
    scalebar_loc='upper right', # specify location for scalebar
    scalebar_col='black', # specify color for scalebar
)
../_images/examples_mineralML_mapping_ebsd_22_0.png

Let’s plot the prediction scores from the output, in mapped form. This allows for further investigation to determine where predictions are more and less certain.

[14]:
fig, ax = mm.plot_score_map(
    output, # take the output from run_map
    scalebar_um=50, # define size of scalebar desired, in microns
    pixel_size_um=2, # define size of each pixel of scalebar, in microns
    scalebar_loc='upper right', # specify location for scalebar
    scalebar_col='black', # specify color for scalebar
)
../_images/examples_mineralML_mapping_ebsd_24_0.png

6. Plot compositions of mapped minerals in various classification diagrams (ternary, quadrilateral).

We can do some more with mineralML now. Let’s plot all the feldspars, pyroxenes, and spinels in ternary space.

Identify the phases present.

[15]:
# Here are all our feldspars
fspars = df_pred[df_pred.Predict_Mineral == 'Plagioclase']
display('Feldspars:', fspars)

# Here are all our pyroxenes
pxs_names = ['Clinopyroxene', 'Orthopyroxene']
pxs = df_pred[df_pred.Predict_Mineral.isin(pxs_names)]
display('Pyroxenes:', pxs)

# Here are all our oxides
ox_names = ['Oxide']
oxs = df_pred[df_pred.Predict_Mineral.isin(ox_names)]
display('Oxides:', oxs)
'Feldspars:'
SiO2 TiO2 Al2O3 FeOt MnO MgO CaO Na2O K2O Cr2O3 P2O5 ZrO2 Predict_Mineral Prediction_Score Prediction_Score_Sigma Second_Predict_Mineral Second_Prediction_Score Submineral
6 59.409736 0.453551 24.911057 0.437281 0.112236 0.207861 6.787231 6.732034 0.948753 0.000000 0.000000 NaN Plagioclase 0.998052 0.010622 Glass 0.001846 Andesine
7 57.293535 0.000000 26.530507 1.352565 0.000000 0.388365 8.102746 5.804944 0.458178 0.000000 0.069160 NaN Plagioclase 0.992805 0.040456 Glass 0.007124 Andesine
8 55.435543 0.033904 25.553034 1.356231 0.297159 0.040152 9.300065 5.570140 0.949996 0.595895 0.000000 NaN Plagioclase 0.988094 0.025955 Glass 0.011509 Andesine
9 55.594348 0.000000 27.377475 1.390827 0.010037 0.019643 8.963531 5.725492 0.710503 0.208144 0.000000 NaN Plagioclase 0.998130 0.007349 Glass 0.001659 Andesine
10 55.342296 0.031976 27.614579 1.644185 0.000000 0.082248 9.629535 5.026421 0.508073 0.000000 0.000000 NaN Plagioclase 0.991868 0.049857 Glass 0.007949 Andesine
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
247253 65.628218 0.102233 20.322669 0.970144 0.000000 0.225444 3.856563 6.940244 1.429413 0.525072 0.000000 NaN Plagioclase 0.920885 0.178779 Glass 0.077788 Oligoclase
247260 64.632527 0.076708 21.037032 0.653223 0.000000 0.119711 3.751034 7.452901 1.592908 0.069690 0.000000 NaN Plagioclase 0.987307 0.027464 Glass 0.011845 Oligoclase
247262 64.296102 0.000000 21.485038 0.976630 0.000000 0.233654 3.234296 7.440961 2.012244 0.293341 0.027735 NaN Plagioclase 0.980832 0.088832 Glass 0.018718 Oligoclase
247288 60.975595 0.000000 22.110719 1.477735 0.102445 1.313905 5.829286 7.125921 1.005915 0.058480 0.000000 NaN Plagioclase 0.967834 0.088990 Glass 0.031806 Oligoclase
247480 63.657836 0.455971 21.603974 1.279722 0.000000 0.323472 3.743229 7.084680 1.518178 0.270450 0.062488 NaN Plagioclase 0.990895 0.026728 Glass 0.008367 Oligoclase

57198 rows × 18 columns

'Pyroxenes:'
SiO2 TiO2 Al2O3 FeOt MnO MgO CaO Na2O K2O Cr2O3 P2O5 ZrO2 Predict_Mineral Prediction_Score Prediction_Score_Sigma Second_Predict_Mineral Second_Prediction_Score Submineral
0 52.869693 0.089446 1.409243 21.357081 0.382089 21.732924 1.831746 0.069385 0.182859 0.000000 0.000000 NaN Orthopyroxene 0.954259 0.121515 Amphibole 0.045181 Enstatite
1 51.892819 0.591858 1.289309 22.414176 1.120519 20.739631 1.453647 0.155577 0.000000 0.000000 0.000000 NaN Orthopyroxene 0.926802 0.161589 Amphibole 0.072842 Enstatite
2 52.287424 0.306354 0.442857 22.273798 0.656842 21.441527 1.709473 0.313483 0.159156 0.326132 0.000000 NaN Orthopyroxene 0.937167 0.156033 Amphibole 0.053794 Enstatite
3 51.667645 0.468977 1.602369 23.606152 0.000000 20.711076 1.316344 0.364752 0.000000 0.000000 0.000000 NaN Orthopyroxene 0.868075 0.213352 Amphibole 0.130258 Enstatite
4 53.113961 0.000000 1.850691 22.954577 0.603992 19.687578 1.703375 0.000000 0.078555 0.007271 0.000000 NaN Orthopyroxene 0.955211 0.083039 Amphibole 0.041217 Enstatite
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
247471 52.777936 0.341422 0.681463 22.052613 0.492089 21.713396 1.409693 0.000000 0.014347 0.128819 0.388221 NaN Orthopyroxene 0.924290 0.209623 Amphibole 0.071631 Enstatite
247472 51.164731 0.033477 0.757427 23.908463 1.223195 20.198822 1.584169 0.034482 0.115519 0.426862 0.000000 NaN Orthopyroxene 0.963717 0.129811 Amphibole 0.035776 Enstatite
247490 52.781085 0.000000 1.084068 25.244632 0.585867 18.465076 1.386827 0.436795 0.000000 0.000000 0.015651 NaN Orthopyroxene 0.800574 0.286466 Amphibole 0.195758 Enstatite
247491 52.136743 0.332666 0.759727 23.384690 0.000000 21.442475 1.262017 0.000000 0.000000 0.573398 0.000000 NaN Orthopyroxene 0.955728 0.141057 Amphibole 0.043054 Enstatite
247492 52.276311 0.283907 1.219325 21.488206 0.775673 21.428622 1.410546 0.323923 0.107557 0.174155 0.000000 NaN Orthopyroxene 0.762849 0.309576 Amphibole 0.236592 Enstatite

115035 rows × 18 columns

'Oxides:'
SiO2 TiO2 Al2O3 FeOt MnO MgO CaO Na2O K2O Cr2O3 P2O5 ZrO2 Predict_Mineral Prediction_Score Prediction_Score_Sigma Second_Predict_Mineral Second_Prediction_Score Submineral
816 9.601393 42.543327 0.726077 42.288306 0.000000 3.217761 1.174167 0.000000 0.234858 0.000000 0.047997 NaN Oxide 0.991546 0.027993 Olivine 0.006062 Rhombohedral_Oxides
817 5.055912 45.136092 0.727534 46.073721 1.004736 1.408648 0.391981 0.057390 0.056051 0.047390 0.040546 NaN Oxide 0.999652 0.000546 Olivine 0.000150 Rhombohedral_Oxides
1317 14.740859 37.393898 4.124402 38.457593 0.104994 2.263607 2.124805 0.105771 0.285457 0.398615 0.000000 NaN Oxide 0.942460 0.152996 Amphibole 0.027525 Rhombohedral_Oxides
1318 5.418784 43.949746 3.063736 45.020854 0.556096 0.814851 0.223852 0.000000 0.101373 0.000000 0.000000 NaN Oxide 0.997249 0.010152 Chlorite 0.001335 Rhombohedral_Oxides
1319 2.445324 47.957417 0.453160 46.692280 0.711513 0.676563 0.358138 0.221529 0.119474 0.151114 0.000000 NaN Oxide 0.998311 0.008520 Kalsilite 0.000778 Rhombohedral_Oxides
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
247375 1.709557 12.717564 1.237637 83.124692 0.166292 0.681558 0.092660 0.000000 0.000000 0.270039 0.000000 NaN Oxide 0.999055 0.005121 Olivine 0.000901 Spinel_Group
247376 1.949185 11.587195 1.197346 82.621199 0.856195 0.850968 0.195952 0.000000 0.000000 0.384142 0.120489 NaN Oxide 0.999924 0.000488 Olivine 0.000043 Spinel_Group
247377 0.929317 12.592958 1.076223 82.325128 0.033928 1.037305 0.338269 0.295170 0.298730 1.023496 0.000000 NaN Oxide 0.999981 0.000345 Epidote 0.000011 Spinel_Group
247378 1.137157 12.901928 1.104496 82.801424 0.117042 1.164574 0.225610 0.000000 0.070023 0.212909 0.264837 NaN Oxide 0.996252 0.026203 Nepheline 0.003743 Spinel_Group
247379 5.987311 11.661483 2.857898 76.584670 0.261887 0.624719 0.561372 0.730096 0.029141 0.701422 0.000000 NaN Oxide 0.999971 0.000000 Rhombohedral_Oxides 0.000012 Spinel_Group

5568 rows × 18 columns

Plot these feldspars, pyroxenes, and spinels!

[16]:
# Use FeldsparClassifier to examine at the component space (XAn, XAb, XOr)
fspar_comp = mm.FeldsparClassifier(fspars).calculate_components()
display(fspar_comp)

# Use FeldsparClassifier to plot up these data.
fig = mm.FeldsparClassifier(fspars).plot()
Sample SiO2 TiO2 Al2O3 FeOt MnO MgO CaO Na2O K2O ... Prediction_Score Prediction_Score_Sigma Second_Predict_Mineral Second_Prediction_Score Cation_Sum M_site T_site An Ab Or
6 NaN 59.409736 0.453551 24.911057 0.437281 0.112236 0.207861 6.787231 6.732034 0.948753 ... 0.998052 0.010622 Glass 0.001846 4.987375 0.963944 3.973663 0.337691 0.606105 0.056204
7 NaN 57.293535 0.000000 26.530507 1.352565 0.000000 0.388365 8.102746 5.804944 0.458178 ... 0.992805 0.040456 Glass 0.007124 4.983526 0.922530 3.981485 0.423060 0.548456 0.028483
8 NaN 55.435543 0.033904 25.553034 1.356231 0.297159 0.040152 9.300065 5.570140 0.949996 ... 0.988094 0.025955 Glass 0.011509 5.026741 1.009083 3.928468 0.453425 0.491428 0.055148
9 NaN 55.594348 0.000000 27.377475 1.390827 0.010037 0.019643 8.963531 5.725492 0.710503 ... 0.998130 0.007349 Glass 0.001659 5.019667 0.978708 3.979120 0.444396 0.513663 0.041941
10 NaN 55.342296 0.031976 27.614579 1.644185 0.000000 0.082248 9.629535 5.026421 0.508073 ... 0.991868 0.049857 Glass 0.007949 4.989487 0.938373 3.982168 0.498163 0.470542 0.031295
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
247253 NaN 65.628218 0.102233 20.322669 0.970144 0.000000 0.225444 3.856563 6.940244 1.429413 ... 0.920885 0.178779 Glass 0.077788 4.892980 0.858505 3.961967 0.212866 0.693194 0.093940
247260 NaN 64.632527 0.076708 21.037032 0.653223 0.000000 0.119711 3.751034 7.452901 1.592908 ... 0.987307 0.027464 Glass 0.011845 4.932751 0.912954 3.982498 0.196039 0.704840 0.099121
247262 NaN 64.296102 0.000000 21.485038 0.976630 0.000000 0.233654 3.234296 7.440961 2.012244 ... 0.980832 0.088832 Glass 0.018718 4.952414 0.908712 3.980610 0.169378 0.705150 0.125471
247288 NaN 60.975595 0.000000 22.110719 1.477735 0.102445 1.313905 5.829286 7.125921 1.005915 ... 0.967834 0.088990 Glass 0.031806 5.015272 0.958287 3.907595 0.292609 0.647272 0.060120
247480 NaN 63.657836 0.455971 21.603974 1.279722 0.000000 0.323472 3.743229 7.084680 1.518178 ... 0.990895 0.026728 Glass 0.008367 4.930467 0.874556 3.959842 0.203757 0.697848 0.098395

57198 rows × 58 columns

../_images/examples_mineralML_mapping_ebsd_29_1.png
[17]:
# Use PyroxeneClassifier to examine at the component space (En, Wo, Fs). If sodic pyroxenes are also within this input, this will plot them up in the sodic pyroxene ternary
pxs_comp = mm.PyroxeneClassifier(pxs).calculate_components()
display(pxs_comp)

# Use PyroxeneClassifier to plot up these data.
fig = mm.PyroxeneClassifier(pxs).plot()
Sample SiO2 TiO2 Al2O3 FeOt MnO MgO CaO Na2O K2O ... EnFs DiHd_2003 Di Fe3_Wang21 Fe2_Wang21 Di_h Hd_h Aeg_h Jd_h En_h
0 NaN 52.869693 0.089446 1.409243 21.357081 0.382089 21.732924 1.831746 0.069385 0.182859 ... 0.913586 0.044671 0.028612 -0.000221 0.665433 0.042671 0.0 0.008467 0.002725 0.914269
1 NaN 51.892819 0.591858 1.289309 22.414176 1.120519 20.739631 1.453647 0.155577 0.000000 ... 0.917508 0.035731 0.021827 0.010831 0.695281 0.027619 0.0 0.000000 0.000000 0.931327
2 NaN 52.287424 0.306354 0.442857 22.273798 0.656842 21.441527 1.709473 0.313483 0.159156 ... 0.927521 0.046937 0.029333 0.029976 0.670329 0.023331 0.0 0.012125 0.000000 0.910934
3 NaN 51.667645 0.468977 1.602369 23.606152 0.000000 20.711076 1.316344 0.364752 0.000000 ... 0.940243 0.026458 0.016139 0.035618 0.708142 0.004287 0.0 0.013351 0.000000 0.924734
4 NaN 53.113961 0.000000 1.850691 22.954577 0.603992 19.687578 1.703375 0.000000 0.078555 ... 0.907582 0.000240 0.000144 -0.054452 0.772333 0.029232 0.0 0.000000 0.003748 0.902651
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
247471 NaN 52.777936 0.341422 0.681463 22.052613 0.492089 21.713396 1.409693 0.000000 0.014347 ... 0.927852 0.037868 0.023927 0.010103 0.677190 0.032816 0.0 0.000000 0.000000 0.938144
247472 NaN 51.164731 0.033477 0.757427 23.908463 1.223195 20.198822 1.584169 0.034482 0.115519 ... 0.938871 0.034974 0.020592 0.034501 0.728767 0.000000 0.0 0.000000 0.000000 0.936789
247490 NaN 52.781085 0.000000 1.084068 25.244632 0.585867 18.465076 1.386827 0.436795 0.000000 ... 0.898961 0.042573 0.023851 -0.011006 0.809892 0.048076 0.0 0.000000 0.032046 0.905598
247491 NaN 52.136743 0.332666 0.759727 23.384690 0.000000 21.442475 1.262017 0.000000 0.000000 ... 0.957045 0.021888 0.013580 0.004964 0.729895 0.012807 0.0 0.000000 0.000000 0.952504
247492 NaN 52.276311 0.283907 1.219325 21.488206 0.775673 21.428622 1.410546 0.323923 0.107557 ... 0.919233 0.035533 0.022445 0.022909 0.651775 0.020572 0.0 0.015542 0.000000 0.918074

115035 rows × 86 columns

/home/docs/checkouts/readthedocs.org/user_builds/mineralml/conda/stable/lib/python3.9/site-packages/ternary/plotting.py:148: UserWarning: No data for colormapping provided via 'c'. Parameters 'vmin', 'vmax' will be ignored
  ax.scatter(xs, ys, vmin=vmin, vmax=vmax, **kwargs)
../_images/examples_mineralML_mapping_ebsd_30_2.png
[18]:
# Use OxideClassifier to examine at the component space.
oxs_comp = mm.OxideClassifier(oxs).calculate_components()
display(oxs_comp)

# Use OxideClassifier to plot up these data.
fig = mm.OxideClassifier(oxs).plot()
SiO2 TiO2 Al2O3 FeOt MnO MgO CaO Na2O K2O Cr2O3 ... Fe2t_cat_4ox Fe2_cat_4ox Fe3_cat_4ox Mn_cat_4ox Mg_cat_4ox Ca_cat_4ox Na_cat_4ox K_cat_4ox P_cat_4ox Cr_cat_4ox
816 9.601393 42.543327 0.726077 42.288306 0.000000 3.217761 1.174167 0.000000 0.234858 0.000000 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
817 5.055912 45.136092 0.727534 46.073721 1.004736 1.408648 0.391981 0.057390 0.056051 0.047390 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1317 14.740859 37.393898 4.124402 38.457593 0.104994 2.263607 2.124805 0.105771 0.285457 0.398615 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1318 5.418784 43.949746 3.063736 45.020854 0.556096 0.814851 0.223852 0.000000 0.101373 0.000000 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1319 2.445324 47.957417 0.453160 46.692280 0.711513 0.676563 0.358138 0.221529 0.119474 0.151114 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
247375 1.709557 12.717564 1.237637 83.124692 0.166292 0.681558 0.092660 0.000000 0.000000 0.270039 ... 2.490985 1.359076 1.131909 0.005047 0.036407 0.003557 0.000000 0.000000 0.000000 0.007650
247376 1.949185 11.587195 1.197346 82.621199 0.856195 0.850968 0.195952 0.000000 0.000000 0.384142 ... 2.474110 1.310324 1.163786 0.025967 0.045423 0.007518 0.000000 0.000000 0.003652 0.010875
247377 0.929317 12.592958 1.076223 82.325128 0.033928 1.037305 0.338269 0.295170 0.298730 1.023496 ... 2.452451 1.233630 1.218821 0.001024 0.055082 0.012910 0.020385 0.013575 0.000000 0.028824
247378 1.137157 12.901928 1.104496 82.801424 0.117042 1.164574 0.225610 0.000000 0.070023 0.212909 ... 2.474554 1.322909 1.151645 0.003543 0.062039 0.008638 0.000000 0.003192 0.008012 0.006015
247379 5.987311 11.661483 2.857898 76.584670 0.261887 0.624719 0.561372 0.730096 0.029141 0.701422 ... 2.235827 1.352598 0.883229 0.007743 0.032510 0.020997 0.049414 0.001298 0.000000 0.019359

5568 rows × 92 columns

/home/docs/checkouts/readthedocs.org/user_builds/mineralml/conda/stable/lib/python3.9/site-packages/ternary/plotting.py:148: UserWarning: No data for colormapping provided via 'c'. Parameters 'vmin', 'vmax' will be ignored
  ax.scatter(xs, ys, vmin=vmin, vmax=vmax, **kwargs)
/home/docs/checkouts/readthedocs.org/user_builds/mineralml/conda/stable/lib/python3.9/site-packages/ternary/plotting.py:148: UserWarning: No data for colormapping provided via 'c'. Parameters 'vmin', 'vmax' will be ignored
  ax.scatter(xs, ys, vmin=vmin, vmax=vmax, **kwargs)
../_images/examples_mineralML_mapping_ebsd_31_2.png
../_images/examples_mineralML_mapping_ebsd_31_3.png

You might note that the structure of these three ...Classifier classes is identical. That is intentional! mm.FeldsparClassifier, mm.PyroxeneClassifier, and mm.OxideClassifier all have calculate_components and plot methods embedded.

7. Plot chemical variation maps

We know the mineralogy now. What if you now want to inspect the chemical variation within the individual crystals? Pull the component maps created for each sample and plot this up with mm.plot_component_composite.

This function currently does this calculation for feldspars, pyroxenes, olivines, and amphibole. This can easily be expanded with all the stoichiometric mineral functions. Here, I will just show this for these common igneous phases.

[19]:
# Inspect what’s available:
print(sorted(output["component_maps"].keys()))

#  Plot map highlighting internal compositional variation
fig = mm.plot_component_composite(output, # specify output from above
                                  title="MH0811b", # optionally add a title to this plot
                                  phases=mh_base_cols.keys(), # phases of interest
                                  phase_colors=mh_base_cols, # provide a color scheme for the phases, as a dictionary mapping phase names to color codes
                                  smooth_sigma=0.25, # add a Gaussian blur to smooth compositional data, usually turned off.
                                  scalebar_um=100, # define size of scalebar desired, in microns
                                  pixel_size_um=2.0, # define size of each pixel of scalebar, in microns
                                  scalebar_loc='lower left', # specify location for scalebar
                                  scalebar_col='black', # specify color for scalebar
                                  )

['Clinopyroxene.En', 'Clinopyroxene.Fs', 'Clinopyroxene.Wo', 'Feldspar.Ab', 'Feldspar.An', 'Feldspar.Or', 'Orthopyroxene.En', 'Orthopyroxene.Fs', 'Orthopyroxene.Wo']
../_images/examples_mineralML_mapping_ebsd_34_1.png

One could alternatively use all the functions within mineralML.mapping to do these same things, in a more stepwise manner. Look through the documentation if you would like to use individual bits of this code.