Skip to content

Commit 0da6598

Browse files
authored
Merge pull request #2320 from lorenzgerber/pca_statistics
PCA, descriptive statistics for features
2 parents 52f58da + 91c1ade commit 0da6598

File tree

7 files changed

+627
-1
lines changed

7 files changed

+627
-1
lines changed

chemclipse/plugins/org.eclipse.chemclipse.xxd.process.supplier.pca.ui/META-INF/MANIFEST.MF

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ Require-Bundle: org.eclipse.ui,
4747
org.eclipse.help
4848
Bundle-RequiredExecutionEnvironment: JavaSE-21
4949
Import-Package: jakarta.annotation;version="2.1.1",
50-
jakarta.inject;version="2.0.1"
50+
jakarta.inject;version="2.0.1",
51+
org.apache.commons.math3.stat.descriptive;version="[3.6.0,4.0.0)"
5152
Bundle-ActivationPolicy: lazy
5253
Service-Component: OSGI-INF/org.eclipse.chemclipse.xxd.process.supplier.pca.ui.quickstart.FileTileDefinition.xml,
5354
OSGI-INF/org.eclipse.chemclipse.xxd.process.supplier.pca.ui.quickstart.FilesLongFormatTileDefinition.xml,

chemclipse/plugins/org.eclipse.chemclipse.xxd.process.supplier.pca.ui/fragment.e4xmi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<elements xsi:type="basic:Part" xmi:id="_pUF1wPRuEeeapKw7g2Cagg" elementId="org.eclipse.chemclipse.xxd.process.supplier.pca.ui.part.pca2dscoreplot" contributionURI="bundleclass://org.eclipse.chemclipse.xxd.process.supplier.pca.ui/org.eclipse.chemclipse.xxd.process.supplier.pca.ui.parts.ScorePlot2DPart" label="Score Plot 2D" iconURI="platform:/plugin/org.eclipse.chemclipse.rcp.ui.icons/icons/16x16/scoreplot2D.png"/>
88
<elements xsi:type="basic:Part" xmi:id="_T6DG0D7wEeiZJpBc4U-l1A" elementId="org.eclipse.chemclipse.xxd.process.supplier.pca.ui.part.explainedvariance" contributionURI="bundleclass://org.eclipse.chemclipse.xxd.process.supplier.pca.ui/org.eclipse.chemclipse.xxd.process.supplier.pca.ui.parts.VariancePart" label="Explained Variance" iconURI="platform:/plugin/org.eclipse.chemclipse.rcp.ui.icons/icons/16x16/barchart2.png"/>
99
<elements xsi:type="basic:Part" xmi:id="_4x8EIDVNEfCIk78mmk2-TA" elementId="org.eclipse.chemclipse.xxd.process.supplier.pca.ui.part.filterdistribution" contributionURI="bundleclass://org.eclipse.chemclipse.xxd.process.supplier.pca.ui/org.eclipse.chemclipse.xxd.process.supplier.pca.ui.parts.FilterDistributionPart" label="Filter Distribution" iconURI="platform:/plugin/org.eclipse.chemclipse.rcp.ui.icons/icons/16x16/filter.png"/>
10+
<elements xsi:type="basic:Part" xmi:id="_PixEIdPaDNEfCIPokk2-TA" elementId="org.eclipse.chemclipse.xxd.process.supplier.pca.ui.part.featurestattable" contributionURI="bundleclass://org.eclipse.chemclipse.xxd.process.supplier.pca.ui/org.eclipse.chemclipse.xxd.process.supplier.pca.ui.parts.FeatureStatTablePart" label="Feature Statistics" iconURI="platform:/plugin/org.eclipse.chemclipse.rcp.ui.icons/icons/16x16/table.png"/>
1011
</fragments>
1112
<fragments xsi:type="fragment:StringModelFragment" xmi:id="_cgvnsN3IEee_Jr66fOQ3Hg" featurename="children" parentElementId="org.eclipse.chemclipse.rcp.app.ui.perspectivestack.main">
1213
<elements xsi:type="advanced:Perspective" xmi:id="_gMoqoN3IEee_Jr66fOQ3Hg" elementId="org.eclipse.chemclipse.xxd.process.supplier.pca.perspective" label="PCA (Principal Component Analysis)" iconURI="platform:/plugin/org.eclipse.chemclipse.rcp.ui.icons/icons/16x16/pca.gif">
@@ -19,6 +20,7 @@
1920
<children xsi:type="advanced:Placeholder" xmi:id="_jHao4D7wEeiZJpBc4U-l1A" elementId="org.eclipse.chemclipse.xxd.process.supplier.pca.ui.part.explainedvariance" ref="_T6DG0D7wEeiZJpBc4U-l1A"/>
2021
<children xsi:type="advanced:Placeholder" xmi:id="_ZAXTIPRvEeeapKw7g2Cagg" elementId="org.eclipse.chemclipse.xxd.process.supplier.pca.ui.part.pcadatatable" ref="_f3a8cPRtEeeapKw7g2Cagg"/>
2122
<children xsi:type="advanced:Placeholder" xmi:id="_roEjgDVQEfCIk78mmk2-TA" elementId="org.eclipse.chemclipse.xxd.process.supplier.pca.ui.part.filterDistribution" ref="_4x8EIDVNEfCIk78mmk2-TA"/>
23+
<children xsi:type="advanced:Placeholder" xmi:id="_PioEgDVPaQEIkPo78mm-TA" elementId="org.eclipse.chemclipse.xxd.process.supplier.pca.ui.part.featurestattable" ref="_PixEIdPaDNEfCIPokk2-TA"/>
2224
</children>
2325
</children>
2426
<children xsi:type="basic:PartSashContainer" xmi:id="_J7oIoPRvEeeapKw7g2Cagg" elementId="org.eclipse.chemclipse.xxd.process.supplier.pca.ui.partsashcontainer.2" horizontal="true">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Lablicate GmbH.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Lorenz Gerber - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.chemclipse.xxd.process.supplier.pca.ui.internal.provider;
14+
15+
import java.util.List;
16+
17+
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
18+
import org.eclipse.chemclipse.model.statistics.ISampleData;
19+
import org.eclipse.chemclipse.model.statistics.IVariable;
20+
import org.eclipse.chemclipse.support.ui.swt.AbstractRecordTableComparator;
21+
import org.eclipse.chemclipse.support.ui.swt.IRecordTableComparator;
22+
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.Feature;
23+
import org.eclipse.jface.viewers.Viewer;
24+
25+
public class FeatureStatComparator extends AbstractRecordTableComparator implements IRecordTableComparator {
26+
27+
@Override
28+
public int compare(Viewer viewer, Object e1, Object e2) {
29+
30+
int sortOrder = 0;
31+
if(e1 instanceof Feature feature1 && e2 instanceof Feature feature2) {
32+
IVariable variable1 = feature1.getVariable();
33+
IVariable variable2 = feature2.getVariable();
34+
DescriptiveStatistics stats = new DescriptiveStatistics();
35+
List<ISampleData<?>> sampleData1 = feature1.getSampleData();
36+
List<ISampleData<?>> sampleData2 = feature2.getSampleData();
37+
//
38+
int columnIndex = getPropertyIndex();
39+
switch(columnIndex) {
40+
case 0:
41+
try {
42+
double value1 = Double.parseDouble(variable1.getValue());
43+
double value2 = Double.parseDouble(variable2.getValue());
44+
sortOrder = Double.compare(value2, value1);
45+
} catch(Exception e) {
46+
sortOrder = variable2.getValue().compareTo(variable1.getValue());
47+
}
48+
break;
49+
case 1:
50+
sortOrder = variable2.getDescription().compareTo(variable1.getDescription());
51+
break;
52+
case 2:
53+
for(int i = 0; i < sampleData1.size(); i++) {
54+
stats.addValue(sampleData1.get(i).getData());
55+
}
56+
Double mean1 = stats.getMean();
57+
stats = new DescriptiveStatistics();
58+
for(int i = 0; i < sampleData2.size(); i++) {
59+
stats.addValue(sampleData2.get(i).getData());
60+
}
61+
Double mean2 = stats.getMean();
62+
sortOrder = Double.compare(mean2, mean1);
63+
break;
64+
case 3:
65+
for(int i = 0; i < sampleData1.size(); i++) {
66+
stats.addValue(sampleData1.get(i).getData());
67+
}
68+
Double min1 = stats.getMin();
69+
stats = new DescriptiveStatistics();
70+
for(int i = 0; i < sampleData2.size(); i++) {
71+
stats.addValue(sampleData2.get(i).getData());
72+
}
73+
Double min2 = stats.getMin();
74+
sortOrder = Double.compare(min2, min1);
75+
break;
76+
case 4:
77+
for(int i = 0; i < sampleData1.size(); i++) {
78+
stats.addValue(sampleData1.get(i).getData());
79+
}
80+
Double max1 = stats.getMax();
81+
stats = new DescriptiveStatistics();
82+
for(int i = 0; i < sampleData2.size(); i++) {
83+
stats.addValue(sampleData2.get(i).getData());
84+
}
85+
Double max2 = stats.getMax();
86+
sortOrder = Double.compare(max2, max1);
87+
break;
88+
case 5:
89+
for(int i = 0; i < sampleData1.size(); i++) {
90+
stats.addValue(sampleData1.get(i).getData());
91+
}
92+
Double rsd1 = stats.getStandardDeviation();
93+
rsd1 = 100.0 / stats.getSum() * rsd1;
94+
stats = new DescriptiveStatistics();
95+
for(int i = 0; i < sampleData2.size(); i++) {
96+
stats.addValue(sampleData2.get(i).getData());
97+
}
98+
Double rsd2 = stats.getStandardDeviation();
99+
rsd2 = 100.0 / stats.getSum() * rsd2;
100+
sortOrder = Double.compare(rsd2, rsd1);
101+
break;
102+
case 6:
103+
for(int i = 0; i < sampleData1.size(); i++) {
104+
stats.addValue(sampleData1.get(i).getData());
105+
}
106+
Double skew1 = stats.getSkewness();
107+
stats = new DescriptiveStatistics();
108+
for(int i = 0; i < sampleData2.size(); i++) {
109+
stats.addValue(sampleData2.get(i).getData());
110+
}
111+
Double skew2 = stats.getSkewness();
112+
sortOrder = Double.compare(skew2, skew1);
113+
break;
114+
case 7:
115+
for(int i = 0; i < sampleData1.size(); i++) {
116+
stats.addValue(sampleData1.get(i).getData());
117+
}
118+
Double kurt1 = stats.getKurtosis();
119+
stats = new DescriptiveStatistics();
120+
for(int i = 0; i < sampleData2.size(); i++) {
121+
stats.addValue(sampleData2.get(i).getData());
122+
}
123+
Double kurt2 = stats.getKurtosis();
124+
sortOrder = Double.compare(kurt2, kurt1);
125+
break;
126+
default:
127+
break;
128+
}
129+
}
130+
if(getDirection() == ASCENDING) {
131+
sortOrder = -sortOrder;
132+
}
133+
return sortOrder;
134+
}
135+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Lablicate GmbH.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Lorenz Gerber - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.chemclipse.xxd.process.supplier.pca.ui.internal.provider;
14+
15+
import java.text.DecimalFormat;
16+
import java.util.List;
17+
18+
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
19+
import org.eclipse.chemclipse.model.statistics.ISampleData;
20+
import org.eclipse.chemclipse.model.statistics.IVariable;
21+
import org.eclipse.chemclipse.support.ui.provider.AbstractChemClipseLabelProvider;
22+
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.Feature;
23+
import org.eclipse.swt.graphics.Image;
24+
25+
public class FeatureStatLabelProvider extends AbstractChemClipseLabelProvider {
26+
27+
public static final String VARIABLE = "Variable";
28+
public static final String NAME = "Name";
29+
public static final String MEAN = "Mean";
30+
public static final String MIN = "Min";
31+
public static final String MAX = "Max";
32+
public static final String RELATIVESTDDEV = "RSD";
33+
public static final String SKEWNESS = "Skewness";
34+
public static final String KURTOSIS = "Excess Kurtosis";
35+
//
36+
private DecimalFormat decimalFormat = getDecimalFormat();
37+
//
38+
public static String[] TITLES = {//
39+
VARIABLE, //
40+
NAME, //
41+
MEAN, //
42+
MIN, //
43+
MAX, //
44+
RELATIVESTDDEV, //
45+
SKEWNESS, //
46+
KURTOSIS, //
47+
};
48+
public static int[] BOUNDS = {//
49+
50, //
50+
280, //
51+
140, //
52+
140, //
53+
140, //
54+
140, //
55+
140, //
56+
140 //
57+
};
58+
59+
public FeatureStatLabelProvider(String pattern) {
60+
61+
super(pattern);
62+
}
63+
64+
@Override
65+
public String getColumnText(Object element, int columnIndex) {
66+
67+
String text = "";
68+
if(element instanceof Feature feature) {
69+
IVariable variable = feature.getVariable();
70+
List<ISampleData<?>> sampleData = feature.getSampleData();
71+
DescriptiveStatistics stats = new DescriptiveStatistics();
72+
for(int i = 0; i < sampleData.size(); i++) {
73+
stats.addValue(sampleData.get(i).getData());
74+
}
75+
double value = 0.0;
76+
//
77+
switch(columnIndex) {
78+
case 0:
79+
text = variable.getValue();
80+
break;
81+
case 1:
82+
text = variable.getDescription();
83+
break;
84+
case 2:
85+
value = 0.0;
86+
value = stats.getMean();
87+
text = Double.isNaN(value) ? "NaN" : decimalFormat.format(value);
88+
break;
89+
case 3:
90+
value = 0.0;
91+
value = stats.getMin();
92+
text = Double.isNaN(value) ? "NaN" : decimalFormat.format(value);
93+
break;
94+
case 4:
95+
value = 0.0;
96+
value = stats.getMax();
97+
text = Double.isNaN(value) ? "NaN" : decimalFormat.format(value);
98+
break;
99+
case 5:
100+
value = 0.0;
101+
value = 100.0 / stats.getSum() * stats.getStandardDeviation();
102+
text = Double.isNaN(value) ? "NaN" : decimalFormat.format(value);
103+
break;
104+
case 6:
105+
value = 0.0;
106+
value = stats.getSkewness();
107+
text = Double.isNaN(value) ? "NaN" : decimalFormat.format(value);
108+
break;
109+
case 7:
110+
value = 0.0;
111+
value = stats.getKurtosis() - 3.0;
112+
text = Double.isNaN(value) ? "NaN" : decimalFormat.format(value);
113+
default:
114+
break;
115+
}
116+
}
117+
return text;
118+
}
119+
120+
@Override
121+
public Image getColumnImage(Object element, int columnIndex) {
122+
123+
return null;
124+
}
125+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Lablicate GmbH.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Lorenz Gerber - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.chemclipse.xxd.process.supplier.pca.ui.parts;
14+
15+
import java.util.List;
16+
17+
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.IEvaluation;
18+
import org.eclipse.chemclipse.xxd.process.supplier.pca.ui.swt.ExtendedFeatureStatListUI;
19+
import org.eclipse.swt.SWT;
20+
import org.eclipse.swt.widgets.Composite;
21+
22+
import jakarta.inject.Inject;
23+
24+
public class FeatureStatTablePart extends AbstractPartPCA<ExtendedFeatureStatListUI> {
25+
26+
@Inject
27+
public FeatureStatTablePart(Composite parent) {
28+
29+
super(parent);
30+
}
31+
32+
@Override
33+
protected ExtendedFeatureStatListUI createControl(Composite parent) {
34+
35+
return new ExtendedFeatureStatListUI(parent, SWT.NONE);
36+
}
37+
38+
@SuppressWarnings("unchecked")
39+
@Override
40+
protected boolean updateData(List<Object> objects, String topic) {
41+
42+
if(objects.size() == 1) {
43+
if(isUnloadEvent(topic)) {
44+
getControl().setInput(null);
45+
unloadData();
46+
return false;
47+
} else if(isUpdateSelection(topic)) {
48+
Object object = objects.get(0);
49+
if(object instanceof IEvaluation evaluation) {
50+
getControl().setInput(evaluation);
51+
return true;
52+
}
53+
}
54+
}
55+
//
56+
return false;
57+
}
58+
}

0 commit comments

Comments
 (0)