rulelearn-ripper-interactive 0.2.3__py3-none-any.whl

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 (73) hide show
  1. rulelearn_ri/__init__.py +6 -0
  2. rulelearn_ri/algorithms/__init__.py +0 -0
  3. rulelearn_ri/algorithms/r2n/__init__.py +0 -0
  4. rulelearn_ri/algorithms/r2n/base.py +28 -0
  5. rulelearn_ri/algorithms/r2n/functions.py +25 -0
  6. rulelearn_ri/algorithms/r2n/mask.py +58 -0
  7. rulelearn_ri/algorithms/r2n/preprocessing_layers.py +192 -0
  8. rulelearn_ri/algorithms/r2n/r2n_algo.py +287 -0
  9. rulelearn_ri/algorithms/r2n/simple_rulenet.py +75 -0
  10. rulelearn_ri/algorithms/r2n/training.py +34 -0
  11. rulelearn_ri/algorithms/r2n/utilities.py +117 -0
  12. rulelearn_ri/algorithms/rbm/__init__.py +0 -0
  13. rulelearn_ri/algorithms/rbm/boolean_rule_cg.py +65 -0
  14. rulelearn_ri/algorithms/rbm/rbm/BRCG.py +69 -0
  15. rulelearn_ri/algorithms/rbm/rbm/GLRM.py +98 -0
  16. rulelearn_ri/algorithms/rbm/rbm/__init__.py +6 -0
  17. rulelearn_ri/algorithms/rbm/rbm/beam_search.py +302 -0
  18. rulelearn_ri/algorithms/rbm/rbm/boolean_rule_cg.py +245 -0
  19. rulelearn_ri/algorithms/rbm/rbm/features.py +544 -0
  20. rulelearn_ri/algorithms/rbm/rbm/linear_regression.py +393 -0
  21. rulelearn_ri/algorithms/rbm/rbm/logistic_regression.py +447 -0
  22. rulelearn_ri/algorithms/rbm/utils.py +13 -0
  23. rulelearn_ri/algorithms/ripper/__init__.py +10 -0
  24. rulelearn_ri/algorithms/ripper/base.py +81 -0
  25. rulelearn_ri/algorithms/ripper/binding.py +541 -0
  26. rulelearn_ri/algorithms/ripper/mdl.py +97 -0
  27. rulelearn_ri/algorithms/ripper/pruning.py +212 -0
  28. rulelearn_ri/algorithms/ripper/ripper.py +600 -0
  29. rulelearn_ri/algorithms/ripper/ripper_ruleset_generator.py +26 -0
  30. rulelearn_ri/algorithms/ripper/single_rule.py +425 -0
  31. rulelearn_ri/dise.py +64 -0
  32. rulelearn_ri/trxf/__init__.py +0 -0
  33. rulelearn_ri/trxf/classifier/__init__.py +0 -0
  34. rulelearn_ri/trxf/classifier/ruleset_classifier.py +153 -0
  35. rulelearn_ri/trxf/core/__init__.py +5 -0
  36. rulelearn_ri/trxf/core/boolean_evaluator.py +15 -0
  37. rulelearn_ri/trxf/core/conjunction.py +50 -0
  38. rulelearn_ri/trxf/core/dnf_ruleset.py +64 -0
  39. rulelearn_ri/trxf/core/feature.py +155 -0
  40. rulelearn_ri/trxf/core/predicate.py +98 -0
  41. rulelearn_ri/trxf/core/ruleset_generator.py +25 -0
  42. rulelearn_ri/trxf/core/utils.py +40 -0
  43. rulelearn_ri/trxf/metrics.py +98 -0
  44. rulelearn_ri/trxf/pmml_export/__init__.py +4 -0
  45. rulelearn_ri/trxf/pmml_export/__version__.py +1 -0
  46. rulelearn_ri/trxf/pmml_export/models/__init__.py +24 -0
  47. rulelearn_ri/trxf/pmml_export/models/attribute.py +24 -0
  48. rulelearn_ri/trxf/pmml_export/models/characteristics.py +20 -0
  49. rulelearn_ri/trxf/pmml_export/models/data_dictionary.py +53 -0
  50. rulelearn_ri/trxf/pmml_export/models/mining_schema.py +25 -0
  51. rulelearn_ri/trxf/pmml_export/models/pmml_ruleset_model.py +16 -0
  52. rulelearn_ri/trxf/pmml_export/models/predicate.py +42 -0
  53. rulelearn_ri/trxf/pmml_export/models/rule.py +26 -0
  54. rulelearn_ri/trxf/pmml_export/models/ruleset.py +28 -0
  55. rulelearn_ri/trxf/pmml_export/models/ruleset_model.py +16 -0
  56. rulelearn_ri/trxf/pmml_export/models/scorecard.py +35 -0
  57. rulelearn_ri/trxf/pmml_export/pmml_exporter.py +18 -0
  58. rulelearn_ri/trxf/pmml_export/reader/__init__.py +2 -0
  59. rulelearn_ri/trxf/pmml_export/reader/abstract_reader.py +16 -0
  60. rulelearn_ri/trxf/pmml_export/reader/trxf_reader.py +113 -0
  61. rulelearn_ri/trxf/pmml_export/serializer/__init__.py +2 -0
  62. rulelearn_ri/trxf/pmml_export/serializer/abstract_serializer.py +11 -0
  63. rulelearn_ri/trxf/pmml_export/serializer/nyoka_serializer.py +162 -0
  64. rulelearn_ri/trxf/scorecard/__init__.py +3 -0
  65. rulelearn_ri/trxf/scorecard/bins.py +253 -0
  66. rulelearn_ri/trxf/scorecard/partition.py +71 -0
  67. rulelearn_ri/trxf/scorecard/scorecard.py +78 -0
  68. rulelearn_ripper_interactive-0.2.3.dist-info/METADATA +164 -0
  69. rulelearn_ripper_interactive-0.2.3.dist-info/RECORD +73 -0
  70. rulelearn_ripper_interactive-0.2.3.dist-info/WHEEL +5 -0
  71. rulelearn_ripper_interactive-0.2.3.dist-info/licenses/LICENSE +201 -0
  72. rulelearn_ripper_interactive-0.2.3.dist-info/licenses/NOTICE +11 -0
  73. rulelearn_ripper_interactive-0.2.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,393 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ from sklearn.linear_model import Lasso, Ridge
4
+ from sklearn.base import BaseEstimator, RegressorMixin
5
+
6
+
7
+ import matplotlib.pyplot as plt
8
+
9
+ from .beam_search import beam_search_K1
10
+
11
+
12
+ class LinearRuleRegression(BaseEstimator, RegressorMixin):
13
+ """Linear Rule Regression is a directly interpretable supervised learning
14
+ method that performs linear regression on rule-based features.
15
+ """
16
+ def __init__(self,
17
+ lambda0=0.05,
18
+ lambda1=0.01,
19
+ useOrd=False,
20
+ debias=True,
21
+ K=1,
22
+ iterMax=200,
23
+ B=1,
24
+ wLB=0.5,
25
+ stopEarly=False, eps=1e-6):
26
+
27
+ """
28
+ Args:
29
+ lambda0 (float, optional): Regularization - fixed cost of each rule
30
+ lambda1 (float, optional): Regularization - additional cost of each literal in rule
31
+ useOrd (bool, optional): Also use standardized numerical features
32
+ debias (bool, optional): Re-fit final solution without regularization
33
+ K (int, optional): Column generation - maximum number of columns generated per iteration
34
+ iterMax (int, optional): Column generation - maximum number of iterations
35
+ B (int, optional): Column generation - beam search width
36
+ wLB (float, optional): Column generation - weight on lower bound in evaluating nodes
37
+ stopEarly (bool, optional): Column generation - stop after current degree once improving column found
38
+ eps (float, optional): Numerical tolerance on comparisons
39
+ """
40
+ # Use standardized ordinal features
41
+ self.useOrd = useOrd
42
+ # Regularization parameters
43
+ self.lambda0 = lambda0 # fixed cost of term
44
+ self.lambda1 = lambda1 # cost per literal
45
+ self.debias = debias # re-fit final solution without regularization
46
+ # Column generation parameters
47
+ self.K = K # maximum number of columns generated per iteration
48
+ self.iterMax = iterMax # maximum number of iterations
49
+ self.B = B # beam search width
50
+ self.wLB = wLB # weight on lower bound in evaluating nodes
51
+ self.stopEarly = stopEarly # stop after current degree once improving column found
52
+ # Numerical tolerance on comparisons
53
+ self.eps = eps
54
+
55
+ def fit(self, X, y, Xstd=None):
56
+ """Fit model to training data.
57
+
58
+ Args:
59
+ X (DataFrame): Binarized features with MultiIndex column labels
60
+ y (array): Target variable
61
+ Xstd (DataFrame, optional): Standardized numerical features
62
+ Returns:
63
+ LinearRuleRegression: Self
64
+ """
65
+ # Initialization
66
+ # Number of samples
67
+ n = X.shape[0]
68
+ # Initialize with X itself i.e. singleton conjunctions
69
+ # Feature indicator and conjunction matrices
70
+ z = pd.DataFrame(np.eye(X.shape[1], dtype=int), index=X.columns)
71
+ # Remove negations
72
+ indPos = X.columns.get_level_values(1).isin(['', '<=', '=='])
73
+ z = z.loc[:,indPos]
74
+ A = X.loc[:,indPos].values
75
+ # Scale conjunction matrix to account for non-uniform penalties
76
+ A = A * self.lambda0 / (self.lambda0 + self.lambda1 * z.sum().values)
77
+ if self.useOrd:
78
+ self.namesOrd = Xstd.columns
79
+ numOrd = Xstd.shape[1]
80
+ # Scale ordinal features to have similar std as "average" binary feature
81
+ Astd = 0.4 * Xstd.values
82
+ # Iteration counter
83
+ self.it = 0
84
+ # Mean absolute deviation from mean as upper bound on lambdas that lead to non-constant solutions
85
+ self.mu = y.mean()
86
+ MADM = np.abs(y - self.mu).mean()
87
+ # Lasso object
88
+ lr = Lasso(alpha=self.lambda0 * MADM / 2, selection='cyclic')
89
+
90
+ # Fit Lasso model
91
+ if self.useOrd:
92
+ B = np.concatenate((Astd, A), axis=1)
93
+ lr.fit(B, y)
94
+ # Initial residual
95
+ r = (lr.predict(B) - y) / n / (MADM/2)
96
+ else:
97
+ lr.fit(A, y)
98
+ # Initial residual
99
+ r = (lr.predict(A) - y) / n / (MADM/2)
100
+ # Most "negative" subderivative among current variables (undo scaling)
101
+ UB = -np.abs(np.dot(r, A))
102
+ UB *= (self.lambda0 + self.lambda1 * z.sum().values) / self.lambda0
103
+ UB += self.lambda0 + self.lambda1 * z.sum().values
104
+ UB = min(UB.min(), 0)
105
+
106
+ # Beam search for conjunctions with subdifferentials that exclude zero
107
+ vp, zp, Ap = beam_search_K1(r, X, self.lambda0, self.lambda1,
108
+ UB=UB, B=self.B, wLB=self.wLB, eps=self.eps, stopEarly=self.stopEarly)
109
+ vn, zn, An = beam_search_K1(-r, X, self.lambda0, self.lambda1,
110
+ UB=UB, B=self.B, wLB=self.wLB, eps=self.eps, stopEarly=self.stopEarly)
111
+ v = np.append(vp, vn)
112
+
113
+ while (v < UB).any() and (self.it < self.iterMax):
114
+ # Subdifferentials excluding zero exist, continue
115
+ self.it += 1
116
+ zNew = pd.concat([zp, zn], axis=1, ignore_index=True)
117
+ Anew = np.concatenate((Ap, An), axis=1)
118
+
119
+ # K conjunctions with largest subderivatives in absolute value
120
+ idxLargest = np.argsort(v)[:self.K]
121
+ v = v[idxLargest]
122
+ zNew = zNew.iloc[:,idxLargest]
123
+ Anew = Anew[:,idxLargest]
124
+ # Scale new conjunction matrix to account for non-uniform penalties
125
+ Anew = Anew * self.lambda0 / (self.lambda0 + self.lambda1 * zNew.sum().values)
126
+
127
+ # Add to existing conjunctions
128
+ z = pd.concat([z, zNew], axis=1, ignore_index=True)
129
+ A = np.concatenate((A, Anew), axis=1)
130
+ # Fit Lasso model
131
+ if self.useOrd:
132
+ B = np.concatenate((Astd, A), axis=1)
133
+ lr.fit(B, y)
134
+ # Residual
135
+ r = (lr.predict(B) - y) / n / (MADM/2)
136
+ else:
137
+ lr.fit(A, y)
138
+ # Residual
139
+ r = (lr.predict(A) - y) / n / (MADM/2)
140
+
141
+ # Most "negative" subderivative among current variables (undo scaling)
142
+ UB = -np.abs(np.dot(r, A))
143
+ UB *= (self.lambda0 + self.lambda1 * z.sum().values) / self.lambda0
144
+ UB += self.lambda0 + self.lambda1 * z.sum().values
145
+ UB = min(UB.min(), 0)
146
+
147
+ # Beam search for conjunctions with subdifferentials that exclude zero
148
+ vp, zp, Ap = beam_search_K1(r, X, self.lambda0, self.lambda1,
149
+ UB=UB, B=self.B, wLB=self.wLB, eps=self.eps, stopEarly=self.stopEarly)
150
+ vn, zn, An = beam_search_K1(-r, X, self.lambda0, self.lambda1,
151
+ UB=UB, B=self.B, wLB=self.wLB, eps=self.eps, stopEarly=self.stopEarly)
152
+ v = np.append(vp, vn)
153
+
154
+ # Restrict model to conjunctions with nonzero coefficients
155
+ try:
156
+ idxNonzero = np.where(np.abs(lr.coef_) > self.eps)[0]
157
+ if self.useOrd:
158
+ # Nonzero indices of standardized and rule features
159
+ self.idxNonzeroOrd = idxNonzero[idxNonzero < numOrd]
160
+ nnzOrd = len(self.idxNonzeroOrd)
161
+ idxNonzeroRules = idxNonzero[idxNonzero >= numOrd] - numOrd
162
+ if self.debias and len(idxNonzero):
163
+ # Re-fit Lasso model with effectively no regularization
164
+ z = z.iloc[:,idxNonzeroRules]
165
+ lr = Ridge(alpha=self.eps)
166
+ lr.fit(B[:,idxNonzero], y)
167
+ idxNonzero = np.where(np.abs(lr.coef_) > self.eps)[0]
168
+ # Nonzero indices of standardized and rule features
169
+ idxNonzeroOrd2 = idxNonzero[idxNonzero < nnzOrd]
170
+ self.idxNonzeroOrd = self.idxNonzeroOrd[idxNonzeroOrd2]
171
+ idxNonzeroRules = idxNonzero[idxNonzero >= nnzOrd] - nnzOrd
172
+ self.z = z.iloc[:,idxNonzeroRules]
173
+ lr.coef_ = lr.coef_[idxNonzero]
174
+ else:
175
+ if self.debias and len(idxNonzero):
176
+ # Re-fit Lasso model with effectively no regularization
177
+ z = z.iloc[:,idxNonzero]
178
+ lr = Ridge(alpha=self.eps)
179
+ lr.fit(A[:,idxNonzero], y)
180
+ idxNonzero = np.where(np.abs(lr.coef_) > self.eps)[0]
181
+ self.z = z.iloc[:,idxNonzero]
182
+ lr.coef_ = lr.coef_[idxNonzero]
183
+ except AttributeError:
184
+ # Model has no coefficients except intercept
185
+ self.z = z
186
+ self.lr = lr
187
+
188
+ def compute_conjunctions(self, X):
189
+ """Compute conjunctions of features as specified in self.z.
190
+
191
+ Args:
192
+ X (DataFrame): Binarized features with MultiIndex column labels
193
+ Returns:
194
+ array: A -- Feature conjunction values, shape (X.shape[0], self.z.shape[1])
195
+ """
196
+ try:
197
+ #A = 1 - (np.matmul(1 - X, self.z) > 0)
198
+ A = 1 - (((1 - X) @ self.z) > 0)
199
+ except AttributeError:
200
+ print("Attribute 'z' does not exist, please fit model first.")
201
+ # Scale conjunctions as done in training
202
+ A = A * self.lambda0 / (self.lambda0 + self.lambda1 * self.z.sum().values)
203
+ return A
204
+
205
+ def predict(self, X, Xstd=None):
206
+ """Predict responses.
207
+
208
+ Args:
209
+ X (DataFrame): Binarized features with MultiIndex column labels
210
+ Xstd (DataFrame, optional): Standardized numerical features
211
+ Returns:
212
+ array: yhat -- Predicted responses
213
+ """
214
+ if len(self.lr.coef_):
215
+ # Compute conjunctions of features
216
+ A = self.compute_conjunctions(X)
217
+ if self.useOrd:
218
+ # Selected ordinal features scaled as in training
219
+ Astd = 0.4 * Xstd.values[:,self.idxNonzeroOrd]
220
+ # Predict probabilities
221
+ B = np.concatenate((Astd, A), axis=1)
222
+ return self.lr.predict(B)
223
+ else:
224
+ # Predict labels
225
+ return self.lr.predict(A)
226
+ else:
227
+ return np.full(X.shape[0], self.mu)
228
+
229
+ def explain(self, maxCoeffs=None, highDegOnly=False, prec=2):
230
+ """Return DataFrame holding model features and their coefficients.
231
+
232
+ Args:
233
+ maxCoeffs (int, optional): Maximum number of rules/numerical features to show
234
+ highDegOnly (bool, optional): Only show higher-degree rules
235
+ prec (int, optional): Number of decimal places to show for floating-value thresholds
236
+ Returns:
237
+ DataFrame: dfExpl -- Rules/numerical features and their coefficients
238
+ """
239
+ # Number of ordinal features used
240
+ if self.useOrd:
241
+ nnzOrd = len(self.idxNonzeroOrd)
242
+ else:
243
+ nnzOrd = 0
244
+ if highDegOnly:
245
+ # Restrict to higher-degree rules
246
+ coeffs = self.lr.coef_[nnzOrd:][self.z.sum() > 1]
247
+ nnzOrd = 0
248
+ z = self.z.loc[:, self.z.sum() > 1]
249
+ else:
250
+ coeffs = self.lr.coef_
251
+ z = self.z
252
+ # Initialize DataFrame to be printed
253
+ truncate = (maxCoeffs is not None) and (len(coeffs) > maxCoeffs)
254
+ nRows = maxCoeffs + 1 if truncate else len(coeffs) + 1
255
+ dfExpl = pd.DataFrame(index=range(nRows), columns=['rule','coefficient'])
256
+
257
+ # Intercept term
258
+ dfExpl.at[0, 'rule'] = '(intercept)'
259
+ dfExpl.at[0, 'coefficient'] = self.lr.intercept_
260
+ # Sort coefficients from largest to smallest
261
+ idxSort = np.abs(coeffs).argsort()[:-nRows:-1]
262
+ dfExpl.loc[1:, 'coefficient'] = coeffs[idxSort]
263
+ # Iterate over sorted coefficients
264
+ for (row, i) in enumerate(idxSort):
265
+ if i < nnzOrd:
266
+ # Ordinal feature
267
+ dfExpl.at[row+1, 'rule'] = self.namesOrd[self.idxNonzeroOrd[i]]
268
+ else:
269
+ # MultiIndex of features participating in rule i
270
+ idxFeat = z.index[z.iloc[:,i - nnzOrd] > 0]
271
+ # String representations of features
272
+ strFeat = idxFeat.get_level_values(0) + ' ' + idxFeat.get_level_values(1)\
273
+ + ' ' + idxFeat.get_level_values(2).to_series()\
274
+ .apply(lambda x: ('{:.' + str(prec) + 'f}').format(x) if type(x) is float else str(x))
275
+ # String representation of rule
276
+ dfExpl.at[row+1, 'rule'] = strFeat.str.cat(sep=' AND ')
277
+
278
+ # Return dataframe holding model features and their coefficients
279
+ return dfExpl
280
+
281
+ def visualize(self, Xorig, fb, features=None):
282
+ """Plot generalized additive model component, which includes first-degree rules
283
+ and linear functions of unbinarized ordinal features but excludes higher-degree rules.
284
+
285
+ Args:
286
+ Xorig (DataFrame): Original unbinarized features
287
+ fb: FeatureBinarizer object used to binarize features
288
+ features (list, optional): Subset of features to be plotted
289
+ """
290
+ # Number of ordinal features used
291
+ if self.useOrd:
292
+ nnzOrd = len(self.idxNonzeroOrd)
293
+ else:
294
+ nnzOrd = 0
295
+
296
+ # Initialize terms Series and x values for plots
297
+ terms = pd.Series(index=pd.MultiIndex.from_arrays([[],[],[]], names=self.z.index.names))
298
+ xPlot = {}
299
+
300
+ # Iterate over ordinal features
301
+ for i in range(nnzOrd):
302
+ # Restrict to specified features
303
+ f = self.namesOrd[self.idxNonzeroOrd[i]]
304
+ if (features is not None) and (f not in features):
305
+ continue
306
+ # Append term
307
+ terms = pd.concat([terms, pd.Series(self.lr.coef_[i], index=[(f,'','')])])
308
+ # Initialize x values with min and max
309
+ xPlot[f] = [Xorig[f].min(), Xorig[f].max()]
310
+
311
+ # Iterate over first-degree rules
312
+ for i in range(self.z.shape[1]):
313
+ if self.z.iloc[:,i].sum() > 1:
314
+ continue
315
+ # MultiIndex of rule
316
+ idxTerm = self.z.index[self.z.iloc[:,i] > 0]
317
+ (f, o, v) = idxTerm[0]
318
+ # Restrict to specified features
319
+ if (features is not None) and (f not in features):
320
+ continue
321
+ # Append new term
322
+ terms = pd.concat([terms, pd.Series(self.lr.coef_[i+nnzOrd], index=idxTerm)])
323
+ # Update x values
324
+ if f not in xPlot:
325
+ if o in ['<=','>']:
326
+ # Ordinal feature, initialize with min and max
327
+ xPlot[f] = [Xorig[f].min(), Xorig[f].max()]
328
+ else:
329
+ # Categorical feature, use all values
330
+ xPlot[f] = np.sort(Xorig[f].unique())
331
+ if o in ['<=','>']:
332
+ # Append values around threshold
333
+ xPlot[f].extend([v-self.eps, v+self.eps])
334
+
335
+ # Initialize y values for plots and variance calculation
336
+ yPlot = {}
337
+ yVar = pd.DataFrame(0., index=Xorig.index, columns=xPlot.keys())
338
+ plotLine = pd.Series(False, index=xPlot.keys())
339
+ # Iterate over GAM features
340
+ for f in xPlot.keys():
341
+ # Sort x values
342
+ xPlot[f] = np.sort(np.array(xPlot[f]))
343
+ yPlot[f] = np.zeros_like(xPlot[f], dtype=float)
344
+ # Iterate over terms involving feature
345
+ for ((o,v), c) in terms[f].items():
346
+ if o == '':
347
+ if self.useOrd and (f in fb.ordinal):
348
+ # Add linear function of standardized feature with same factor of 0.4
349
+ idxf = fb.ordinal.index(f)
350
+ yPlot[f] += 0.4 * c * (xPlot[f] - fb.scaler.mean_[idxf]) / fb.scaler.scale_[idxf]
351
+ yVar[f] += 0.4 * c * (Xorig[f] - fb.scaler.mean_[idxf]) / fb.scaler.scale_[idxf]
352
+ plotLine[f] = True
353
+ else:
354
+ # Binary feature, add indicator function
355
+ yPlot[f] += c * (xPlot[f] == fb.maps[f].index[1])
356
+ yVar[f] += c * (Xorig[f] == fb.maps[f].index[1])
357
+ elif o == '<=':
358
+ # Add step function
359
+ yPlot[f] += c * (xPlot[f] <= v)
360
+ yVar[f] += c * (Xorig[f] <= v)
361
+ plotLine[f] = True
362
+ elif o == '>':
363
+ # Add step function
364
+ yPlot[f] += c * (xPlot[f] > v)
365
+ yVar[f] += c * (Xorig[f] > v)
366
+ plotLine[f] = True
367
+ elif o == '==':
368
+ # Add indicator function
369
+ yPlot[f] += c * (xPlot[f].astype(str) == v)
370
+ yVar[f] += c * (Xorig[f].astype(str) == v)
371
+ elif o == '!=':
372
+ # Add indicator function
373
+ yPlot[f] += c * (xPlot[f].astype(str) != v)
374
+ yVar[f] += c * (Xorig[f].astype(str) != v)
375
+ elif o == 'not':
376
+ # Binary feature, add indicator function
377
+ yPlot[f] += c * (xPlot[f] == fb.maps[f].index[0])
378
+ yVar[f] += c * (Xorig[f] == fb.maps[f].index[0])
379
+
380
+ # Plot in order of descending variance
381
+ figs = {}
382
+ yVar2 = yVar.var().sort_values(ascending=False)
383
+ for f in yVar2.index:
384
+ figs[f] = plt.figure()
385
+ ax = figs[f].add_subplot(111)
386
+ if plotLine[f]:
387
+ ax.plot(xPlot[f], yPlot[f])
388
+ else:
389
+ ax.bar(xPlot[f], yPlot[f])
390
+ ax.xaxis.set_ticks(xPlot[f])
391
+ plt.xlabel(f)
392
+ plt.ylabel('contribution to prediction')
393
+ return figs, yVar2