pyrolite 0.0.14__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__init__.py +10 -0
  2. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__pycache__/__init__.cpython-36.pyc +0 -0
  3. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__pycache__/_version.cpython-36.pyc +0 -0
  4. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__pycache__/alteration.cpython-36.pyc +0 -0
  5. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__pycache__/classification.cpython-36.pyc +0 -0
  6. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__pycache__/compositions.cpython-36.pyc +0 -0
  7. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__pycache__/geochem.cpython-36.pyc +0 -0
  8. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__pycache__/melts.cpython-36.pyc +0 -0
  9. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__pycache__/norm.cpython-36.pyc +0 -0
  10. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__pycache__/normalisation.cpython-36.pyc +0 -0
  11. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/__pycache__/plot.cpython-36.pyc +0 -0
  12. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/_version.py +21 -0
  13. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/alteration.py +66 -0
  14. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/classification.py +222 -0
  15. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/comp/__init__.py +9 -0
  16. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/comp/__pycache__/__init__.cpython-36.pyc +0 -0
  17. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/comp/__pycache__/aggregate.cpython-36.pyc +0 -0
  18. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/comp/__pycache__/codata.cpython-36.pyc +0 -0
  19. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/comp/__pycache__/impute.cpython-36.pyc +0 -0
  20. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/comp/__pycache__/renorm.cpython-36.pyc +0 -0
  21. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/comp/aggregate.py +391 -0
  22. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/comp/codata.py +266 -0
  23. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/comp/impute.py +82 -0
  24. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/comp/renorm.py +40 -0
  25. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/compositions.py +524 -0
  26. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/georoc/GEOROC_CFB_Dataset_List.csv +42 -0
  27. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/georoc/GEOROC_Convergent_Dataset_List.csv +42 -0
  28. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/georoc/GEOROC_OBFB_Dataset_List.csv +5 -0
  29. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/georoc/GEOROC_OIB_Dataset_List.csv +49 -0
  30. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/georoc/GEOROC_OceanicPlateau_Dataset_List.csv +18 -0
  31. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/georoc/contents.json +1 -0
  32. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/melts/__pycache__/env.cpython-35.pyc +0 -0
  33. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/melts/__pycache__/env.cpython-36.pyc +0 -0
  34. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/melts/env.py +1063 -0
  35. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.Ba.modelfield +0 -0
  36. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.Bs.modelfield +0 -0
  37. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.F.modelfield +0 -0
  38. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.O1.modelfield +0 -0
  39. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.O2.modelfield +0 -0
  40. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.O3.modelfield +0 -0
  41. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.Pc.modelfield +0 -0
  42. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.Ph.modelfield +0 -0
  43. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.R.modelfield +0 -0
  44. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.S1.modelfield +0 -0
  45. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.S2.modelfield +0 -0
  46. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.S3.modelfield +0 -0
  47. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.T1.modelfield +0 -0
  48. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.T2.modelfield +0 -0
  49. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.U1.modelfield +0 -0
  50. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.U2.modelfield +0 -0
  51. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.U3.modelfield +0 -0
  52. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.modelfields +0 -0
  53. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.nan.modelfield +0 -0
  54. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS/TAS.none.modelfield +0 -0
  55. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/TAS.clsf.gz +0 -0
  56. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/aphanitic.clsf.gz +0 -0
  57. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/gabbroic.clsf.gz +0 -0
  58. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/peralkalinity.clsf.gz +0 -0
  59. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/phaneritic.clsf.gz +0 -0
  60. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/models/ultramafic.clsf.gz +0 -0
  61. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/refcomp/CH_PalmeONeill2014.csv +95 -0
  62. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/refcomp/DDMM_WorkmanHart2005.csv +105 -0
  63. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/refcomp/DMM_WorkmanHart2005.csv +105 -0
  64. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/refcomp/DM_SaltersStrake2004.csv +95 -0
  65. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/refcomp/EDMM_WorkmanHart2005.csv +105 -0
  66. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/refcomp/PM_PalmeONeill2014.csv +95 -0
  67. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/data/timescale/geotimescale_spans.csv +180 -0
  68. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/geochem.py +821 -0
  69. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/melts.py +92 -0
  70. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/mineral/__init__.py +10 -0
  71. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/mineral/__pycache__/__init__.cpython-36.pyc +0 -0
  72. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/mineral/__pycache__/db.cpython-36.pyc +0 -0
  73. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/mineral/__pycache__/ions.cpython-36.pyc +0 -0
  74. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/mineral/__pycache__/mineral.cpython-36.pyc +0 -0
  75. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/mineral/__pycache__/sites.cpython-36.pyc +0 -0
  76. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/mineral/db.py +88 -0
  77. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/mineral/ions.py +78 -0
  78. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/mineral/mineral.py +587 -0
  79. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/mineral/sites.py +134 -0
  80. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/norm.py +224 -0
  81. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/normalisation.py +204 -0
  82. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/plot.py +514 -0
  83. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__init__.py +13 -0
  84. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/__init__.cpython-36.pyc +0 -0
  85. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/database.cpython-36.pyc +0 -0
  86. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/env.cpython-36.pyc +0 -0
  87. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/general.cpython-36.pyc +0 -0
  88. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/georoc.cpython-36.pyc +0 -0
  89. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/math.cpython-36.pyc +0 -0
  90. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/melts.cpython-36.pyc +0 -0
  91. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/multip.cpython-36.pyc +0 -0
  92. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/multiprocessing.cpython-36.pyc +0 -0
  93. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/pd.cpython-36.pyc +0 -0
  94. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/plot.cpython-36.pyc +0 -0
  95. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/skl.cpython-36.pyc +0 -0
  96. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/spatial.cpython-36.pyc +0 -0
  97. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/text.cpython-36.pyc +0 -0
  98. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/time.cpython-36.pyc +0 -0
  99. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/__pycache__/wfs.cpython-36.pyc +0 -0
  100. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/database.py +88 -0
  101. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/env.py +81 -0
  102. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/general.py +266 -0
  103. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/georoc.py +444 -0
  104. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/math.py +371 -0
  105. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/melts.py +397 -0
  106. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/multip.py +29 -0
  107. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/multiprocessing.py +29 -0
  108. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/pd.py +214 -0
  109. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/plot.py +345 -0
  110. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/skl.py +847 -0
  111. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/spatial.py +91 -0
  112. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/text.py +207 -0
  113. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/time.py +224 -0
  114. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite/util/wfs.py +10 -0
  115. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite-0.0.14-py3.6.egg-info/PKG-INFO +61 -0
  116. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite-0.0.14-py3.6.egg-info/SOURCES.txt +83 -0
  117. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite-0.0.14-py3.6.egg-info/dependency_links.txt +1 -0
  118. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite-0.0.14-py3.6.egg-info/requires.txt +47 -0
  119. ProgramData/Anaconda3_64/Lib/site-packages/pyrolite-0.0.14-py3.6.egg-info/top_level.txt +1 -0
@@ -0,0 +1,847 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ from functools import partial
4
+ import itertools
5
+ from ..geochem import *
6
+ from ..geochem import __common_elements__, __common_oxides__
7
+ from ..comp.codata import *
8
+ from ..comp.aggregate import *
9
+ from .plot import *
10
+
11
+ import matplotlib.colors as mplc
12
+
13
+ import logging
14
+
15
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
16
+ logger = logging.getLogger(__name__)
17
+
18
+ try:
19
+ from sklearn.base import TransformerMixin, BaseEstimator
20
+ from sklearn.metrics import confusion_matrix
21
+ except ImportError:
22
+ msg = "scikit-learn not installed"
23
+ logger.warning(msg)
24
+
25
+ try:
26
+ from fancyimpute import IterativeImputer, SoftImpute
27
+ except ImportError:
28
+ msg = "fancyimpute not installed"
29
+ logger.warning(msg)
30
+
31
+ try:
32
+ from imblearn.over_sampling import RandomOverSampler
33
+ except ImportError:
34
+ msg = "imbalanced-learn not installed"
35
+ logger.warning(msg)
36
+
37
+
38
+ def get_confusion_matrix(clf, test_X, test_y):
39
+ y_true = test_y
40
+ y_pred = clf.predict(test_X)
41
+ return confusion_matrix(y_true, y_pred)
42
+
43
+
44
+ def plot_confusion_matrix(
45
+ *args,
46
+ classes=[],
47
+ normalize=False,
48
+ title="Confusion Matrix",
49
+ cmap=plt.cm.Blues,
50
+ norm=mplc.Normalize(vmin=0, vmax=1.0),
51
+ ax=None
52
+ ):
53
+ """
54
+ This function prints and plots the confusion matrix.
55
+ Normalization can be applied by setting `normalize=True`.
56
+ """
57
+ if len(args) == 1:
58
+ cm = args[0]
59
+ else:
60
+ cm = get_confusion_matrix(*args)
61
+ if not classes:
62
+ if hasattr(args[0], "classes_"):
63
+ classes = list(args[0].classes_)
64
+
65
+ if not classes:
66
+ classes = np.arange(cm.shape[0])
67
+
68
+ if normalize:
69
+ cm = cm.astype("float") / cm.sum(axis=1)[:, np.newaxis]
70
+
71
+ if ax is None:
72
+ fig, ax = plt.subplots(1)
73
+
74
+ im = ax.imshow(cm, interpolation="nearest", cmap=cmap, norm=norm)
75
+ ax.set_title(title)
76
+ plt.colorbar(im, ax=ax)
77
+ tick_marks = np.arange(len(classes))
78
+
79
+ fmt = ".2f" if normalize else "d"
80
+ thresh = cm.max() / 2.0
81
+ for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
82
+ ax.text(
83
+ j,
84
+ i,
85
+ format(cm[i, j], fmt),
86
+ horizontalalignment="center",
87
+ color="white" if cm[i, j] > thresh else "black",
88
+ )
89
+
90
+ ax.set(
91
+ ylabel="True",
92
+ xlabel="Predicted",
93
+ xticks=tick_marks,
94
+ yticks=tick_marks,
95
+ xticklabels=classes,
96
+ yticklabels=classes,
97
+ )
98
+ plt.tight_layout()
99
+ return ax
100
+
101
+
102
+ def plot_gs_results(gs, xvar=None, yvar=None):
103
+ """Plots the results from a GridSearch showing location of optimum in 2D."""
104
+ labels = gs.param_grid.keys()
105
+ grid_items = list(gs.param_grid.items())
106
+ no_items = len(grid_items)
107
+ if (
108
+ len(grid_items) == 1
109
+ ): # if there's only one item, there's only one way to plot it.
110
+ (xvar, xx) = grid_items[0]
111
+ (yvar, yy) = "", np.array([0])
112
+ else:
113
+ if xvar is None and yvar is None:
114
+ (yvar, yy), (xvar, xx) = [(k, v) for (k, v) in grid_items][:3]
115
+ elif xvar is not None and yvar is not None:
116
+ yy, xx = gs.param_grid[yvar], gs.param_grid[xvar]
117
+ else:
118
+ if xvar is not None:
119
+ xx = gs.param_grid[xvar]
120
+ (yvar, yy) = [(k, v) for (k, v) in grid_items if not k == xvar][0]
121
+ else:
122
+ yy = gs.param_grid[yvar]
123
+ (xvar, xx) = [(k, v) for (k, v) in grid_items if not k == yvar][0]
124
+ xx, yy = np.array(xx), np.array(yy)
125
+ other_keys = [i for i in gs.param_grid.keys() if i not in [xvar, yvar]]
126
+ if other_keys:
127
+ pass
128
+ else:
129
+ results = np.array(gs.cv_results_["mean_test_score"]).reshape(xx.size, yy.size)
130
+ fig, ax = plt.subplots(1)
131
+ ax.imshow(results.T, cmap=plt.cm.Blues)
132
+
133
+ ax.set(
134
+ xlabel=xvar,
135
+ ylabel=yvar,
136
+ xticks=np.arange(len(xx)),
137
+ yticks=np.arange(len(yy)),
138
+ xticklabels=["{:01.2g}".format(i) for i in xx],
139
+ yticklabels=["{:01.2g}".format(i) for i in yy],
140
+ )
141
+ ax.set_xticklabels(ax.get_xticklabels(), rotation=45)
142
+ ax.invert_yaxis()
143
+
144
+ max = np.nanmax(results)
145
+ locmax = np.where(results == max)
146
+ x, y = locmax
147
+ ax.scatter(x, y, marker="D", s=100, c="k")
148
+ return ax
149
+
150
+
151
+ def plot_cooccurence(
152
+ df,
153
+ ax=None,
154
+ normalize=True,
155
+ log=False,
156
+ norm=mplc.Normalize(vmin=0, vmax=1.0),
157
+ **kwargs
158
+ ):
159
+ if ax is None:
160
+ fig, ax = plt.subplots(1, figsize=(4.2, 4))
161
+ co_occur = df.fillna(0)
162
+ co_occur[co_occur > 0] = 1
163
+ co_occur = co_occur.T.dot(co_occur).astype(int)
164
+ if normalize:
165
+ diags = np.diagonal(co_occur)
166
+ for i in range(diags.shape[0]):
167
+ for j in range(diags.shape[0]):
168
+ co_occur.iloc[i, j] = co_occur.iloc[i, j] / np.max([diags[i], diags[j]])
169
+ if log:
170
+ co_occur = co_occur.applymap(np.log)
171
+ heatmap = ax.pcolor(co_occur, norm=norm, **kwargs)
172
+ ax.set_yticks(np.arange(co_occur.shape[0]) + 0.5, minor=False)
173
+ ax.set_xticks(np.arange(co_occur.shape[1]) + 0.5, minor=False)
174
+ ax.invert_yaxis()
175
+ ax.xaxis.tick_top()
176
+ ax.set_xticklabels(df.columns, minor=False, rotation=90)
177
+ ax.set_yticklabels(df.columns, minor=False)
178
+ add_colorbar(heatmap)
179
+ return ax
180
+
181
+
182
+ class DropBelowZero(BaseEstimator, TransformerMixin):
183
+ """
184
+ Transformer for scikit-learn like use.
185
+ """
186
+
187
+ def __init__(self, **kwargs):
188
+ self.kpairs = kwargs
189
+ self.label = "Feedthrough"
190
+
191
+ def transform(self, X, *args, **kwargs):
192
+ if isinstance(X, pd.DataFrame) or isinstance(X, pd.Series):
193
+ out = X.where(X > 0, np.nan)
194
+ else:
195
+ out = np.where(X > 0, X, np.nan)
196
+ return out
197
+
198
+ def fit(self, X, *args):
199
+ return self
200
+
201
+
202
+ class LinearTransform(BaseEstimator, TransformerMixin):
203
+ """
204
+ Linear Transformer for scikit-learn like use.
205
+ """
206
+
207
+ def __init__(self, **kwargs):
208
+ self.kpairs = kwargs
209
+ self.label = "Feedthrough"
210
+ self.forward = lambda x: x
211
+ self.inverse = lambda x: x
212
+
213
+ def transform(self, X, *args, **kwargs):
214
+ if isinstance(X, pd.DataFrame):
215
+ out = X.copy(deep=True)
216
+ out.loc[:, :] = self.forward(X.values, *args, **kwargs)
217
+ elif isinstance(X, pd.Series):
218
+ out = X.copy(deep=True)
219
+ out.loc[:] = self.forward(X.values, *args, **kwargs)
220
+ else:
221
+ out = self.forward(np.array(X), *args, **kwargs)
222
+ return out
223
+
224
+ def inverse_transform(self, Y, *args, **kwargs):
225
+ if isinstance(Y, pd.DataFrame):
226
+ out = Y.copy(deep=True)
227
+ out.loc[:, :] = self.inverse(Y.values, *args, **kwargs)
228
+ elif isinstance(Y, pd.Series):
229
+ out = Y.copy(deep=True)
230
+ out.loc[:] = self.inverse(Y.values, *args, **kwargs)
231
+ else:
232
+ out = self.inverse(np.array(Y), *args, **kwargs)
233
+ return out
234
+
235
+ def fit(self, X, *args):
236
+ return self
237
+
238
+
239
+ class ExpTransform(BaseEstimator, TransformerMixin):
240
+ """
241
+ Exponential Transformer for scikit-learn like use.
242
+ """
243
+
244
+ def __init__(self, **kwargs):
245
+ self.kpairs = kwargs
246
+ self.label = "Feedthrough"
247
+ self.forward = np.exp
248
+ self.inverse = np.log
249
+
250
+ def transform(self, X, *args, **kwargs):
251
+ if isinstance(X, pd.DataFrame):
252
+ out = X.applymap(self.forward)
253
+ elif isinstance(X, pd.Series):
254
+ out = X.apply(self.forward)
255
+ else:
256
+ out = self.forward(np.array(X), *args, **kwargs)
257
+ return out
258
+
259
+ def inverse_transform(self, Y, *args, **kwargs):
260
+ if isinstance(Y, pd.DataFrame):
261
+ out = Y.applymap(self.inverse)
262
+ elif isinstance(Y, pd.Series):
263
+ out = Y.apply(self.inverse)
264
+ else:
265
+ out = self.inverse(np.array(Y), *args, **kwargs)
266
+ return out
267
+
268
+ def fit(self, X, *args):
269
+ return self
270
+
271
+
272
+ class LogTransform(BaseEstimator, TransformerMixin):
273
+ """
274
+ Log Transformer for scikit-learn like use.
275
+ """
276
+
277
+ def __init__(self, **kwargs):
278
+ self.kpairs = kwargs
279
+ self.label = "Feedthrough"
280
+ self.forward = np.log
281
+ self.inverse = np.exp
282
+
283
+ def transform(self, X, *args, **kwargs):
284
+ if isinstance(X, pd.DataFrame):
285
+ out = X.copy(deep=True)
286
+ out.loc[:, :] = self.forward(X.values, *args, **kwargs)
287
+ elif isinstance(X, pd.Series):
288
+ out = X.copy(deep=True)
289
+ out.loc[:] = self.forward(X.values, *args, **kwargs)
290
+ else:
291
+ out = self.forward(np.array(X), *args, **kwargs)
292
+ return out
293
+
294
+ def inverse_transform(self, Y, *args, **kwargs):
295
+ if isinstance(Y, pd.DataFrame):
296
+ out = Y.copy(deep=True)
297
+ out.loc[:, :] = self.inverse(Y.values, *args, **kwargs)
298
+ elif isinstance(Y, pd.Series):
299
+ out = Y.copy(deep=True)
300
+ out.loc[:] = self.inverse(Y.values, *args, **kwargs)
301
+ else:
302
+ out = self.inverse(np.array(Y), *args, **kwargs)
303
+ return out
304
+
305
+ def fit(self, X, *args):
306
+ return self
307
+
308
+
309
+ class ALRTransform(BaseEstimator, TransformerMixin):
310
+ """
311
+ Additive Log Ratio Transformer for scikit-learn like use.
312
+ """
313
+
314
+ def __init__(self, **kwargs):
315
+ self.kpairs = kwargs
316
+ self.label = "ALR"
317
+ self.forward = alr
318
+ self.inverse = inv_alr
319
+
320
+ def transform(self, X, *args, **kwargs):
321
+ if isinstance(X, pd.DataFrame):
322
+ out = pd.DataFrame(
323
+ index=X.index, data=self.forward(X.values, *args, **kwargs)
324
+ )
325
+ elif isinstance(X, pd.Series):
326
+ out = pd.Series(index=X.index, data=self.forward(X.values, *args, **kwargs))
327
+ else:
328
+ out = self.forward(np.array(X), *args, **kwargs)
329
+ return out
330
+
331
+ def inverse_transform(self, Y, *args, **kwargs):
332
+ if isinstance(Y, pd.DataFrame):
333
+ out = pd.DataFrame(
334
+ index=Y.index, data=self.inverse(Y.values, *args, **kwargs)
335
+ )
336
+ elif isinstance(Y, pd.Series):
337
+ out = pd.Series(index=Y.index, data=self.inverse(Y.values, *args, **kwargs))
338
+ else:
339
+ out = self.inverse(np.array(Y), *args, **kwargs)
340
+ return out
341
+
342
+ def fit(self, X, *args, **kwargs):
343
+ return self
344
+
345
+
346
+ class CLRTransform(BaseEstimator, TransformerMixin):
347
+ """
348
+ Centred Log Ratio Transformer for scikit-learn like use.
349
+ """
350
+
351
+ def __init__(self, **kwargs):
352
+ self.kpairs = kwargs
353
+ self.label = "CLR"
354
+ self.forward = clr
355
+ self.inverse = inv_clr
356
+
357
+ def transform(self, X, *args, **kwargs):
358
+ if isinstance(X, pd.DataFrame):
359
+ out = X.copy(deep=True)
360
+ out.loc[:, :] = self.forward(X.values, *args, **kwargs)
361
+ elif isinstance(X, pd.Series):
362
+ out = X.copy(deep=True)
363
+ out.loc[:] = self.forward(X.values, *args, **kwargs)
364
+ else:
365
+ out = self.forward(np.array(X), *args, **kwargs)
366
+ return out
367
+
368
+ def inverse_transform(self, Y, *args, **kwargs):
369
+ if isinstance(Y, pd.DataFrame):
370
+ out = Y.copy(deep=True)
371
+ out.loc[:, :] = self.inverse(Y.values, *args, **kwargs)
372
+ elif isinstance(Y, pd.Series):
373
+ out = Y.copy(deep=True)
374
+ out.loc[:] = self.inverse(Y.values, *args, **kwargs)
375
+ else:
376
+ out = self.inverse(np.array(Y), *args, **kwargs)
377
+ return out
378
+
379
+ def fit(self, X, *args, **kwargs):
380
+ return self
381
+
382
+
383
+ class ILRTransform(BaseEstimator, TransformerMixin):
384
+ """
385
+ Isometric Log Ratio Transformer for scikit-learn like use.
386
+ """
387
+
388
+ def __init__(self, **kwargs):
389
+ self.kpairs = kwargs
390
+ self.label = "ILR"
391
+ self.forward = ilr
392
+ self.inverse = inv_ilr
393
+
394
+ def transform(self, X, *args, **kwargs):
395
+ self.X = np.array(X)
396
+ if isinstance(X, pd.DataFrame):
397
+ out = pd.DataFrame(
398
+ index=X.index, data=self.forward(X.values, *args, **kwargs)
399
+ )
400
+ elif isinstance(X, pd.Series):
401
+ out = X.copy(deep=True)
402
+ out.loc[:] = self.forward(X.values, *args, **kwargs)
403
+ else:
404
+ out = self.forward(np.array(X), *args, **kwargs)
405
+ return out
406
+
407
+ def inverse_transform(self, Y, *args, **kwargs):
408
+ if "X" not in kwargs:
409
+ kwargs.update(dict(X=self.X))
410
+ if isinstance(Y, pd.DataFrame):
411
+ out = pd.DataFrame(
412
+ index=Y.index, data=self.inverse(Y.values, *args, **kwargs)
413
+ )
414
+ elif isinstance(Y, pd.Series):
415
+ out = pd.Series(index=Y.index, data=self.inverse(Y.values, *args, **kwargs))
416
+ else:
417
+ out = self.inverse(np.array(Y), *args, **kwargs)
418
+ return out
419
+
420
+ def fit(self, X, *args, **kwargs):
421
+ return self
422
+
423
+
424
+ class BoxCoxTransform(BaseEstimator, TransformerMixin):
425
+ """
426
+ BoxCox Transformer for scikit-learn like use.
427
+ """
428
+
429
+ def __init__(self, **kwargs):
430
+ self.kpairs = kwargs
431
+ self.label = "BoxCox"
432
+ self.forward = boxcox
433
+ self.inverse = inv_boxcox
434
+ self.lmbda = None
435
+
436
+ def transform(self, X, *args, **kwargs):
437
+ self.X = np.array(X)
438
+ if "lmbda" not in kwargs:
439
+ if not (self.lmbda is None):
440
+ kwargs.update(dict(lmbda=self.lmbda))
441
+ data = self.forward(X, *args, **kwargs)
442
+ else:
443
+ kwargs.update(dict(return_lmbda=True))
444
+ data, lmbda = self.forward(X, *args, **kwargs)
445
+ self.lmbda = lmbda
446
+ return data
447
+
448
+ def inverse_transform(self, Y, *args, **kwargs):
449
+ if "lmbda" not in kwargs:
450
+ kwargs.update(dict(lmbda=self.lmbda))
451
+ return self.inverse(Y, *args, **kwargs)
452
+
453
+ def fit(self, X, *args, **kwargs):
454
+ bc_data, lmbda = boxcox(X, *args, **kwargs)
455
+ self.lmbda = lmbda
456
+
457
+
458
+ class TypeSelector(BaseEstimator, TransformerMixin):
459
+ def __init__(self, dtype):
460
+ self.dtype = dtype
461
+
462
+ def fit(self, X, y=None):
463
+ return self
464
+
465
+ def transform(self, X):
466
+ assert isinstance(X, pd.DataFrame)
467
+ return X.select_dtypes(include=[self.dtype])
468
+
469
+
470
+ class ColumnSelector(BaseEstimator, TransformerMixin):
471
+ def __init__(self, columns):
472
+ self.columns = columns
473
+
474
+ def fit(self, X, y=None):
475
+ return self
476
+
477
+ def transform(self, X):
478
+ assert isinstance(X, pd.DataFrame)
479
+
480
+ try:
481
+ return X.loc[:, self.columns]
482
+ except KeyError:
483
+ cols_error = list(set(self.columns) - set(X.columns))
484
+ raise KeyError(
485
+ "The DataFrame does not include the columns: %s" % cols_error
486
+ )
487
+
488
+
489
+ class CompositionalSelector(BaseEstimator, TransformerMixin):
490
+ def __init__(
491
+ self, components=__common_elements__ | __common_oxides__, inverse=False
492
+ ):
493
+ self.columns = components
494
+ self.inverse = inverse
495
+
496
+ def fit(self, X, y=None):
497
+ return self
498
+
499
+ def transform(self, X):
500
+ assert isinstance(X, pd.DataFrame)
501
+ if self.inverse:
502
+ out_cols = [i for i in X.columns if i not in self.columns]
503
+ else:
504
+ out_cols = [i for i in X.columns if i in self.columns]
505
+ out = X.loc[:, out_cols]
506
+ return out
507
+
508
+
509
+ class MajorsSelector(BaseEstimator, TransformerMixin):
510
+ def __init__(self, components=common_oxides()):
511
+ self.columns = components
512
+
513
+ def fit(self, X, y=None):
514
+ return self
515
+
516
+ def transform(self, X):
517
+ assert isinstance(X, pd.DataFrame)
518
+ out_cols = [i for i in X.columns if i in self.columns]
519
+ out = X.loc[:, out_cols]
520
+ return out
521
+
522
+
523
+ class ElementSelector(BaseEstimator, TransformerMixin):
524
+ def __init__(self, components=common_elements()):
525
+ self.columns = components
526
+
527
+ def fit(self, X, y=None):
528
+ return self
529
+
530
+ def transform(self, X):
531
+ assert isinstance(X, pd.DataFrame)
532
+ out_cols = [i for i in X.columns if i in self.columns]
533
+ out = X.loc[:, out_cols]
534
+ return out
535
+
536
+
537
+ class REESelector(BaseEstimator, TransformerMixin):
538
+ def __init__(self, components=REE()):
539
+ components = [i for i in components if not i == "Pm"]
540
+ self.columns = components
541
+
542
+ def fit(self, X, y=None):
543
+ return self
544
+
545
+ def transform(self, X):
546
+ assert isinstance(X, pd.DataFrame)
547
+ out_cols = [i for i in self.columns if i in X.columns]
548
+ out = X.loc[:, out_cols]
549
+ return out
550
+
551
+
552
+ class Devolatilizer(BaseEstimator, TransformerMixin):
553
+ def __init__(
554
+ self, exclude=["H2O", "H2O_PLUS", "H2O_MINUS", "CO2", "LOI"], renorm=True
555
+ ):
556
+ self.exclude = [i.upper() for i in exclude]
557
+ self.renorm = renorm
558
+
559
+ def fit(self, X, y=None):
560
+ return self
561
+
562
+ def transform(self, X):
563
+ assert isinstance(X, pd.DataFrame)
564
+ exclude = [i for i in X.columns if i.upper() in self.exclude]
565
+ return devolatilise(X, exclude=exclude, renorm=self.renorm)
566
+
567
+
568
+ class RedoxAggregator(BaseEstimator, TransformerMixin):
569
+ def __init__(self, to_oxidised=False, renorm=True, total_suffix="T"):
570
+ self.to_oxidised = to_oxidised
571
+ self.renorm = renorm
572
+ self.total_suffix = total_suffix
573
+
574
+ def fit(self, X, y=None):
575
+ return self
576
+
577
+ def transform(self, X):
578
+ assert isinstance(X, pd.DataFrame)
579
+ return recalculate_redox(
580
+ X,
581
+ to_oxidised=self.to_oxidised,
582
+ renorm=self.renorm,
583
+ total_suffix=self.total_suffix,
584
+ )
585
+
586
+
587
+ class ElementAggregator(BaseEstimator, TransformerMixin):
588
+ def __init__(self, renorm=True, form="oxide"):
589
+ self.renorm = renorm
590
+ self.form = form
591
+
592
+ def fit(self, X, y=None):
593
+ return self
594
+
595
+ def transform(self, X):
596
+ assert isinstance(X, pd.DataFrame)
597
+ multiple_entries = check_multiple_cation_inclusion(X)
598
+
599
+ for el in multiple_entries:
600
+ X = aggregate_cation(X, el, form=self.form)
601
+ return X
602
+
603
+
604
+ class PdUnion(BaseEstimator, TransformerMixin):
605
+ def __init__(self, estimators: list = []):
606
+ self.estimators = estimators
607
+
608
+ def fit(self, X, y=None):
609
+ return self
610
+
611
+ def transform(self, X):
612
+ assert isinstance(X, pd.DataFrame)
613
+ parts = []
614
+ for est in self.estimators:
615
+ if isinstance(est, pd.DataFrame):
616
+ parts.append(est)
617
+ elif isinstance(est, TransformerMixin) or isinstance(est, BaseEstimator):
618
+ if hasattr(est, "fit"):
619
+ parts.append(est.fit_transform(X))
620
+ else:
621
+ parts.append(est.transform(X))
622
+ else: # e.g. Numpy array, try to convert to dataframe
623
+ parts.append(pd.DataFrame(est))
624
+
625
+ columns = []
626
+ idxs = []
627
+ for p in parts:
628
+ columns += [i for i in p.columns if not i in columns]
629
+ idxs.append(p.index.size)
630
+
631
+ # check the indexes are all the same length
632
+ assert all([idx == idxs[0] for idx in idxs])
633
+
634
+ out = pd.DataFrame(columns=columns)
635
+ for p in parts:
636
+ out[p.columns] = p
637
+
638
+ return out
639
+
640
+
641
+ class LambdaTransformer(BaseEstimator, TransformerMixin):
642
+ def __init__(
643
+ self, norm_to="Chondrite_PON", exclude=["Pm", "Eu", "Ce"], params=None, degree=5
644
+ ):
645
+ self.norm_to = norm_to
646
+ self.ree = [i for i in REE() if not i in exclude]
647
+ self.radii = np.array(get_radii(self.ree))
648
+ self.exclude = exclude
649
+ if params is None:
650
+ self.degree = degree
651
+ self.params = OP_constants(self.radii, degree=self.degree)
652
+ else:
653
+ self.params = params
654
+ self.degree = len(params)
655
+
656
+ def fit(self, X, y=None):
657
+ return self
658
+
659
+ def transform(self, X):
660
+ assert isinstance(X, pd.DataFrame)
661
+ ree_present = [i in X.columns for i in self.ree]
662
+ if not all(ree_present):
663
+ self.ree = [i for i in self.ree if i in X.columns]
664
+ self.radii = self.radii[ree_present]
665
+ self.params = OP_constants(self.radii, degree=self.degree)
666
+
667
+ return lambda_lnREE(
668
+ X,
669
+ norm_to=self.norm_to,
670
+ params=self.params,
671
+ degree=self.degree,
672
+ exclude=self.exclude,
673
+ )
674
+
675
+
676
+ class MultipleImputer(BaseEstimator, TransformerMixin):
677
+ """
678
+ Multiple Imputation via fancyimpute.IterativeImputer.
679
+ """
680
+
681
+ def __init__(self, multiple=5, n_iter=10, groupby=None, *args, **kwargs):
682
+ self.multiple = multiple
683
+ self.n_iter = n_iter
684
+ self.args = args
685
+ self.kwargs = kwargs
686
+ self.groupby = groupby
687
+
688
+ def transform(self, X, *args, **kwargs):
689
+ assert isinstance(X, pd.DataFrame)
690
+ df = pd.DataFrame(columns=X.columns, index=X.index)
691
+ if isinstance(self.imputers, dict):
692
+ for c, d in self.imputers.items():
693
+ mask = d["mask"]
694
+ imputers = d["impute"]
695
+ imputed_data = np.array([imp.transform(X[mask, :]) for imp in imputers])
696
+ mean = np.mean(imputed_data, axis=0)
697
+ df.loc[mask, ~pd.isnull(X[mask, :]).all(axis=0)] = mean
698
+ return df
699
+ else:
700
+ imputed_data = np.array([imp.transform(X) for imp in self.imputers])
701
+ mean = np.mean(imputed_data, axis=0)
702
+ df.loc[:, ~pd.isnull(X).all(axis=0)] = mean
703
+ return df
704
+
705
+ """
706
+ def inverse_transform(self, Y, *args, **kwargs):
707
+ # For non-compositional data, take the mask and reverting to nan
708
+ # for compositional data, renormalisation would be needed
709
+ pass
710
+ """
711
+
712
+ def fit(self, X, y=None):
713
+ assert isinstance(X, pd.DataFrame)
714
+ start = X
715
+ y_present = y is not None
716
+ groupby_present = self.groupby is not None
717
+ self.imputers = []
718
+ if y_present or groupby_present:
719
+ assert not (groupby_present and y_present)
720
+ if y_present:
721
+ classes = np.unique(y)
722
+ gen_mask = lambda c: y == c
723
+ if groupby_present:
724
+ classes = X[self.groupby].unique()
725
+ gen_mask = lambda c: X[self.groupby] == c
726
+ self.imputers = {
727
+ c: {
728
+ "impute": [
729
+ IterativeImputer(
730
+ n_iter=self.n_iter,
731
+ sample_posterior=True,
732
+ random_state=ix,
733
+ **self.kwargs
734
+ )
735
+ for ix in range(self.multiple)
736
+ ],
737
+ "mask": gen_mask(c),
738
+ }
739
+ for c in classes
740
+ }
741
+
742
+ msg = """Imputation transformer: {} imputers x {} classes""".format(
743
+ self.multiple, len(classes)
744
+ )
745
+ logger.info(msg)
746
+
747
+ for c, d in self.imputers.items():
748
+ for imp in d["impute"]:
749
+ imp.fit(X[d["mask"], :])
750
+
751
+ else:
752
+ for ix in range(self.multiple):
753
+ self.imputers.append(
754
+ IterativeImputer(
755
+ n_iter=self.n_iter,
756
+ sample_posterior=True,
757
+ random_state=ix,
758
+ **self.kwargs
759
+ )
760
+ )
761
+ msg = """Imputation transformer: {} imputers""".format(self.multiple)
762
+ logger.info(msg)
763
+ for ix in range(self.multiple):
764
+ self.imputers[ix].fit(X)
765
+
766
+ return self
767
+
768
+
769
+ class PdSoftImputer(BaseEstimator, TransformerMixin):
770
+ """
771
+ Multiple Imputation via fancyimpute.SoftImpute.
772
+ """
773
+
774
+ def __init__(self, max_iters=100, groupby=None, donotimpute=[], *args, **kwargs):
775
+ self.args = args
776
+ self.kwargs = kwargs
777
+ self.max_iters = max_iters
778
+ self.groupby = groupby
779
+ self.donotimpute = donotimpute
780
+
781
+ def transform(self, X, *args, **kwargs):
782
+ """
783
+ Impute Missing Values
784
+
785
+ Need to use masks to avoid SoftImpute returning 0. where it cannot impute.
786
+ """
787
+ assert isinstance(X, pd.DataFrame)
788
+ df = pd.DataFrame(columns=X.columns, index=X.index) # df of nans
789
+ df.loc[:, self.donotimpute] = X.loc[:, self.donotimpute]
790
+ to_impute = [i for i in X.columns if not i in self.donotimpute]
791
+ imputable = ~pd.isnull(X.loc[:, to_impute]).all(axis=1)
792
+ if isinstance(self.imputer, dict):
793
+ for c, d in self.imputer.items():
794
+ mask = d["mask"]
795
+ mask = mask & imputable
796
+ imputer = d["impute"]
797
+ imputed_data = imputer.fit_transform(X.loc[mask, to_impute])
798
+ assert imputed_data.shape[0] == X.loc[mask, :].index.size
799
+ df.loc[mask, to_impute] = imputed_data
800
+
801
+ return df
802
+ else:
803
+ imputed_data = self.imputer.fit_transform(X.loc[imputable, to_impute])
804
+ assert imputed_data.shape[0] == X.loc[imputable, :].index.size
805
+ df.loc[imputable, to_impute] = imputed_data
806
+ return df
807
+
808
+ """
809
+ def inverse_transform(self, Y, *args, **kwargs):
810
+ # For non-compositional data, take the mask and reverting to nan
811
+ # for compositional data, renormalisation would be needed
812
+ pass
813
+ """
814
+
815
+ def fit(self, X, y=None):
816
+ assert isinstance(X, pd.DataFrame)
817
+ start = X
818
+ y_present = y is not None
819
+ groupby_present = self.groupby is not None
820
+ self.imputer = []
821
+ if y_present or groupby_present:
822
+ assert not (groupby_present and y_present)
823
+ if y_present:
824
+ classes = np.unique(y)
825
+ gen_mask = lambda c: y == c
826
+ if groupby_present:
827
+ classes = X[self.groupby].unique()
828
+ gen_mask = lambda c: X[self.groupby] == c
829
+ self.imputer = {
830
+ c: {
831
+ "impute": SoftImpute(max_iters=self.max_iters, **self.kwargs),
832
+ "mask": gen_mask(c),
833
+ }
834
+ for c in classes
835
+ }
836
+
837
+ msg = """Building Soft Imputation Transformers for {} classes""".format(
838
+ len(classes)
839
+ )
840
+ logger.info(msg)
841
+
842
+ else:
843
+ self.imputer = SoftImpute(max_iters=self.max_iters, **self.kwargs)
844
+ msg = """Building Soft Imputation Transformer"""
845
+ logger.info(msg)
846
+
847
+ return self