# -*- coding: utf-8 -*-
"""
Created on Fri Jun 7 10:10:42 2024
@author: Dr. Sunil Anandatheertha
"""
import sys
import numpy as np
import pandas as pd
from copy import deepcopy
import matplotlib.pyplot as plt
from collections import namedtuple
from matplotlib import cm
from scipy import stats
import seaborn as sns
from scipy.stats import mannwhitneyu
from scipy.stats import ks_2samp
from scipy.stats import kruskal
from scipy.stats import entropy
from upxo.ggrowth.mcgs import mcgs
from upxo.pxtalops.Characterizer import mcgs_mchar_2d
from upxo._sup import dataTypeHandlers as dth
from upxo.geoEntities.mulpoint2d import MPoint2d
from upxo.interfaces.user_inputs.excel_commons import read_excel_range
from upxo.interfaces.user_inputs.excel_commons import write_array_to_excel
from upxo._sup.data_ops import find_outliers_iqr, distance_between_two_points
from scipy.interpolate import griddata
import matplotlib.ticker as ticker
from scipy.stats import entropy
from scipy.stats import gaussian_kde
from mpl_toolkits.axes_grid1 import make_axes_locatable
from upxo.statops.stattests import test_rand_distr_autocorr
from upxo.statops.stattests import test_rand_distr_runs
from upxo.statops.stattests import test_rand_distr_chisquare
from upxo.statops.stattests import test_rand_distr_kolmogorovsmirnov
from upxo.statops.stattests import test_rand_distr_kullbackleibler
import networkx as nx
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from networkx.algorithms import community
from scipy.stats import wasserstein_distance, ks_2samp, energy_distance
try:
import netlsd
_NETLSD_AVAILABLE = True
except ImportError:
_NETLSD_AVAILABLE = False
from upxo._sup.data_ops import calculate_angular_distance as calc_angdist
from upxo._sup.data_ops import calculate_density_bins
from upxo._sup.data_ops import approximate_to_bin_means
from upxo.netops import kmake
from tqdm import tqdm
from upxo.netops.kmake import make_gid_net_from_neighlist
import upxo.netops.kmake as kmake
import upxo.netops.kcmp as kcmp
import upxo.netops.kchar as kchar
NUMBERS = dth.dt.NUMBERS
ITERABLES = dth.dt.ITERABLES
RNG = np.random.default_rng()
DCOPY = deepcopy
[docs]
class KREPR():
"""
Grain-network representativeness assessor (2D K-topology / morphometrics).
Compares **target** and **sample** grain structures via neighbour
networks (NetworkX / UPXO netops), morphological property tables, and
distribution distances (Wasserstein, KS, energy distance, optional
NetLSD). Used by ``repgen2d`` ranking and standalone R-field studies.
Import::
from upxo.repqual.grain_network_repr_assesser import KREPR
Parameters / attributes
-----------------------
upxogs_tgt, upxogs_smp
Single UPXO grain-structure objects when used in one-vs-one mode.
tgset, sgset : dict
Sets of target / sample grain structures (keys often tslices).
tkset, skset : dict
Neighbour-network graphs (or adjacency dicts) for target / sample,
keyed by neighbour order ``ordern``.
tnset, snset : dict
UPXO neighbour maps (gid → neighbour gids) by order.
tmpset, smpset : dict
Morphological property sets for target / sample.
tid, sid : list
Usable target / sample IDs (subset of set keys); default all keys.
ordern : list
Neighbour orders to analyse (default ``[1]`` if unset).
mprop2d_flags, mprop3d_flags, sprop2d_flags, sprop3d_flags : dict
Flags controlling which morphological properties are computed.
mprop2d, mprop3d, sprop2d, sprop3d : dict
Computed morphological property stores.
rkf : dict
Network-based R-field data; set before full representativeness runs.
_cim_: str
Class initiation method, not intended for user use.
Author: Dr. Sunil Anandatheertha
om jayanti man'gaLA kALi bhadrakALi kapAlini |
durgA kshamA shivA dhAtri svAhA svadhA namOstute ||
"""
__slots__ = ('gstype', 'upxogs_tgt', 'upxogs_smp',
'tgset', 'sgset', 'tkset', 'skset', 'tnset', 'snset',
'tmpset', 'smpset',
'tid', 'sid', 'ntid', 'nsid',
'ordern', 'dim',
'rkf_flags', 'rkf',
'mprop2d_flags', 'mprop3d_flags', 'mprop2d', 'mprop3d',
'mp_gspn_map', '_cim_',)
def __init__(self, **kwargs):
"""Initialise the instance."""
self._cim_ = kwargs['_cim_']
# --------------------------------------------
self.mp_gspn_map = {'area_pix': 'area'}
# --------------------------------------------
self.dim = namedtuple('dim', ['tgt', 'smp'])
self.gstype = namedtuple('gstype', ['tgt', 'smp'])
# ==========================================================
# TEND TO DIFFERENT CLASS CREATOIPN METHODS
if kwargs['_cim_'] == 'from_gs':
# Validations
self.gstype.tgt = kwargs['gstype_tgt']
self.gstype.smp = kwargs['gstype_smp']
# --------------------------------------------
self.upxogs_tgt = kwargs['upxogs_tgt']
self.upxogs_smp = kwargs['upxogs_smp']
tgset, sgset = kwargs['tgset'], kwargs['sgset']
self.tgset = {i: gs for i, gs in tgset.items()}
self.sgset = {i: gs for i, gs in sgset.items()}
# self.tid, self.sid = kwargs['tid'], kwargs['sid']
self.tkset, self.skset = None, None
self.tnset, self.snset = None, None
elif kwargs['_cim_'] == 'from_k':
# Validations
self.gstype.tgt, self.gstype.smp = None, None
self.upxogs_tgt, self.upxogs_smp = None, None
self.tgset, self.sgset = None, None
self.tkset, self.skset = kwargs['tkset'], kwargs['skset']
# self.tid, self.sid = kwargs['tid'], kwargs['sid']
self.tnset, self.snset = None, None
elif kwargs['_cim_'] == 'from_neigh':
# Validations
self.gstype.tgt, self.gstype.smp = None, None
self.tgset, self.sgset = None, None
self.tkset, self.skset = None, None
self.tnset, self.snset = kwargs['tnset'], kwargs['snset']
self.tid, self.sid = kwargs['tid'], kwargs['sid'],
elif kwargs['_cim_'] == 'from_gsgen':
"""
Note @dev:
This branch is as of now, identical to 1st branch 'from_gs'.
This is expected to change with further development.
Continue to develop identical to 1st branch 'from_gs'.
"""
# Validations
self.gstype.tgt = kwargs['gstype_tgt']
self.gstype.smp = kwargs['gstype_smp']
self.upxogs_tgt = kwargs['upxogs_tgt']
self.upxogs_smp = kwargs['upxogs_smp']
tgset, sgset = kwargs['tgset'], kwargs['sgset']
self.tgset = {i: gs for i, gs in tgset.items()}
self.sgset = {i: gs for i, gs in sgset.items()}
# self.tid, self.sid = kwargs['tid'], kwargs['sid']
self.tkset, self.skset = None, None
self.tnset, self.snset = None, None
# ==========================================================
# Set the dimensioanlities of the problem.
self.init_subdef_set_dim()
self.init_subdef_set_gsid(kwargs)
self.init_subdef_set_neighs(kwargs)
self.init_subdef_set_networks()
self.init_subdef_set_prop_flags()
# ==========================================================
[docs]
@classmethod
def from_gs(cls, *, upxogs_tgt=None, upxogs_smp=None,
tgset=None, sgset=None, ordern=[1],
tsid_source='from_gs', ssid_source='from_gs',
tid=None, sid=None, _cim_='from_gs',
gstype_tgt='mcgs2d', gstype_smp='mcgs3d'):
"""
Instantiate network based repr class using UPXO grain structure.
Parameters
----------
upxogs_tgt
upxogs_smp
tgset: dict
Target grain structures. Defaults to None.
sgset: dict
Sample grain structures. Defaults to None.
ordern: list
Neighbour order-n to be used. Defaults to [1].
_cim_: str
Class initiation method. Defaults to 'from_gs'.
Not intended for user. Leave it alone.
from upxo.ggrowth.mcgs import mcgs
tgt = mcgs(study='independent', input_dashboard='input_dashboard.xls')
tgt.simulate()
tgt.detect_grains()
tgt.char_morph_2d(tgt.m)
tgset = {i: gs for i, gs in tgt.gs.items()}
from upxo.repqual.grain_network_repr_assesser import KREPR
kr = KREPR.from_gs(tgset=tgset, sgset=tgset, ordern=[1, 5])
kr.creation_method
kr.calculate_mprop2d()
kr.set_rkf(js=True, wd=True, ksp=True, ed=True, nlsd=True, degcen=False,
btwcen=False, clscen=False, egnvcen=False)
kr.calculate_rkf()
kr.plot_rkf(neigh_orders=[1, 5], power=1, figsize=(7, 5), dpi=120,
xtick_incr=5, ytick_incr=5, lfs=7, tfs=8,
cmap='nipy_spectral', cbarticks=np.arange(0, 1.1, 0.1), cbfs=10,
cbtitle='Measure of representativeness R(S|T)',
cbfraction=0.046, cbpad=0.04, cbaspect=30, shrink=0.5,
cborientation='vertical',
flags={'rkf_js': False, 'rkf_wd': True,
'rkf_ksp': True, 'rkf_ed': True,
'rkf_nlsd': True, 'rkf_degcen': False,
'rkf_btwcen': False, 'rkf_clscen': False,
'rkf_egnvcen': False})
data_title = 'R-Field measure: Energy Distance'
n_bins = 5
AD, AX = kr.calculate_uncertainty_angdist(rkf_measure='ed',
neigh_orders=[1, 5],
n_bins=n_bins,
data_title=data_title,
throw=True, plot_ad=False)
kr.plot_ang_dist(AD, neigh_orders=[1, 5], n_bins=n_bins,
figsize=(5, 5), dpi=150, data_title=data_title,
cmap='nipy_spectral')
kr.calculate_mprop2d()
"""
# Validations
return cls(upxogs_tgt=upxogs_tgt, upxogs_smp=upxogs_smp,
tgset=tgset, sgset=sgset, ordern=ordern,
tsid_source=tsid_source, ssid_source=ssid_source,
tid=tid, sid=sid,
gstype_tgt=gstype_tgt, gstype_smp=gstype_smp,
_cim_=_cim_)
[docs]
@classmethod
def from_neigh(cls, *, tnset=None, snset=None, ordern=[1],
tsid_source='from_neigh', ssid_source='from_neigh',
tid=None, sid=None, _cim_='from_neigh'):
"""
# Assuming 20 tslices being available witrh increments of tslice=1,
# we will go through the folloing example.
ordern = [1, 3]
from upxo.ggrowth.mcgs import mcgs
tgt = mcgs(study='independent', input_dashboard='mcgs2d_100x100_m50_q10_mcalg201.xls')
tgt.simulate()
tgt.detect_grains()
tslices = np.array(list(tgt.gs.keys()))[1::10]
tnset = {no: {tslice: None for tslice in tslices} for no in ordern}
for no in ordern:
for tslice in tslices:
_ = tgt.gs[tslice].get_upto_nth_order_neighbors_all_grains
tnn = _(no, include_parent=True, output_type='nparray')
tnset[no][tslice] = tnn
smp = mcgs(study='independent', input_dashboard='mcgs2d_100x100_m50_q10_mcalg201.xls')
smp.simulate()
smp.detect_grains()
tslices = np.array(list(smp.gs.keys()))[1::10]
snset = {no: {tslice: None for tslice in tslices} for no in ordern}
for no in ordern:
for tslice in tslices:
_ = smp.gs[tslice].get_upto_nth_order_neighbors_all_grains
snn = _(no, include_parent=True, output_type='nparray')
snset[no][tslice] = snn
from upxo.repqual.grain_network_repr_assesser import KREPR
kr = KREPR.from_neigh(tnset=tnset, snset=tnset,
tid=list(tnset.keys()),
sid=list(snset.keys()),
_cim_='from_neigh')
kr.snset.keys()
kr.snset[3].keys()
kr.snset[3][11]
kr.snset[3][11][40] # <-- O(3) Neigh gids of gid=40 of tslice = 11
kr.ordern
kr.tid
"""
return cls(tnset=tnset, snset=snset, ordern=ordern,
tsid_source=tsid_source, ssid_source=ssid_source,
tid=tid, sid=sid,
_cim_=_cim_)
[docs]
@classmethod
def from_k(cls, *, tkset=None, skset=None, ordern=[1],
tsid_source='from_k', ssid_source='from_k',
tid=None, sid=None,_cim_='from_k'):
"""
# Assuming 20 tslices being available witrh increments of tslice=1,
# we will go through the folloing example.
ordern = [1, 3]
from upxo.ggrowth.mcgs import mcgs
tgt = mcgs(study='independent', input_dashboard='mcgs2d_100x100_m50_q10_mcalg201.xls')
tgt.simulate()
tgt.detect_grains()
tslices = np.array(list(tgt.gs.keys()))[1::10]
tkset = {no: {tslice: None for tslice in tslices} for no in ordern}
for no in ordern:
for tslice in tslices:
_ = tgt.gs[tslice].get_upto_nth_order_neighbors_all_grains
tnn = _(no, include_parent=True, output_type='nparray')
tnn_k = kmake.create_grain_network_nx(tnn)
tkset[no][tslice] = tnn_k
smp = mcgs(study='independent', input_dashboard='mcgs2d_100x100_m50_q10_mcalg201.xls')
smp.simulate()
smp.detect_grains()
tslices = np.array(list(smp.gs.keys()))[1::10]
skset = {no: {tslice: None for tslice in tslices} for no in ordern}
for no in ordern:
for tslice in tslices:
_ = smp.gs[tslice].get_upto_nth_order_neighbors_all_grains
snn = _(no, include_parent=True, output_type='nparray')
snn_k = kmake.create_grain_network_nx(snn)
skset[no][tslice] = snn_k
from upxo.repqual.grain_network_repr_assesser import KREPR
kr = KREPR.from_k(tkset=tkset, skset=tkset,
tid=list(tkset.keys()),
sid=list(skset.keys()),
_cim_='from_k')
kr.tkset
kr.ordern
kr.tid
"""
# Validations
return cls(tkset=tkset, skset=skset, ordern=ordern,
tsid_source=tsid_source, ssid_source=ssid_source,
tid=tid, sid=sid,
_cim_=_cim_)
[docs]
@classmethod
def from_gsgen(cls, gstype_tgt='mcgs2d', gstype_smp='mcgs3d',
is_smp_same_as_tgt=False,
characterize_tgt=True, characterize_smp=True,
tgt_dashboard='input_dashboard_krepr1.xls',
smp_dashboard='input_dashboard_krepr2.xls', ordern=[1],
tsid_source='from_neigh', ssid_source='from_neigh',
tid=None, sid=None, _cim_='from_gsgen'):
"""
Initiate KREPR by generating target and sample grain structure sets.
Parameters
----------
gstype: str
Type of grain structure needed.
Could be deprecated later on.
Defaults to 'mcgs'.
is_smp_same_as_tgt: bool
Defaults to False.
tgt_dashboard: str
Defaults to 'input_dashboard.xls'.
smp_dashboard: str
Defaults to 'input_dashboard.xls'.
_cim_: str
Defaults to 'from_gsgen'.
Explanations
------------
Example
-------
from upxo.repqual.grain_network_repr_assesser import KREPR
kr = KREPR.from_gsgen(gstype='mcgs',
is_smp_same_as_tgt = False,
tgt_dashboard='input_dashboard.xls',
smp_dashboard='input_dashboard.xls',
_cim_='from_gsgen')
kr.tgset
kr.ordern
kr.tid
"""
# --------------------------------------------------------
print('GENERATING TARGET GRAIN STRUCTURES')
tgt = mcgs(study='independent', input_dashboard=tgt_dashboard)
tgt.simulate()
tgt.detect_grains()
if tid is None:
tid = list(tgt.gs.keys())
else:
# validate user input tid
pass
tgset = {i: gs for i, gs in tgt.gs.items()}
# --------------------------------------------------------
if not is_smp_same_as_tgt:
print('GENERATING SAMPLE GRAIN STRUCTURES')
smp = mcgs(study='independent', input_dashboard=smp_dashboard)
smp.simulate()
smp.detect_grains()
if sid is None:
sid = list(smp.gs.keys())
else:
# validate user input tid
pass
sgset = {i: gs for i, gs in smp.gs.items()}
else:
smp = deepcopy(tgt)
sgset = deepcopy(tgset)
# --------------------------------------------------------
if characterize_tgt:
print(50*'#', '\n Characterizing target grain structure database.', '\n', 10*'. ')
for i in tid:
print(f'Target gsid: {i} of len(tid)')
tgt.gs[i].char_morph_2d()
print(50*'#')
if characterize_smp:
print(50*'#', '\n Characterizing sample grain structure database.', '\n', 10*'. ')
for i in sid:
print(f'Target gsid: {i} of len(tid)')
smp.gs[i].char_morph_2d()
print(50*'#')
# --------------------------------------------------------
return cls(gstype_tgt=gstype_tgt, gstype_smp=gstype_smp,
upxogs_tgt=tgt, upxogs_smp=smp,
tgset=tgset, sgset=sgset,
ordern=ordern,
tsid_source=tsid_source, ssid_source=ssid_source,
tid=tid, sid=sid,
_cim_=_cim_)
@property
def creation_method(self):
"""Creation method."""
return self._cim_
[docs]
def init_subdef_set_dim(self):
"""Init subdef set dim."""
if self.gstype.tgt and isinstance(self.gstype.tgt, str):
if '2' in self.gstype.tgt:
self.dim.tgt = 2
elif '3' in self.gstype.tgt:
self.dim.tgt = 3
else:
self.dim.tgt = 2.01 # Assumed to be 2D.
else:
self.dim.tgt = 2.01 # Assumed to be 2D.
if self.gstype.smp and isinstance(self.gstype.smp, str):
if '2' in self.gstype.smp:
self.dim.smp = 2
elif '3' in self.gstype.smp:
self.dim.smp = 3
else:
self.dim.smp = 2.01 # Assumed to be 2D.
else:
self.dim.smp = 2.01 # Assumed to be 2D.
[docs]
def init_subdef_set_gsid(self, data):
"""Init subdef set gsid."""
print('Setting grain structure IDs.')
if data['tsid_source'] == 'from_gs':
from_gs, from_k, from_neigh = True, False, False
elif data['tsid_source'] == 'from_k':
from_gs, from_k, from_neigh = False, True, False
elif data['tsid_source'] == 'from_neigh':
from_gs, from_k, from_neigh = False, False, True
elif data['tsid_source'] == 'from_gsgen':
from_gs, from_k, from_neigh = True, False, False
elif data['tsid_source'] == 'user':
from_gs, from_k, from_neigh = False, False, False
if data['tsid_source'] in ('from_gs', 'from_k',
'from_neigh', 'from_gsgen'):
self.set_tid(from_gs=from_gs, from_k=from_k, from_neigh=from_neigh,
tid=data['tid'])
elif data['tsid_source'] == 'user':
self.tid = data['tid']
if data['ssid_source'] in ('from_gs', 'from_k',
'from_neigh', 'from_gsgen'):
self.set_sid(from_gs=from_gs, from_k=from_k, from_neigh=from_neigh,
sid=data['sid'])
elif data['ssid_source'] == 'user':
self.sid = data['sid']
self.ntid, self.nsid = len(self.tid), len(self.sid)
[docs]
def init_subdef_set_neighs(self, data):
"""Init subdef set neighs."""
self.set_ordern(data['ordern'])
self.find_neigh_order_n(saa=True, throw=False)
[docs]
def init_subdef_set_networks(self):
"""Init subdef set networks."""
self.create_tgt_smp_networks(saa=True, throw=False)
[docs]
def init_subdef_set_prop_flags(self):
"""Init subdef set prop flags."""
if 3 not in (self.dim.tgt, self.dim.smp):
'''
This means that eiythewr:
1. target and sample grain strucures are given to be 2d, or
2. target or sample grain strucvtuer is assumed to be 2d.
'''
self.set_mprop2d_flags()
elif self.dim.tgt == self.dim.smp == 3:
'''
This means that bth target and samnple grain strucruers are 3D.
'''
self.set_mprop3d_flags()
self.set_sprop3d_flags()
elif 2 in (self.dim.tgt, self.dim.smp) or 3 in (self.dim.tgt, self.dim.smp):
'''
This means that the target set and sample sets, each can contain
mixture of 2D and 3D grain strucuersa.
'''
self.set_mprop2d_flags()
self.set_mprop3d_flags()
# self.set_sprop3d_flags()
[docs]
def set_ordern(self, ordern):
"""
Set the n values in O(n).
Parametyers
-----------
ordern: list
O(n) values
Return
------
None
"""
if type(ordern) in NUMBERS:
ordern = [abs(ordern)]
elif type(ordern) in ITERABLES:
if dth.ALL_NUM(ordern):
ordern = [abs(on) for on in ordern]
else:
raise ValueError('Invalid datatype / datatye combinations.')
self.ordern = ordern
[docs]
def set_tid(self, from_gs=False, from_k=False, from_neigh=False,
tid=None):
"""Set or update tid."""
if from_gs and not from_k and not from_neigh:
self.tid = list(self.tgset.keys())
elif not from_gs and from_k and not from_neigh:
self.tid = list(self.tkset.keys())
elif not from_gs and not from_k and from_neigh:
self.tid = list(self.tnset.keys())
else:
self.tid = tid
[docs]
def set_sid(self, from_gs=False, from_k=False, from_neigh=False,
sid=None):
"""Set or update sid."""
if from_gs and not from_k and not from_neigh:
self.sid = list(self.sgset.keys())
elif not from_gs and from_k and not from_neigh:
self.sid = list(self.skset.keys())
elif not from_gs and not from_k and from_neigh:
self.sid = list(self.snset.keys())
else:
self.sid = sid
[docs]
def set_mprop2d_flags(self, area_pix=True, area_geo=False,
gbl_pix=False, gbl_geo=False,
eq_dia=False, ell_a=False, ell_b=False,
inclination=False, aspect_ratio=False,
roundness=False, circularity=False, solidity=False,
formfactor=False, convexity=False, br=False,
gbr=False, fractal_dimension=False
):
"""
Set flags for operational 2D morphological properties.
Parameters
----------
area_pix: Pixel area of the grains. Defaults to True.
area_geo: Geometric area of the grains. Defaults to False.
gbl_pix: Pixel gb length of the grains. Defaults to False.
gbl_geo: Geometric gb length of the grains. Defaults to False.
eq_dia: Equivalent diameter of the grains. Defaults to False.
ell_a: Fit ellipse's major axis length of grains. Defaults to False.
ell_b: Fit ellipse's minor axis length of grains. Defaults to False.
inclination: Morphological inclination of grains. Defaults to False.
The input arguments for characterisaion module for mcgs 2d and their
mapping with the above variables are:
npixels: area_pix
npixels_gb: gbl_pix
area: gbl_pix (preferred as of now)
eq_diameter: eq_dia
perimeter: gbl_pix
perimeter_crofton: not available yet
compactness
gb_length_px
aspect_ratio
solidity
morph_ori
circularity
eccentricity
feret_diameter
major_axis_length
minor_axis_length
euler_number
Data structures
---------------
mprop2d_flags: dict
"""
# Validations
self.mprop2d_flags = {'area_pix': area_pix,
'area_geo': area_geo,
'gbl_pix': gbl_pix,
'gbl_geo': gbl_geo,
'eq_dia': eq_dia,
'ell_a': ell_a,
'ell_b': ell_b,
'inclination': inclination,
'aspect_ratio': aspect_ratio,
'roundness': roundness,
'circularity': circularity,
'solidity': solidity,
'formfactor': formfactor,
'convexity': convexity,
'gbr': gbr,
'fractal_dimension': fractal_dimension
}
[docs]
def set_mprop3d_flags(self, volume_vox=False, volume_geo=False,
eq_dia=False, gba_vox=False, gba_geo=False,
gbl_vox=False, gbl_geo=False, ell_a=False,
ell_b=False, ell_c=False, inclination=False,
sphericity=False, aspectratio_lw=False,
aspectratio_lh=False, aspectratio_wh=False,
vol_surfarea_ratio=False, flatness=False,
compactness=False, iso_parametric_quotient=False,
convextity=False, shape_entropy=False
):
"""
Set flags for operational 3D morphological properties.
Parameters
----------
volume_vox: Voxellated volume of the grains. Defaults to True.
volume_geo: Geometric volume of the grains. Defaults to False.
gba_vox: Voxellated gb surface area of grains. Defaults to False.
gba_geo: Geometric gb surface area of grains. Defaults to False.
gbl_vox: Voxellated gb length of grains. Defaults to False.
gbl_geo: Geometric gb length of grains. Defaults to False.
eq_dia: Equivalent diameter of the grains. Defaults to False.
ell_a: Fit ellipsoid's 1st axis length of grains. Defaults to False.
ell_b: Fit ellipsoid's 2nd axis length of grains. Defaults to False.
ell_c: Fit ellipsoid's 3rd axis length of grains. Defaults to False.
inclination: Morphological inclination of grains. Defaults to False.
Data structures
---------------
mprop3d_flags: dict
"""
# Validations
self.mprop3d_flags = {'volume_vox': volume_vox,
'volume_geo': volume_geo,
'eq_dia': eq_dia,
'gba_vox': gba_vox,
'gba_geo': gba_geo,
'gbl_vox': gbl_vox,
'gbl_geo': gbl_geo,
'ell_a': ell_a,
'ell_b': ell_b,
'ell_c': ell_c,
'inclination': inclination,
'sphericity': sphericity,
'aspectratio_lw': aspectratio_lw,
'aspectratio_lh': aspectratio_lh,
'aspectratio_wh': aspectratio_wh,
'vol_surfarea_ratio': vol_surfarea_ratio,
'flatness': flatness,
'compactness': compactness,
'iso_parametric_quotient': iso_parametric_quotient,
'convextity': convextity,
'shape_entropy': shape_entropy}
[docs]
def set_prop_flag(self, propname, propflagvalue):
"""Set or update prop flag."""
if not isinstance(propflagvalue, bool):
raise TypeError(f'Invalid propflagvalue (={propflagvalue}) type. ',
'Must be bool.')
'''
Your true colours are visible when you are weak. Your collegue's true
colours are also known when you are weak. - Dr. SA
'''
if not isinstance(propname, str):
raise TypeError(f'Invalid propname (={propname}) type. ',
'Must be str.')
if propname in self.mprop2d_flags.keys():
self.mprop2d_flags[propname] = propflagvalue
elif propname in self.mprop3d_flags.keys():
self.mprop3d_flags[propname] = propflagvalue
elif propname in self.sprop2d_flags.keys():
self.sprop2d_flags[propname] = propflagvalue
elif propname in self.sprop3d_flags.keys():
self.sprop3d_flags[propname] = propflagvalue
else:
raise ValueError(f'Invalid property name: {propname}')
[docs]
def calculate_mprop2d(self,
print_msg_tors=True, print_msg_prnm=True,
print_msg_no=True, print_msg_gsid=False,
print_msg_gid=False
):
"""
Data structure
--------------
kr.mprop2d: dict
kr.mprop2d[tors]: dict
kr.mprop2d[tors][prnm]: dict
kr.mprop2d[tors][prnm][no]: dict
kr.mprop2d[tors][prnm][no][gsid]: dict
kr.mprop2d[tors][prnm][no][gsid][gid]: np.array
kr.mprop2d[tors][prnm][no][gsid][gid][i]: float
Where,
mprop2d: 2d morphology properties
tors: either 'tgt' or 'smp'
prnm: property name
no: neighbour order
gsid: grain structure ID
gid: grain ID
i: prnm Property value of ith neighbour of gid grain of tors gid
for O(n) = on.
Data access
-----------
kr.mprop2d['tgt']['area_pix'][O(n)][gsid][GID]. This contains a
list of gids which are O(n) neighbours of GID grain.
Example:
gid = 2
kr.mprop2d['tgt']['area_pix'][1.25][8][gid]
The correspionding neighbour data is:
kr.tnset[1.25][8][gid]
Note
----
len(kr.tnset[1.25][8][gid]) = kr.mprop2d['tgt']['area_pix'][1.25][8][gid].size
@ Dev: Variables
----------------
mpflags: local copy of morpho prop flag.
reqprop: keys in mpflags with True values.
tors: target or sample: self.mprop2d keys.
prnm: property name in the list of values in reqprop.
no: neighbour order in list kr.ordern.
gsid: Grain structue ID in self.tid
gid: Grain IDs in local neighbour network.
Author: Dr. Sunil Anandatheertha
"""
print(40*'#')
mpflags = self.mprop2d_flags
reqprop = [prnm for prnm in mpflags.keys() if mpflags[prnm]]
# -----------------------------------------------
if not reqprop:
self.mprop2d['tgt'] = 'no prop names defined !!'
self.mprop2d['smp'] = 'no prop names defined !!'
print('No properties calculated as no prop names querried.')
return
# -----------------------------------------------
self.mprop2d = {'tgt': None, 'smp': None}
for tors in self.mprop2d.keys():
if print_msg_tors:
print(f'Building tors grain-netork-propety map data for {tors}')
self.mprop2d[tors] = {}
for prnm in reqprop:
if print_msg_prnm:
print(f'Building tors grain-netork-propety map data for property: {prnm}')
if print_msg_no:
print(40*'-')
kprop_on_level = {}
for no in self.ordern:
kprop_gsid_level = {}
if print_msg_no:
print(f'.... {tors}: O(n): {no}.')
GSID = self.tid if tors == 'tgt' else self.sid if tors == 'smp' else None
for igsid, gsid in enumerate(GSID):
if print_msg_gsid:
if igsid % 5 == 0:
print(f'.... GSID n.: {igsid}/{len(self.tid)}.')
mapname = self.mp_gspn_map[prnm]
mprops = self.tgset[gsid].prop[mapname].to_numpy()
kprop_gid_level = {}
for gid in self.tnset[no][gsid].keys():
''' ngids: neighbour grain ids. '''
if print_msg_gid:
print(f'........ gid: {gid}')
ngids = np.array(self.tnset[no][gsid][gid])-1
kprop_gid_level[gid] = mprops[ngids]
kprop_gsid_level[gsid] = kprop_gid_level
kprop_on_level[no] = kprop_gsid_level
self.mprop2d[tors][prnm] = kprop_on_level
[docs]
def estimate_upper_ordern_bycount(self, tors='tgt', gsid=1, on_start=1.0,
on_max=10.0, on_incr=0.5,
neigh_count_vf_max=0.8,
include_parent=True,
kdeplot=True,
kdeplot_kwargs={'figsize': (5, 5),
'dpi': 120,
'fill': True,
'cmap': 'cividis',
'fs_xlabel': 12,
'fs_ylabel': 12,
'fs_legend': 10,
'fs_xticks': 10,
'fs_yticks': 10,
'legend_ncols': 2,
'legend_loc': 'best'
},
statplot=True,
statplot_kwargs={'stat': 'mean',
'figsize': (5, 5),
'dpi': 120},
gsplot=True,
gsplot_kwargs={'figsize': (5, 5),
'dpi': 120},
):
"""
Estimate O(n) needed to reach neigh_count_vf_max.
Parameters
----------
tors: str
Specify 'tgt' for Target and 'smp' for Sample. Defaults to 'tgt'.
gsid: int
Grain Structure ID. Defaults to 1.
on_start: float
Minimum O(n) value to start iterations from. on_start >= 1.
Defaults to 1.0.
on_max: float
Maximum O(n) value to end iterating. on_max >= on_start. Defaults
to 10.0.
on_incr: float
del(O(n)) increments to o(n) search space. on_incr >= 0.1. Defaults
to 0.5.
neigh_count_vf_max: float
neigh_count_vf value to stop iterating.
0.11 < neigh_count_vf_max < 0.99, generally, although value may
change depending on grain structure. Note: these bounds are not
accurate. Defaults to 0.8.
include_parent: bool
Include gid in the neigh list of gid if True, else exclude.
Defaults to True.
plot_kde: bool
Plot kdes of a list containing total number of neighbours of
every gid in the grain structure for each O(n). Defaults to True.
Returns
-------
LON: float
Limiting Order-n
neighn_stats: dict
keys: on of every iteration.
value: dict
(key, value):
'mean' neighn.min()
'min': neighn.min()
'max': neighn.max()
'std': neighn.std()
'var': neighn.var()
'iqr': stats.iqr(neighn): Inter-quartile range
'sem': stats.sem(neighn): Standard Error of the Mean
Where,
neighn = np.array([len(neighs) for neighs in ngh.values()])
ngh: dict: {gid: gid neighbours list}
Ng: int
Number of grains in the provided grain structure.
Explanations
------------
As O(n) increases the number of order-n neighbours (N) for a gid
increases. But, it cannot increase for ever. Its maximum value is
the total number of grains in the grain structure. The ratio of N to
total number of grains (i.e. neigh_count_vf) is then unity. However,
for o(n) < O(n), neigh_count_vf < 1. This function helps determine
o(n) for which neigh_count_vf < neigh_count_vf_max.
The kde if plotted, will show the following trends:
* Shift right as o(n) increases during iterations.
* Peak drops initially as o(n) increases and as width increases.
* Peak increase again as o(n) increases further and width decreases.
*
"""
'''
tors='tgt'
gsid=1
on_start=1
on_max=10
on_incr=0.2
neigh_count_vf_max=0.9
include_parent=True
'''
# Validations.
if tors == 'tgt':
gs, GIDs = self.tgset[gsid], self.tgset[gsid].gid
elif tors == 'smp':
gs, GIDs = self.sgset[gsid], self.sgset[gsid].gid
# -------------------------------------
neighn_stats, neighn_factor, on, kde_plot_i = {}, 0.0, on_start, 0
neighn_values = {}
while neighn_factor < neigh_count_vf_max and on < on_max:
print(f'O(n)={on}')
# SOME CALCULATIONS
ngh = self._find_neigh_order_n_(gs, ordern=on,
include_parent=include_parent,
output_type='list',
print_msg=False)
neighn = np.array([len(neighs) for neighs in ngh.values()])
neighn_values[on] = neighn
neighn_stats[on] = {'distribution': neighn,
'count': neighn.size,
'mean': neighn.mean(),
'min': neighn.min(),
'max': neighn.max(),
'std': neighn.std(),
'var': neighn.var(),
'iqr': stats.iqr(neighn),
'sem': stats.sem(neighn)}
on += on_incr
neighn_factor = neighn.mean()/len(GIDs)
else:
if neighn_factor >= neigh_count_vf_max:
print(f'O(n) max found for neigh_count_vf_max: {neigh_count_vf_max}')
print(f'on: {on-on_incr}. neighn_factor: {neighn_factor}')
elif on >= on_max:
print(f'O(n) max found for the user set, on_max criteria: {neigh_count_vf_max}')
print(f'on: {on-on_incr}. neighn_factor: {neighn_factor}')
LON = np.round(on, 4)
Ng = len(self.tgset[gsid].gid)
'''
Following to wrap up kdeplot after all iterations have completed:
'''
if kdeplot:
plt.figure(figsize=kdeplot_kwargs['figsize'],
dpi=kdeplot_kwargs['dpi'])
cmap = cm.get_cmap(kdeplot_kwargs['cmap'])
i, _neighn_max_ = 1, []
for _on_, neighn in neighn_values.items():
color = cmap(i / len(neighn_values.keys()))
sns.kdeplot(neighn,
color=color,
fill=kdeplot_kwargs['fill'],
label=f'O(n): {_on_}')
_neighn_max_.append(max(neighn))
i += 1
plt.xlabel('GID neighbour counts for O(n)',
fontsize=kdeplot_kwargs['fs_xlabel'])
plt.ylabel('KDE density',
fontsize=kdeplot_kwargs['fs_ylabel'])
plt.legend(fontsize=kdeplot_kwargs['fs_legend'],
ncols=kdeplot_kwargs['legend_ncols'],
loc=kdeplot_kwargs['legend_loc'])
plt.axvline(x=max(_neighn_max_), color='gray',
linestyle='dashed', linewidth=0.5)
plt.text(max(_neighn_max_)*1.02, 0.01, f'Ng: {Ng}',
rotation=90)
# -------------------------------------------------
if statplot:
x = np.array(list(neighn_stats.keys()))
y = np.array([neighn_stats[no]['mean'] for no in neighn_stats.keys()])/Ng
neighn_std = np.array([neighn_stats[no]['std'] for no in neighn_stats.keys()])/Ng
plt.figure(figsize=statplot_kwargs['figsize'],
dpi=statplot_kwargs['dpi'])
if statplot_kwargs['stat'] == 'mean':
plt.fill_between(x, y-neighn_std, y+neighn_std,
color='cyan', alpha=0.5, interpolate=True)
plt.plot([x[0], x[-1]], [1, 1], '--k', lw=1)
plt.errorbar(x, y, yerr=neighn_std, color='k', ecolor='b', lw=1)
plt.xlabel('Neighbour order, O(n)', fontsize=12)
plt.ylabel("N' = No. of neigh. grains / Ng", fontsize=12)
plt.text(x[0], 0.9, f'Ng: {Ng}',
bbox=dict(boxstyle="square", ec='black',
fc='cyan', alpha=0.25),
fontsize=12)
if gsplot:
gs.plotgs(figsize=gsplot_kwargs['figsize'],
dpi=gsplot_kwargs['dpi'])
# -------------------------------------------------
return LON, neighn_stats, Ng
# -------------------------------------
def _find_neigh_order_n_(self, gs, ordern=[1],
include_parent=True,
output_type='nparray',
print_msg=False):
""" find neigh order n ."""
# non = gs.get_upto_nth_order_neighbors_all_grains(ordern,
# include_parent=True,
# output_type='nparray')
non = gs.get_upto_nth_order_neighbors_all_grains_prob(ordern,
recalculate=False,
include_parent=True,
print_msg=False)
return non
[docs]
def find_neigh_order_n(self, saa=True, throw=False):
"""Find neigh order n."""
# Validation
ngh = self._find_neigh_order_n_
# --------------------------------------
print('Starting to extract neighbourhood data for target gs dataset')
tnset = {on: {i: None for i in self.tid} for on in self.ordern}
for on in self.ordern:
for i in self.tid:
if i % 10 == 0:
print(f' O(n): {on}, gsID: {i}/{self.ntid}')
tnset[on][i] = ngh(self.tgset[i], on)
#tnset = {on: {i: ngh(self.tgset[i], on)
# for i in self.tid} for on in self.ordern}
# --------------------------------------
print('Starting to extract neighbourhood data for sample gs dataset')
snset = {on: {i: None for i in self.sid} for on in self.ordern}
for on in self.ordern:
for i in self.sid:
if i % 10 == 0:
print(f' O(n): {on}, gsID: {i}/{self.nsid}')
snset[on][i] = ngh(self.sgset[i], on)
#snset = {on: {i: ngh(self.sgset[i], on)
# for i in self.sid} for on in self.ordern}
# --------------------------------------
if saa:
self.tnset, self.snset = tnset, snset
if throw:
return tnset, snset
[docs]
def create_gid_network(self, dataid='tgt', neigh_order=1, gsid=1):
"""
Create the network nx graph from the neighbours dictionary.
Parameters
----------
dataid: str. Options: 'tgt' (default), 'smp'.
neigh_order: int. Order of the raw neighbours data-structure. Defaults
to 1.
gsid: int. ID of the grain structure. Defaults to 1.
Return
------
nxg: network nx graph.
"""
if dataid == 'tgt':
neighlist = self.tnset[neigh_order][gsid]
elif dataid == 'smp':
neighlist = self.snset[neigh_order][gsid]
kgid = kmake.make_gid_net_from_neighlist(neighlist)
return kgid
[docs]
def create_tgt_networks(self, saa=True, throw=False):
"""
Create networkx graphs for all target gs neighbours database.
Parameters
----------
saa: bool.
Save as attrbute of True. Defults to True.
throw: bool.
Return value if True. Defaults to False.
Data structure
--------------
dict(no1: dict(gsid1: dict(gid1: [12, 1, 16,..]))),
no2: dict(gsid2: dict(gid2: [16, 15, 8,..]))),...
noi: dict(gsidj: dict(gidk: [2, 86, 95,..]))),...
noN: dict(gsidM: dict(gidG: [20, 15, 196,..]))),... )
Where,
noi: an element of ordern list of size N.
gsidj: jth grain structure's ID of a toytal of M grain structes.
gidk: kth grain ID of all G grains.
noi-gsidj-gidk: kth grain ID in the jth grain structure's
neighbour network dictionary of the ith O(n) database.
"""
print(40*'-')
print('Creating networks for target grain structure dataset.')
tkset = {on: {i: None for i in self.tid} for on in self.ordern}
for on in self.ordern:
for tid in self.tid:
if tid % 10 == 0:
print(f' O(n) = {on}, gsID: {tid}/{self.ntid}')
tkset[on][tid] = self.create_gid_network(dataid='tgt',
neigh_order=on,
gsid=tid)
if saa:
self.tkset = tkset
if throw:
return tkset
[docs]
def create_smp_networks(self, saa=True, throw=False):
"""
Create networkx graphs for all sample gs neighbours database.
Parameters
----------
saa: bool.
Save as attrbute of True. Defults to True.
throw: bool.
Return value if True. Defaults to False.
Data structure
--------------
dict(no1: dict(gsid1: dict(gid1: [12, 1, 16,..]))),
no2: dict(gsid2: dict(gid2: [16, 15, 8,..]))),...
noi: dict(gsidj: dict(gidk: [2, 86, 95,..]))),...
noN: dict(gsidM: dict(gidG: [20, 15, 196,..]))),... )
Where,
noi: an element of ordern list of size N.
gsidj: jth grain structure's ID of a toytal of M grain structes.
gidk: kth grain ID of all G grains.
noi-gsidj-gidk: kth grain ID in the jth grain structure's
neighbour network dictionary of the ith O(n) database.
"""
print(40*'-')
print('Creating networks for sample grain structure dataset.')
skset = {on: {i: None for i in self.tid} for on in self.ordern}
for on in self.ordern:
for sid in self.sid:
if sid % 10 == 0:
print(f' O(n) = {on}, gsID: {sid}/{self.nsid}')
skset[on][sid] = self.create_gid_network(dataid='smp',
neigh_order=on,
gsid=sid)
if saa:
self.skset = skset
if throw:
return skset
[docs]
def create_tgt_smp_networks(self, saa=True, throw=False):
"""
Create networkx graphs for all tgt and smp gs neighbours database.
Parameters
----------
saa: bool.
Save as attrbute of True. Defults to True.
throw: bool.
Return value if True. Defaults to False.
Data structure
--------------
dict(no1: dict(gsid1: dict(gid1: [12, 1, 16,..]))),
no2: dict(gsid2: dict(gid2: [16, 15, 8,..]))),...
noi: dict(gsidj: dict(gidk: [2, 86, 95,..]))),...
noN: dict(gsidM: dict(gidG: [20, 15, 196,..]))),... )
Where,
noi: an element of ordern list of size N.
gsidj: jth grain structure's ID of a toytal of M grain structes.
gidk: kth grain ID of all G grains.
noi-gsidj-gidk: kth grain ID in the jth grain structure's
neighbour network dictionary of the ith O(n) database.
"""
tkset = self.create_tgt_networks(saa=False, throw=True)
skset = self.create_smp_networks(saa=False, throw=True)
if saa:
self.tkset, self.skset = tkset, skset
if throw:
return tkset, skset
[docs]
def set_rkf(self, js=False, wd=False, ksp=False, ed=False, nlsd=False,
degcen=False, btwcen=False, clscen=False, egnvcen=False):
"""
Set rkf field calculation flags and initiate rkf dict accordingly.
Parameters
----------
js: bool
Jaccard similarity measure of representativeness.
Defaults to True
wd: bool
Wasserstein distance measure of representativeness.
Defaults to True
ksp: bool
K-S test P-value measure of representativeness.
Defaults to False
ed: bool
Energy distance measure of representativeness.
Defaults to True
nlsd: bool
NetLSD similarity measure of representativeness.
Defaults to False
degcen: bool
Betweenness Centrality. How connected each grain is. Defaults to
False.
btwcen: bool
Betweenness Centrality. How important a grain is in connecting
others. Defaults to False.
clscen: bool
Closeness Centrality. How close a grain is to all other grains.
Defaults to False.
egnvcen: bool
Eigenvector Centrality. How influential a grain is within the
network. Defaults to False.
Data structures
---------------
kr.rkf[RMNAME] = {n: ZEROS for n in kr.ordern}
Where,
MNAME = Repr metric name in ('js', 'wd', 'ksp', 'ed', 'nlsd')
ZEROS = np.zeros((len(kr.sid), len(kr.tid)))
"""
self.rkf_flags = {'js': js,
'wd': wd,
'ksp': ksp,
'ed': ed,
'nlsd': nlsd,
'degcen': degcen,
'btwcen': btwcen,
'clscen': clscen,
'egnvcen': egnvcen}
self.initiate_rk_dict(js=js, wd=wd, ksp=ksp, ed=ed, nlsd=nlsd,
degcen=degcen, btwcen=btwcen, clscen=clscen,
egnvcen=egnvcen)
[docs]
def initiate_rk_dict(self, js=False, wd=False, ksp=False, ed=False,
nlsd=False, degcen=False, btwcen=False,
clscen=False, egnvcen=False):
"""
Initiate dictionaries to store representativeness measures.
Parameters
----------
js: bool
Jaccard similarity measure of representativeness.
Defaults to True
wd: bool
Wasserstein distance measure of representativeness.
Defaults to True
ksp: bool
K-S test P-value measure of representativeness.
Defaults to False
ed: bool
Energy distance measure of representativeness.
Defaults to True
nlsd: bool
NetLSD similarity measure of representativeness.
Defaults to False
Data structures
---------------
kr.rkf[RMNAME] = {n: ZEROS for n in kr.ordern}
Where,
MNAME = Repr metric name in ('js', 'wd', 'ksp', 'ed', 'nlsd')
ZEROS = np.zeros((len(kr.sid), len(kr.tid)))
"""
print(40*'-')
print('Creating R-field data structures.')
self.rkf = {}
nrc = len(self.sid), len(self.tid)
data_structure = {n: np.zeros(nrc) for n in self.ordern}
self.rkf['js'] = DCOPY(data_structure) if js else None
self.rkf['wd'] = DCOPY(data_structure) if wd else None
self.rkf['ksp'] = DCOPY(data_structure) if ksp else None
self.rkf['ed'] = DCOPY(data_structure) if ed else None
self.rkf['nlsd'] = DCOPY(data_structure) if nlsd else None
self.rkf['degcen'] = DCOPY(data_structure) if degcen else None
self.rkf['btwcen'] = DCOPY(data_structure) if btwcen else None
self.rkf['clscen'] = DCOPY(data_structure) if clscen else None
self.rkf['egnvcen'] = DCOPY(data_structure) if egnvcen else None
[docs]
def calculate_kdeg(self, ktgt, ksmp):
"""
Calculate the node degrees of target and sample gs O(n) networks.
Paramerters
-----------
ktgt: target grain structure O(n) neighbour network graph.
ksmp: sample grain structure O(n) neighbour network graph.
Return
------
kd_tgt: node degrees of target gs O(n) neigh network graph.
kd_smp: node degrees of sample gs O(n) neigh network graph.
Data structures
---------------
ktgt: networkx graph for target gs's O(n) neighbour netwprk dict data.
ksmp: networkx graph for sample gs's O(n) neighbour netwprk dict data.
kd_tgt: list: nodal degres of ktgt
kd_smp: list: nodal degres of ksmp
Exzplanations
-------------
This def calls for calculate_kdegrees. Please refer to
upxo.netops.kchar.calculate_kdegrees for complete documentaion.
"""
# Validations
kd_tgt, kd_smp = kchar.calculate_kdegrees([ktgt, ksmp])
return kd_tgt, kd_smp
[docs]
def calculate_kdeg_equal_binning(self, ktgt, ksmp):
"""
Calculate the node degrees of T and S gs O(n) k's and equally bin them.
Paramerters
-----------
ktgt: target grain structure O(n) neighbour network graph.
ksmp: sample grain structure O(n) neighbour network graph.
Return
------
kd_tgt: node degrees of target gs O(n) neigh network graph.
kd_smp: node degrees of sample gs O(n) neigh network graph.
Data structures
---------------
ktgt: networkx graph for target gs's O(n) neighbour netwprk dict data.
ksmp: networkx graph for sample gs's O(n) neighbour netwprk dict data.
kd_tgt: list: nodal degres of ktgt
kd_smp: list: nodal degres of ksmp
Exzplanations
-------------
This def calls for calculate_kdegrees_equalbinning. Please refer to
upxo.netops.kchar.calculate_kdegrees_equalbinning for complete
documentaion.
Data is binned as per global min and max in degree and the distribtuion
is re-computed using histogram.
"""
# Validations
kd_tgt, kd_smp = kchar.calculate_kdegrees_equalbinning([ktgt, ksmp])
return kd_tgt, kd_smp
[docs]
def calculate_rkf_js_pairwise(self, ktgt, ksmp):
"""
Calculate Jaccard similarity between ktgt and ksmp.
Parameters
----------
ktgt: target grain structure O(n) neighbour network graph.
ksmp: sample grain structure O(n) neighbour network graph.
Return
------
r: representativeness level.
Explanations
------------
Refer to calculate_rkfield_js for complete documentation.
Location: upxo.netops.kcmp.calculate_rkfield_js
Data structures
---------------
ktgt: networkx graph for target gs's O(n) neighbour netwprk dict data.
ksmp: networkx graph for sample gs's O(n) neighbour netwprk dict data.
r: int between 0 and 1. Higher the value, greater is
the representativeness.
"""
r = kcmp.calculate_rkfield_js(ktgt, ksmp)
return r
[docs]
def calculate_rkf_wd_pairwise(self, ktgt, ksmp, equal_bins=False):
"""
Calculate Jaccard similarity between ktgt and ksmp.
Parameters
----------
ktgt: target grain structure O(n) neighbour network graph.
ksmp: sample grain structure O(n) neighbour network graph.
Return
------
r: representativeness level.
Explanations
------------
Refer to calculate_rkfield_wd for complete documentation.
Location: upxo.netops.kcmp.calculate_rkfield_wd
Data structures
---------------
ktgt: networkx graph for target gs's O(n) neighbour netwprk dict data.
ksmp: networkx graph for sample gs's O(n) neighbour netwprk dict data.
r: int between 0 and 1. Higher the value, greater is
the representativeness.
"""
# Validations
if equal_bins:
kd_tgt, kd_smp = self.calculate_kdeg_equal_binning(ktgt, ksmp)
else:
kd_tgt, kd_smp = self.calculate_kdeg(ktgt, ksmp)
r = kcmp.calculate_rkfield_wd(kd_tgt, kd_smp)
return r
[docs]
def calculate_rkf_ksp_pairwise(self, ktgt, ksmp, equal_bins=False):
"""
Calculate Jaccard similarity between ktgt and ksmp.
Parameters
----------
ktgt: target grain structure O(n) neighbour network graph.
ksmp: sample grain structure O(n) neighbour network graph.
Return
------
r: representativeness level.
Explanations
------------
Refer to calculate_rkfield_ksp for complete documentation.
Location: upxo.netops.kcmp.calculate_rkfield_ksp
Data structures
---------------
ktgt: networkx graph for target gs's O(n) neighbour netwprk dict data.
ksmp: networkx graph for sample gs's O(n) neighbour netwprk dict data.
r: int between 0 and 1. Higher the value, greater is
the representativeness.
"""
# Validations
if equal_bins:
kd_tgt, kd_smp = self.calculate_kdeg_equal_binning(ktgt, ksmp)
else:
kd_tgt, kd_smp = self.calculate_kdeg(ktgt, ksmp)
r = kcmp.calculate_rkfield_ksp(kd_tgt, kd_smp)
return r
[docs]
def calculate_rkf_ed_pairwise(self, ktgt, ksmp, equal_bins=False):
"""
Calculate Jaccard similarity between ktgt and ksmp.
Parameters
----------
ktgt: target grain structure O(n) neighbour network graph.
ksmp: sample grain structure O(n) neighbour network graph.
Return
------
r: representativeness level.
Explanations
------------
Refer to calculate_rkfield_ed for complete documentation.
Location: upxo.netops.kcmp.calculate_rkfield_ed
Data structures
---------------
ktgt: networkx graph for target gs's O(n) neighbour netwprk dict data.
ksmp: networkx graph for sample gs's O(n) neighbour netwprk dict data.
r: int between 0 and 1. Higher the value, greater is
the representativeness.
"""
# Validations
if equal_bins:
kd_tgt, kd_smp = self.calculate_kdeg_equal_binning(ktgt, ksmp)
else:
kd_tgt, kd_smp = self.calculate_kdeg(ktgt, ksmp)
r = kcmp.calculate_rkfield_ed(kd_tgt, kd_smp)
return r
[docs]
def calculate_rkf_nlsd_pairwise(self, ktgt, ksmp,
timescales=np.logspace(-2, 2, 20),
equal_bins=False):
"""
Calculate Jaccard similarity between ktgt and ksmp.
Parameters
----------
ktgt: target grain structure O(n) neighbour network graph.
ksmp: sample grain structure O(n) neighbour network graph.
Return
------
r: representativeness level.
Explanations
------------
Refer to calculate_rkfield_nlsd for complete documentation.
Location: upxo.netops.kcmp.calculate_rkfield_nlsd
Data structures
---------------
ktgt: networkx graph for target gs's O(n) neighbour netwprk dict data.
ksmp: networkx graph for sample gs's O(n) neighbour netwprk dict data.
r: int between 0 and 1. Higher the value, greater is
the representativeness.
"""
# Validations
if equal_bins:
kd_tgt, kd_smp = self.calculate_kdeg_equal_binning(ktgt, ksmp)
else:
kd_tgt, kd_smp = self.calculate_kdeg(ktgt, ksmp)
r = kcmp.calculate_rkfield_nlsd(kd_tgt, kd_smp,
timescales=timescales)
return r
[docs]
def calculate_rkf_js_on(self, neigh_order=1):
"""
Parameters
----------
notgt: neighbour order of interest for target
nosmp: neighbour order of interest for sample
"""
print(f'Calculating RKF-JS for neighbour order {neigh_order}')
# Validations
tkset = list(self.tkset[neigh_order].values())
skset = list(self.skset[neigh_order].values())
DEF_rkf_js = self.calculate_rkf_js_pairwise
for idtgt, ktgt in enumerate(tkset):
for idsmp, ksmp in enumerate(skset):
r = DEF_rkf_js(ktgt, ksmp)
self.rkf['js'][neigh_order][idsmp, idtgt] = r
[docs]
def calculate_rkf_wd_on(self, neigh_order=1, equal_bins=False):
"""Calculate rkf wd on."""
# Validations
print(f'Calculating RKF-WD for neighbour order {neigh_order}')
tkset = list(self.tkset[neigh_order].values())
skset = list(self.skset[neigh_order].values())
DEF_rkf_wd = self.calculate_rkf_wd_pairwise
for idtgt, ktgt in enumerate(tkset):
for idsmp, ksmp in enumerate(skset):
r = DEF_rkf_wd(ktgt, ksmp, equal_bins=equal_bins)
self.rkf['wd'][neigh_order][idsmp, idtgt] = r
[docs]
def calculate_rkf_wd_on_generalized(self,
neigh_order_tgt=1,
neigh_order_smp=1,
equal_bins=False):
"""Calculate rkf wd on generalized."""
# Validations
print(f'Calculating RKF-JS for T-O({neigh_order_tgt})|S-O({neigh_order_smp})')
tkset = list(self.tkset[neigh_order_tgt].values())
skset = list(self.skset[neigh_order_smp].values())
# ---------------------------
DEF_rkf_wd = self.calculate_rkf_wd_pairwise
rkf_wd = np.zeros((self.nsid, self.ntid))
# ---------------------------
for idtgt, ktgt in enumerate(tkset):
for idsmp, ksmp in enumerate(skset):
r = DEF_rkf_wd(ktgt, ksmp, equal_bins=equal_bins)
rkf_wd[idsmp, idtgt] = r
[docs]
def calculate_rkf_ksp_on(self, neigh_order=1, equal_bins=False):
"""Calculate rkf ksp on."""
# Validations
print(f'Calculating RKF-KSP for neighbour order {neigh_order}')
tkset = list(self.tkset[neigh_order].values())
skset = list(self.skset[neigh_order].values())
DEF_rkf_ksp = self.calculate_rkf_ksp_pairwise
for idtgt, ktgt in enumerate(tkset):
for idsmp, ksmp in enumerate(skset):
r = DEF_rkf_ksp(ktgt, ksmp, equal_bins=equal_bins)
self.rkf['ksp'][neigh_order][idsmp, idtgt] = r
[docs]
def calculate_rkf_ksp_on_generalized(self,
neigh_order_tgt=1,
neigh_order_smp=1,
equal_bins=False):
"""Calculate rkf ksp on generalized."""
# Validations
print(f'Calculating RKF-KSP for T-O({neigh_order_tgt})|S-O({neigh_order_smp})')
tkset = list(self.tkset[neigh_order_tgt].values())
skset = list(self.skset[neigh_order_smp].values())
# ---------------------------
DEF_rkf_ksp = self.calculate_rkf_ksp_pairwise
rkf_ksp = np.zeros((self.nsid, self.ntid))
# ---------------------------
for idtgt, ktgt in enumerate(tkset):
for idsmp, ksmp in enumerate(skset):
r = DEF_rkf_ksp(ktgt, ksmp, equal_bins=equal_bins)
rkf_ksp[idsmp, idtgt] = r
[docs]
def calculate_rkf_ed_on(self, neigh_order=1, equal_bins=False):
"""Calculate rkf ed on."""
# Validations
print(f'Calculating RKF-ED for neighbour order {neigh_order}')
tkset = list(self.tkset[neigh_order].values())
skset = list(self.skset[neigh_order].values())
DEF_rkf_ed = self.calculate_rkf_ed_pairwise
for idtgt, ktgt in enumerate(tkset):
for idsmp, ksmp in enumerate(skset):
r = DEF_rkf_ed(ktgt, ksmp, equal_bins=equal_bins)
self.rkf['ed'][neigh_order][idsmp, idtgt] = r
[docs]
def calculate_rkf_ed_on_generalized(self,
neigh_order_tgt=1,
neigh_order_smp=1,
equal_bins=False):
"""Calculate rkf ed on generalized."""
# Validations
print(f'Calculating RKF-ED for T-O({neigh_order_tgt})|S-O({neigh_order_smp})')
tkset = list(self.tkset[neigh_order_tgt].values())
skset = list(self.skset[neigh_order_smp].values())
# ---------------------------
DEF_rkf_ed = self.calculate_rkf_ed_pairwise
rkf_ed = np.zeros((self.nsid, self.ntid))
# ---------------------------
for idtgt, ktgt in enumerate(tkset):
for idsmp, ksmp in enumerate(skset):
r = DEF_rkf_ed(ktgt, ksmp, equal_bins=equal_bins)
rkf_ed[idsmp, idtgt] = r
[docs]
def calculate_rkf_nlsd_on(self, neigh_order=1,
timescales=np.logspace(-2, 2, 20),
equal_bins=False):
"""Calculate rkf nlsd on."""
# Validations
print(f'Calculating RKF-NLSD for neighbour order {neigh_order}')
tkset = list(self.tkset[neigh_order].values())
skset = list(self.skset[neigh_order].values())
DEF_rkf_nlsd = self.calculate_rkf_nlsd_pairwise
for idtgt, ktgt in enumerate(tkset):
for idsmp, ksmp in enumerate(skset):
r = DEF_rkf_nlsd(ktgt, ksmp,
timescales=timescales,
equal_bins=equal_bins)
self.rkf['nlsd'][neigh_order][idsmp, idtgt] = r
[docs]
def calculate_rkf_ed_nlsd_generalized(self,
neigh_order_tgt=1,
neigh_order_smp=1,
equal_bins=False):
"""Calculate rkf ed nlsd generalized."""
# Validations
print(f'Calculating RKF-NLSD for T-O({neigh_order_tgt})|S-O({neigh_order_smp})')
tkset = list(self.tkset[neigh_order_tgt].values())
skset = list(self.skset[neigh_order_smp].values())
# ---------------------------
DEF_rkf_nlsd = self.calculate_rkf_nlsd_pairwise
rkf_nlsd = np.zeros((self.nsid, self.ntid))
# ---------------------------
for idtgt, ktgt in enumerate(tkset):
for idsmp, ksmp in enumerate(skset):
r = DEF_rkf_nlsd(ktgt, ksmp, equal_bins=equal_bins)
rkf_nlsd[idsmp, idtgt] = r
[docs]
def calculate_rkf_js(self):
"""Calculate rkf js."""
for no in self.ordern:
self.calculate_rkf_js_on(neigh_order=no)
[docs]
def calculate_rkf_wd(self):
"""Calculate rkf wd."""
for no in self.ordern:
self.calculate_rkf_wd_on(neigh_order=no)
[docs]
def calculate_rkf_ksp(self):
"""Calculate rkf ksp."""
for no in self.ordern:
self.calculate_rkf_ksp_on(neigh_order=no)
[docs]
def calculate_rkf_ed(self):
"""Calculate rkf ed."""
for no in self.ordern:
self.calculate_rkf_ed_on(neigh_order=no)
[docs]
def calculate_rkf_nlsd(self, timescales=np.logspace(-2, 2, 20),
equal_bins=False):
"""Calculate rkf nlsd."""
# Validations
for no in self.ordern:
self.calculate_rkf_nlsd_on(neigh_order=no,
timescales=timescales,
equal_bins=equal_bins)
[docs]
def calculate_rkf_pairwise(self, neigh_order,
idtgt, idsmp,
prop='kdegree', printmsg=False):
"""Calculate rkf pairwise."""
# Validations
ktgt = self.tkset[neigh_order][self.tid[idtgt]]
ksmp = self.skset[neigh_order][self.sid[idsmp]]
if self.rkf_flags['js']:
r = self.calculate_rkf_js_pairwise(ktgt, ksmp)
self.rkf['js'][neigh_order][idsmp, idtgt] = r
if printmsg:
printstr1 = f'RKF-JS for O({neigh_order})'
printstr2 = f', tid={idtgt}, sid={idsmp} = {r}'
print(printstr1+printstr2)
if self.rkf_flags['wd']:
r = self.calculate_rkf_wd_pairwise(ktgt, ksmp)
self.rkf['wd'][neigh_order][idsmp, idtgt] = r
if printmsg:
printstr1 = f'RKF-WD for O({neigh_order})'
printstr2 = f', tid={idtgt}, sid={idsmp} = {r}'
print(printstr1+printstr2)
if self.rkf_flags['ksp']:
r = self.calculate_rkf_ksp_pairwise(ktgt, ksmp)
self.rkf['ksp'][neigh_order][idsmp, idtgt] = r
if printmsg:
printstr1 = f'RKF-KSP for O({neigh_order})'
printstr2 = f', tid={idtgt}, sid={idsmp} = {r}'
print(printstr1+printstr2)
if self.rkf_flags['ed']:
r = self.calculate_rkf_ed_pairwise(ktgt, ksmp)
self.rkf['ed'][neigh_order][idsmp, idtgt] = r
if printmsg:
printstr1 = f'RKF-ED for O({neigh_order})'
printstr2 = f', tid={idtgt}, sid={idsmp} = {r}'
print(printstr1+printstr2)
if self.rkf_flags['nlsd']:
r = self.calculate_rkf_nlsd_pairwise(ktgt, ksmp)
self.rkf['nlsd'][neigh_order][idsmp, idtgt] = r
if printmsg:
printstr1 = f'RKF-NLSD for O({neigh_order})'
printstr2 = f', tid={idtgt}, sid={idsmp} = {r}'
print(printstr1+printstr2)
[docs]
def calculate_rkf_pairwise_generalized(self,
neigh_order_tgt, neigh_order_smp,
idtgt, idsmp,
prop='kdegree', printmsg=False):
"""Calculate rkf pairwise generalized."""
# Validations
ktgt = self.tkset[neigh_order_tgt][idtgt]
ksmp = self.skset[neigh_order_smp][idsmp]
r_js, r_wd, r_ksp, r_ed, r_nlsd = None, None, None, None, None
if self.rkf_flags['js']:
r_js = self.calculate_rkf_js_pairwise(ktgt, ksmp)
if printmsg:
printstr1 = f'RKF-JS for T-O({neigh_order_tgt})|S-O({neigh_order_smp})'
printstr2 = f', tid={idtgt}, sid={idsmp} = {r_js}'
print(printstr1+printstr2)
if self.rkf_flags['wd']:
r_wd = self.calculate_rkf_wd_pairwise(ktgt, ksmp)
if printmsg:
printstr1 = f'RKF-WD for T-O({neigh_order_tgt})|S-O({neigh_order_smp})'
printstr2 = f', tid={idtgt}, sid={idsmp} = {r_wd}'
print(printstr1+printstr2)
if self.rkf_flags['ksp']:
r_ksp = self.calculate_rkf_ksp_pairwise(ktgt, ksmp)
if printmsg:
printstr1 = f'RKF-KSP for T-O({neigh_order_tgt})|S-O({neigh_order_smp})'
printstr2 = f', tid={idtgt}, sid={idsmp} = {r_ksp}'
print(printstr1+printstr2)
if self.rkf_flags['ed']:
r_ed = self.calculate_rkf_ed_pairwise(ktgt, ksmp)
if printmsg:
printstr1 = f'RKF-ED for T-O({neigh_order_tgt})|S-O({neigh_order_smp})'
printstr2 = f', tid={idtgt}, sid={idsmp} = {r_ed}'
print(printstr1+printstr2)
if self.rkf_flags['nlsd']:
r_nlsd = self.calculate_rkf_nlsd_pairwise(ktgt, ksmp)
if printmsg:
printstr1 = f'RKF-NLSD for T-O({neigh_order_tgt})|S-O({neigh_order_smp})'
printstr2 = f', tid={idtgt}, sid={idsmp} = {r_nlsd}'
print(printstr1 + printstr2)
return r_js, r_wd, r_ksp, r_ed, r_nlsd
[docs]
def calculate_rkf_no(self, neigh_order, prop='kdegree'):
"""Calculate rkf no."""
for I, idtgt in enumerate(self.tid, start=0):
for J, idsmp in enumerate(self.sid, start=0):
if idtgt % 5 == idsmp % 20 == 0:
print(f' O(n): {neigh_order}, gsID pair: ({idtgt}-{idsmp})')
self.calculate_rkf_pairwise(neigh_order, I, J, prop=prop)
[docs]
def calculate_rkf(self, prop='kdegree'):
"""
Calculate the network R-Field values for entire tgt and smp database.
Parmeters
---------
prop: str
property name. Defaults to 'kdegree'. Options include:
* 'kdegree'
* 'area_pixel'
* 'volume_voxel'
* 'gblength_pixel'
* 'gblength_geom2'
* 'gblength_voxel'
* 'gblength_geom3'
* 'gbarea_voxels'
* 'gbarea_geom'
* 'gbrough_r'
* 'ntjp'
Explanations
------------
User specified boolean flags in rkf_flags dictate which R-field
metrics would be calculated.
"""
print('++++++++++++++++++++++++++++++++++++++')
print(str(self.ordern), '-------------')
print('++++++++++++++++++++++++++++++++++++++')
for no in self.ordern:
print(40*'-')
print(f'Calculating R-field.')
self.calculate_rkf_no(no, prop=prop)
[docs]
def calculate_uncertainty_angdist(self,
rkf_measure='js',
neigh_orders=[1],
n_bins=30,
data_title='Jaccard sim. measure',
throw=False,
plot_ad=True):
"""Calculate uncertainty angdist."""
# Validations
if rkf_measure in self.rkf_flags.keys():
if self.rkf_flags[rkf_measure]:
DATA = self.rkf[rkf_measure]
else:
print(f'rkf_measure: {rkf_measure} not calculated.')
return
else:
print(f'Invalid rkf_measure: {rkf_measure}.')
return
# ---------------------------------------------
ANG_DISTANCE = {i: {'bin_means': None,
'min': None,
'mean': None,
'max': None,
'std': None,
'nbins': n_bins} for i in neigh_orders}
# ---------------------------------------------
for no in neigh_orders:
print(f'Calculating uncertainty measure: Ang. Dist. for {rkf_measure} at O(n): {no}')
bin_means, DATA_approx = approximate_to_bin_means(DATA[no],
n_bins=n_bins)
ang_dist_min = np.zeros_like(bin_means)
ang_dist_mean = np.zeros_like(bin_means)
ang_dist_max = np.zeros_like(bin_means)
ang_dist_std = np.zeros_like(bin_means)
for bm_i, bm in enumerate(bin_means):
print(f'....U(RKF: {rkf_measure}) at O({no}): bin {bm_i}/{len(bin_means)} ')
bm_locs = np.argwhere(DATA_approx == bm)
bin_means_sparse = np.zeros((bm_locs.shape[0],
bm_locs.shape[0]))
ang_dist_sparse = np.zeros((bm_locs.shape[0],
bm_locs.shape[0]))
for i in range(bm_locs.shape[0]):
for j in range(bm_locs.shape[0]):
if i > j:
# Only find the upper tri matrix, thats enough.
ang_dist_sparse[j, i] = calc_angdist(bm_locs[j],
bm_locs[i])
else:
# Nothing left to do here.
pass
# plt.imshow(ang_dist_sparse)
ang_dist_sparse = np.unique(ang_dist_sparse)
ang_dist_sparse_compact = ang_dist_sparse[np.nonzero(ang_dist_sparse)[0]]
if ang_dist_sparse_compact.size == 0:
ang_dist_min[bm_i] = np.NaN
ang_dist_mean[bm_i] = np.NaN
ang_dist_max[bm_i] = np.NaN
ang_dist_std[bm_i] = np.NaN
else:
ang_dist_min[bm_i] = ang_dist_sparse_compact.min()
ang_dist_mean[bm_i] = ang_dist_sparse_compact.mean()
ang_dist_max[bm_i] = ang_dist_sparse_compact.max()
ang_dist_std[bm_i] = ang_dist_sparse_compact.std()
ANG_DISTANCE[no]['bin_means'] = bin_means
ANG_DISTANCE[no]['min'] = ang_dist_min
ANG_DISTANCE[no]['mean'] = ang_dist_mean
ANG_DISTANCE[no]['max'] = ang_dist_max
ANG_DISTANCE[no]['std'] = ang_dist_std
AX = self.plot_ang_dist(ANG_DISTANCE, n_bins=n_bins,
neigh_orders=neigh_orders,
figsize=(5, 5), dpi=150,
data_title=data_title,
cmap='nipy_spectral') if plot_ad else None
if throw:
return ANG_DISTANCE, AX
[docs]
def plot_ang_dist(self, ANG_DISTANCE, n_bins, neigh_orders=[1],
figsize=(5, 5), dpi=150,
data_title='DATA TITLE',
cmap='nipy_spectral', throw_axis=True
):
"""Visualise ang dist using Matplotlib or PyVista."""
plt.figure(figsize=figsize,
dpi=dpi,
constrained_layout=True)
# Choose a colormap (e.g., 'viridis', 'plasma', 'tab20')
cmap = cm.get_cmap(cmap)
num_colors = len(neigh_orders) # Number of colors needed
legends, legend_names = [], []
color_increment = 1.0 / (len(neigh_orders) + 1) # Add 1 to avoid using the last color in the colormap, which is often too light
for i, neigh_order in enumerate(neigh_orders):
color = cmap(color_increment * (i + 1)) # Use color_increment to space out the colors
line_1, = plt.plot(ANG_DISTANCE[neigh_order]['bin_means'][:-1],
ANG_DISTANCE[neigh_order]['mean'][:-1],
linestyle='-', color=color,
marker='s', markersize=5, markerfacecolor=color)
fill_1 = plt.fill_between(ANG_DISTANCE[neigh_order]['bin_means'][:-1],
ANG_DISTANCE[neigh_order]['mean'][:-1] - ANG_DISTANCE[neigh_order]['std'][:-1],
ANG_DISTANCE[neigh_order]['mean'][:-1] + ANG_DISTANCE[neigh_order]['std'][:-1],
color=color, alpha=0.2)
legends.append((line_1, fill_1))
legend_names.append(f'Neigh order, O({neigh_order})')
plt.margins(x=0)
plt.legend(legends, legend_names, facecolor='none', edgecolor='none', loc=1)
ax=plt.gca()
ax.set_xlim(0, 1.0)
ax.set_ylim(0, 1.6)
ax.set_xlabel(data_title, fontsize=10)
ax.set_ylabel('Uncertainty (Mean angular distance), @Iso-R-bins, radians', fontsize=10)
ax.set_xticks(np.arange(0, 1.1, 0.1))
plt.grid(True, linestyle=':', color='gray', alpha=0.2)
plt.text(0.025, 1.525, f'No. of bins: {n_bins}', fontsize=10)
return ax
ax.set_xlim(0.94, 1.0)
ax.set_xticks(np.arange(0.94, 1.0, 0.02))
[docs]
def plot_rkf(self, neigh_orders=[1], power=1, figsize=(7, 5), dpi=120,
xtick_incr=2, ytick_incr=2,
lfs=7, tfs=8,
cmap='nipy_spectral',
cbarticks=np.arange(0, 1.1, 0.1),
cbfs=10,
cbtitle='Measure of representativeness R(S|T)',
cbfraction=0.046, cbpad=0.04,
cbaspect=30, shrink=0.5,
cborientation='vertical',
flags={'rkf_js': False,
'rkf_wd': False,
'rkf_ksp': False,
'rkf_ed': False,
'rkf_nlsd': False,
'rkf_degcen': False,
'rkf_btwcen': False,
'rkf_clscen': False,
'rkf_egnvcen': False,
},
xlabel='Target GS ID',
ylabel='Sample GS ID',
):
"""
Example
-------
import numpy as np
from upxo.repqual.grain_network_repr_assesser import KREPR
import matplotlib.pyplot as plt
kr = KREPR.from_gsgen(gstype='mcgs',
is_smp_same_as_tgt = False,
tgt_dashboard='input_dashboard.xls',
smp_dashboard='input_dashboard.xls',
ordern=[1, 3, 5],
tsid_source='from_gs',
ssid_source='from_gs',
tid=None, sid=None,
_cim_='from_gsgen')
kr.set_rkf(js=True, wd=True, ksp=False, ed=True, nlsd=False)
kr.calculate_rkf()
kr.plot_rkf(neigh_orders=[1, 3, 5], figsize=(7, 5), dpi=50,
xtick_incr=2, ytick_incr=2,
lfs=7, tfs=8,
cmap='nipy_spectral',
cbarticks=np.arange(0, 1.1, 0.1),
cbfs=10,
cbtitle='Measure of representativeness R(S|T)',
cbfraction=0.046, cbpad=0.04, cbaspect=15, shrink=0.4,
cborientation='vertical',
plot_rkf_js=False)
"""
# Validations
flag_js = self.rkf_flags['js'] and flags['rkf_js']
flag_wd = self.rkf_flags['wd'] and flags['rkf_wd']
flag_ksp = self.rkf_flags['ksp'] and flags['rkf_ksp']
flag_ed = self.rkf_flags['ed'] and flags['rkf_ed']
flag_nlsd = self.rkf_flags['nlsd'] and flags['rkf_nlsd']
flag_degcen = self.rkf_flags['degcen'] and flags['rkf_degcen']
flag_btwcen = self.rkf_flags['btwcen'] and flags['rkf_btwcen']
flag_clscen = self.rkf_flags['clscen'] and flags['rkf_clscen']
flag_egnvcen = self.rkf_flags['egnvcen'] and flags['rkf_egnvcen']
flags = [flag_js, flag_wd, flag_ksp, flag_ed, flag_nlsd,
flag_degcen, flag_btwcen, flag_clscen, flag_egnvcen]
if not any(flags):
print('Nothing to plot')
return
# -------------------------------------------
fig, ax = plt.subplots(nrows=len(neigh_orders),
ncols=np.argwhere(flags).size,
figsize=figsize,
dpi=dpi,
constrained_layout=True,
sharex=True,
sharey=True)
# -------------------------------------------
xticks = np.arange(0, len(self.tid), xtick_incr)
yticks = np.arange(0, len(self.sid), ytick_incr)
# -------------------------------------------
if len(neigh_orders) == 1 and np.argwhere(flags).size == 1:
single_plot = True
else:
single_plot = False
# ---------------------
if len(neigh_orders) > 1 and np.argwhere(flags).size == 1:
col_plot = True
else:
col_plot = False
# ---------------------
if len(neigh_orders) == 1 and np.argwhere(flags).size > 1:
row_plot = True
else:
row_plot = False
# ---------------------
if not single_plot and not col_plot and not row_plot:
matrix_type_plot = True
else:
matrix_type_plot = False
# -------------------------------------------
R = 0
for no in neigh_orders:
C = 0
if flag_js:
# print(f'JS. no: {no}, R: {R}, C: {C}')
if single_plot: AX = ax
elif col_plot: AX = ax[R]
elif row_plot: AX = ax[C]
elif matrix_type_plot: AX = ax[R, C]
data = np.power(self.rkf['js'][no], power)
imh = AX.imshow(data, cmap=cmap, vmin=0, vmax=1)
AX.set_xlabel(xlabel, fontsize=lfs)
AX.set_ylabel(ylabel, fontsize=lfs)
ts = f'Jaccard sim. measure,\n O(n)={no}'
AX.set_title(ts, fontsize=tfs)
AX.invert_yaxis()
AX.set_xticks(xticks)
AX.set_yticks(yticks)
AX.tick_params(axis='both', which='major', labelsize=lfs)
C += 1
if flag_wd:
# print(f'WD. no: {no}, R: {R}, C: {C}')
if single_plot: AX = ax
elif col_plot: AX = ax[R]
elif row_plot: AX = ax[C]
elif matrix_type_plot: AX = ax[R, C]
data = np.power(self.rkf['wd'][no], power)
imh = AX.imshow(data, cmap=cmap, vmin=0, vmax=1)
AX.set_xlabel(xlabel, fontsize=lfs)
AX.set_ylabel(ylabel, fontsize=lfs)
ts = f'Wasserstein distance based sim.\n measure, O(n)={no}'
AX.set_title(ts, fontsize=tfs)
AX.invert_yaxis()
AX.set_xticks(xticks)
AX.set_yticks(yticks)
AX.tick_params(axis='both', which='major', labelsize=lfs)
C += 1
if flag_ksp:
# print(f'KSP. no: {no}, R: {R}, C: {C}')
if single_plot: AX = ax
elif col_plot: AX = ax[R]
elif row_plot: AX = ax[C]
elif matrix_type_plot: AX = ax[R, C]
data = np.power(self.rkf['ksp'][no], power)
imh = AX.imshow(data, cmap=cmap, vmin=0, vmax=1)
AX.set_xlabel(xlabel, fontsize=lfs)
AX.set_ylabel(ylabel, fontsize=lfs)
ts = ['Kolmogorov-Smirnov P-value based\n sim.'
f' measure, O(n)={no}. Inequal bins']
AX.set_title(ts[0], fontsize=tfs)
AX.invert_yaxis()
AX.set_xticks(xticks)
AX.set_yticks(yticks)
AX.tick_params(axis='both', which='major', labelsize=lfs)
C += 1
if flag_ed:
# print(f'ED. no: {no}, R: {R}, C: {C}')
if single_plot: AX = ax
elif col_plot: AX = ax[R]
elif row_plot: AX = ax[C]
elif matrix_type_plot: AX = ax[R, C]
data = np.power(self.rkf['ed'][no], power)
imh = AX.imshow(data, cmap=cmap, vmin=0, vmax=1)
AX.set_xlabel(xlabel, fontsize=lfs)
AX.set_ylabel(ylabel, fontsize=lfs)
ts = f'Energy distance based\n sim. measure, O(n)={no}'
AX.set_title(ts, fontsize=tfs)
AX.invert_yaxis()
AX.set_xticks(xticks)
AX.set_yticks(yticks)
AX.tick_params(axis='both', which='major', labelsize=lfs)
C += 1
if flag_nlsd:
# print(f'NLSD. no: {no}, R: {R}, C: {C}')
if single_plot: AX = ax
elif col_plot: AX = ax[R]
elif row_plot: AX = ax[C]
elif matrix_type_plot: AX = ax[R, C]
data = np.power(self.rkf['nlsd'][no], power)
imh = AX.imshow(data, cmap=cmap, vmin=0, vmax=1)
AX.set_xlabel(xlabel, fontsize=lfs)
AX.set_ylabel(ylabel, fontsize=lfs)
ts = f'NetLSD sim. measure,\n O(n)={no}'
AX.set_title(ts, fontsize=tfs)
AX.invert_yaxis()
AX.set_xticks(xticks)
AX.set_yticks(yticks)
AX.tick_params(axis='both', which='major', labelsize=lfs)
C += 1
R += 1
if not single_plot:
AX = ax[:]
else:
AX = ax
cbar = plt.colorbar(imh, ax=AX,
fraction=cbfraction, pad=cbpad,
orientation=cborientation,
aspect=cbaspect,
shrink=shrink,
ticks=cbarticks)
cbar.set_label(cbtitle+f'. Power: {power}', fontsize=cbfs)
cbar.ax.tick_params(labelsize=cbfs)