|
| 1 | +from sklearn.discriminant_analysis import LinearDiscriminantAnalysis |
| 2 | + |
| 3 | +import Orange.data |
| 4 | +from Orange.classification.logistic_regression import _FeatureScorerMixin |
| 5 | +from Orange.data.util import SharedComputeValue |
| 6 | +from Orange.projection import SklProjector, Projection, LinearCombinationSql |
| 7 | + |
| 8 | +__all__ = ["LDA"] |
| 9 | + |
| 10 | + |
| 11 | +class LDA(SklProjector, _FeatureScorerMixin): |
| 12 | + name = "LDA" |
| 13 | + supports_sparse = False |
| 14 | + |
| 15 | + def __init__(self, n_components=2, solver='eigen', preprocessors=None): |
| 16 | + super().__init__(preprocessors=preprocessors) |
| 17 | + self.n_components = n_components |
| 18 | + self.solver = solver |
| 19 | + |
| 20 | + def fit(self, X, Y=None): |
| 21 | + if self.n_components is not None: |
| 22 | + self.n_components = min(min(X.shape), self.n_components) |
| 23 | + proj = LinearDiscriminantAnalysis(solver='eigen', n_components=2) |
| 24 | + proj = proj.fit(X, Y) |
| 25 | + return LDAModel(proj, self.domain) |
| 26 | + |
| 27 | + |
| 28 | +class _LDATransformDomain: |
| 29 | + """Computation common for all LDA variables.""" |
| 30 | + def __init__(self, lda): |
| 31 | + self.lda = lda |
| 32 | + |
| 33 | + def __call__(self, data): |
| 34 | + if data.domain != self.lda.pre_domain: |
| 35 | + data = data.transform(self.lda.pre_domain) |
| 36 | + return self.lda.transform(data.X) |
| 37 | + |
| 38 | + |
| 39 | +class LDAModel(Projection): |
| 40 | + name = "LDAModel" |
| 41 | + |
| 42 | + def __init__(self, proj, domain): |
| 43 | + lda_transform = _LDATransformDomain(self) |
| 44 | + self.components_ = proj.scalings_.T |
| 45 | + |
| 46 | + def lda_variable(i): |
| 47 | + return Orange.data.ContinuousVariable( |
| 48 | + 'LD%d' % (i + 1), compute_value=LDAProjector(self, i, lda_transform)) |
| 49 | + |
| 50 | + super().__init__(proj=proj) |
| 51 | + self.orig_domain = domain |
| 52 | + self.n_components = self.components_.shape[0] |
| 53 | + self.domain = Orange.data.Domain( |
| 54 | + [lda_variable(i) for i in range(proj.n_components)], |
| 55 | + domain.class_vars, domain.metas) |
| 56 | + |
| 57 | + |
| 58 | +class LDAProjector(SharedComputeValue): |
| 59 | + """Transform into a given LDA component.""" |
| 60 | + def __init__(self, projection, feature, lda_transform): |
| 61 | + super().__init__(lda_transform) |
| 62 | + self.feature = feature |
| 63 | + |
| 64 | + def compute(self, data, lda_space): |
| 65 | + return lda_space[:, self.feature] |
0 commit comments