Skip to content

Commit 2abf9e4

Browse files
committed
added t-test for results
1 parent 1afad29 commit 2abf9e4

File tree

5 files changed

+125
-23
lines changed

5 files changed

+125
-23
lines changed

webdriver-ts-results/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
},
4343
"dependencies": {
4444
"babel-polyfill": "^6.23.0",
45+
"jStat": "^1.6.2",
4546
"react": "15.5.4",
4647
"react-dom": "15.5.4"
4748
}

webdriver-ts-results/src/App.tsx

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ interface State {
2121
separateKeyedAndNonKeyed: boolean;
2222
resultTables: Array<ResultTableData>;
2323
sortKey: string;
24+
compareWith: Framework | undefined;
2425
}
2526

2627
let allBenchmarks = () => benchmarks.reduce((set, b) => set.add(b), new Set() );
@@ -37,13 +38,13 @@ class App extends React.Component<{}, State> {
3738
event.preventDefault();
3839
let set = this.state.selectedBenchmarks;
3940
benchmarks.forEach(b => {if ((b.type === BenchmarkType.MEM) === memBenchmarks) set.add(b);});
40-
this.setState({selectedBenchmarks: set, resultTables: this.updateResultTable(set, this.state.selectedFrameworks, this.state.separateKeyedAndNonKeyed, this.state.sortKey)});
41+
this.setState({selectedBenchmarks: set, resultTables: this.updateResultTable(set, this.state.selectedFrameworks, this.state.separateKeyedAndNonKeyed, this.state.sortKey, this.state.compareWith)});
4142
},
4243
selectNone: (event: React.SyntheticEvent<any>) => {
4344
event.preventDefault();
4445
let set = this.state.selectedBenchmarks;
4546
benchmarks.forEach(b => {if ((b.type === BenchmarkType.MEM) === memBenchmarks) set.delete(b);});
46-
this.setState({selectedBenchmarks: set, sortKey: SORT_BY_NAME, resultTables: this.updateResultTable(set, this.state.selectedFrameworks, this.state.separateKeyedAndNonKeyed, SORT_BY_NAME)});
47+
this.setState({selectedBenchmarks: set, sortKey: SORT_BY_NAME, resultTables: this.updateResultTable(set, this.state.selectedFrameworks, this.state.separateKeyedAndNonKeyed, SORT_BY_NAME, this.state.compareWith)});
4748
},
4849
areAllSelected: () => benchmarks.filter(b => memBenchmarks ? b.type === BenchmarkType.MEM : b.type !== BenchmarkType.MEM)
4950
.every(b => this.state.selectedBenchmarks.has(b)),
@@ -56,13 +57,13 @@ class App extends React.Component<{}, State> {
5657
event.preventDefault();
5758
let set = this.state.selectedFrameworks;
5859
frameworks.forEach(framework => {if (framework.nonKeyed === nonKeyed && !set.has(framework)) set.add(framework);});
59-
this.setState({selectedFrameworks: set, resultTables: this.updateResultTable(this.state.selectedBenchmarks, set, this.state.separateKeyedAndNonKeyed, this.state.sortKey)});
60+
this.setState({selectedFrameworks: set, resultTables: this.updateResultTable(this.state.selectedBenchmarks, set, this.state.separateKeyedAndNonKeyed, this.state.sortKey, this.state.compareWith)});
6061
},
6162
selectNone: (event: React.SyntheticEvent<any>) => {
6263
event.preventDefault();
6364
let set = this.state.selectedFrameworks;
6465
set.forEach(framework => {if (framework.nonKeyed === nonKeyed) set.delete(framework);});
65-
this.setState({selectedFrameworks: set, resultTables: this.updateResultTable(this.state.selectedBenchmarks, set, this.state.separateKeyedAndNonKeyed, this.state.sortKey)});
66+
this.setState({selectedFrameworks: set, resultTables: this.updateResultTable(this.state.selectedBenchmarks, set, this.state.separateKeyedAndNonKeyed, this.state.sortKey, this.state.compareWith)});
6667
},
6768
areAllSelected: () => frameworks.filter(f => f.nonKeyed===nonKeyed).every(f => this.state.selectedFrameworks.has(f)),
6869
isNoneSelected: () => frameworks.filter(f => f.nonKeyed===nonKeyed).every(f => !this.state.selectedFrameworks.has(f)),
@@ -84,8 +85,9 @@ class App extends React.Component<{}, State> {
8485
selectedBenchmarks: _allBenchmarks,
8586
selectedFrameworks: _allFrameworks,
8687
separateKeyedAndNonKeyed: true,
87-
resultTables: this.updateResultTable(_allBenchmarks, _allFrameworks, true, SORT_BY_NAME),
88-
sortKey: SORT_BY_NAME
88+
resultTables: this.updateResultTable(_allBenchmarks, _allFrameworks, true, SORT_BY_NAME, undefined),
89+
sortKey: SORT_BY_NAME,
90+
compareWith: undefined
8991
};
9092
}
9193
selectBenchmark = (benchmark: Benchmark, value: boolean) => {
@@ -97,29 +99,39 @@ class App extends React.Component<{}, State> {
9799
let setIds = new Set();
98100
set.forEach(b => setIds.add(b.id))
99101
if ((sortKey!=SORT_BY_NAME && sortKey!=SORT_BY_GEOMMEAN) && !setIds.has(sortKey)) sortKey = SORT_BY_NAME;
100-
this.setState({selectedBenchmarks: set, sortKey, resultTables: this.updateResultTable(set, this.state.selectedFrameworks, this.state.separateKeyedAndNonKeyed, sortKey)});
102+
this.setState({selectedBenchmarks: set, sortKey, resultTables: this.updateResultTable(set, this.state.selectedFrameworks, this.state.separateKeyedAndNonKeyed, sortKey, this.state.compareWith)});
101103
}
102104
selectFramework = (framework: Framework, value: boolean): void => {
103105
let set = new Set();
104106
this.state.selectedFrameworks.forEach(framework => set.add(framework));
105107
if (set.has(framework)) set.delete(framework);
106108
else set.add(framework);
107-
this.setState({selectedFrameworks: set, resultTables: this.updateResultTable(this.state.selectedBenchmarks, set, this.state.separateKeyedAndNonKeyed, this.state.sortKey)});
109+
this.setState({selectedFrameworks: set, resultTables: this.updateResultTable(this.state.selectedBenchmarks, set, this.state.separateKeyedAndNonKeyed, this.state.sortKey, this.state.compareWith)});
108110
}
109111
selectSeparateKeyedAndNonKeyed = (value: boolean): void => {
110-
this.setState({separateKeyedAndNonKeyed: value, resultTables: this.updateResultTable(this.state.selectedBenchmarks, this.state.selectedFrameworks, value, this.state.sortKey)});
112+
this.setState({separateKeyedAndNonKeyed: value, resultTables: this.updateResultTable(this.state.selectedBenchmarks, this.state.selectedFrameworks, value, this.state.sortKey, this.state.compareWith)});
111113
}
112-
updateResultTable(selectedBenchmarks: Set<Benchmark>, selectedFrameworks: Set<Framework>, separateKeyedAndNonKeyed: boolean, sortKey: string) {
114+
updateResultTable(selectedBenchmarks: Set<Benchmark>, selectedFrameworks: Set<Framework>, separateKeyedAndNonKeyed: boolean, sortKey: string, compareWith: Framework|undefined) {
113115
if (separateKeyedAndNonKeyed) {
114-
return [new ResultTableData(frameworks, benchmarks, resultLookup, selectedFrameworks, selectedBenchmarks, false, sortKey),
115-
new ResultTableData(frameworks, benchmarks, resultLookup, selectedFrameworks, selectedBenchmarks, true, sortKey)]
116+
return [new ResultTableData(frameworks, benchmarks, resultLookup, selectedFrameworks, selectedBenchmarks, false, sortKey, compareWith),
117+
new ResultTableData(frameworks, benchmarks, resultLookup, selectedFrameworks, selectedBenchmarks, true, sortKey, compareWith)]
116118
} else {
117-
return [new ResultTableData(frameworks, benchmarks, resultLookup, selectedFrameworks, selectedBenchmarks, undefined, sortKey)]
119+
return [new ResultTableData(frameworks, benchmarks, resultLookup, selectedFrameworks, selectedBenchmarks, undefined, sortKey, compareWith)]
118120
}
119121
}
122+
selectComparison = (framework: string): void => {
123+
let compareWith: Framework | undefined = undefined;
124+
compareWith = this.state.frameworksKeyed.find((f) => f.name === framework);
125+
if (!compareWith) {
126+
compareWith = this.state.frameworksNonKeyed.find((f) => f.name === framework);
127+
}
128+
console.log("compareWith", compareWith);
129+
this.setState({compareWith:compareWith, resultTables: this.updateResultTable(this.state.selectedBenchmarks, this.state.selectedFrameworks, this.state.separateKeyedAndNonKeyed, this.state.sortKey, compareWith)});
130+
}
131+
120132
sortBy = (sortkey: string, tableIdx: number): void => {
121133
this.state.resultTables[tableIdx].sortBy(sortkey);
122-
this.setState({sortKey:sortkey, resultTables: this.updateResultTable(this.state.selectedBenchmarks, this.state.selectedFrameworks, this.state.separateKeyedAndNonKeyed, sortkey)});
134+
this.setState({sortKey:sortkey, resultTables: this.updateResultTable(this.state.selectedBenchmarks, this.state.selectedFrameworks, this.state.separateKeyedAndNonKeyed, sortkey, this.state.compareWith)});
123135
}
124136
render() {
125137
let disclaimer = (false) ? (<div>
@@ -143,7 +155,15 @@ class App extends React.Component<{}, State> {
143155
selectBenchmark={this.selectBenchmark}
144156
selectFramework={this.selectFramework}
145157
selectSeparateKeyedAndNonKeyed={this.selectSeparateKeyedAndNonKeyed}
146-
separateKeyedAndNonKeyed={this.state.separateKeyedAndNonKeyed}/>
158+
separateKeyedAndNonKeyed={this.state.separateKeyedAndNonKeyed}
159+
compareWith={this.state.compareWith}
160+
selectComparison={this.selectComparison}
161+
/>
162+
{!this.state.compareWith ? null :
163+
(<p style={{marginTop:'10px'}}>In comparison mode white cells mean there's no statistically significant difference.
164+
Green cells are significantly faster than the comparison and red cells are slower.
165+
The test is performed as a one sided t-test. The significance level is 10%. The darker the color the lower the p-Value.</p>
166+
)}
147167
<ResultTable currentSortKey={this.state.sortKey} data={this.state.resultTables} separateKeyedAndNonKeyed={this.state.separateKeyedAndNonKeyed} sortBy={this.sortBy}/>
148168
</div>
149169
);

webdriver-ts-results/src/Common.tsx

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react';
2+
var jStat:any = require('jstat').jStat;
23

34
export interface Framework {
45
name: string;
@@ -22,6 +23,7 @@ export interface Result {
2223
min: number;
2324
max: number;
2425
mean: number;
26+
count?: number;
2527
geometricMean: number;
2628
standardDeviation: number;
2729
}
@@ -59,11 +61,13 @@ let computeColor = function(factor: number): string {
5961

6062
export class TableResultValueEntry implements TableResultEntry {
6163
color: string;
62-
constructor(public key:string, public mean: number, public standardDeviation: number, public factor: number) {
64+
constructor(public key:string, public mean: number, public standardDeviation: number, public factor: number, public statisticallySignificantFactor: string|number|undefined, public statisticalCol: [string,string]|undefined) {
6365
this.color = computeColor(factor);
6466
}
6567
render() {
66-
return (<td key={this.key} style={{backgroundColor:this.color}}>
68+
let col = this.statisticalCol === undefined ? this.color : this.statisticalCol[0];
69+
let textCol = this.statisticalCol === undefined ? '#000' : this.statisticalCol[1];
70+
return (<td key={this.key} style={{backgroundColor:col, color: textCol}}>
6771
<span className="mean">{this.mean.toFixed(1)}</span>
6872
<span className="deviation">{this.standardDeviation.toFixed(1)}</span>
6973
<br />
@@ -101,6 +105,25 @@ export function convertToMap(results: Array<Result>): ResultLookup {
101105
}
102106
}
103107

108+
let statisticComputeColor = function(sign: number, pValue: number): [string, string] {
109+
if (pValue < 0.90) {
110+
return ['#fff','#000'];
111+
}
112+
if (sign < 0) {
113+
let a = (pValue - 0.9) * 10.0;
114+
let r = 0;
115+
let g = (1.0-a)* 255 + a * 160;
116+
let b = 0;
117+
return [`rgb(${r.toFixed(0)}, ${g.toFixed(0)}, ${b.toFixed(0)})`, '#fff'];
118+
} else {
119+
let a = (pValue - 0.9) * 10.0;
120+
let r = (1.0-a)* 255 + a * 160;
121+
let g = 0;
122+
let b = 0;
123+
return [`rgb(${r.toFixed(0)}, ${g.toFixed(0)}, ${b.toFixed(0)})`, '#fff'];
124+
}
125+
}
126+
104127
export class ResultTableData {
105128
// Rows
106129
benchmarksCPU: Array<Benchmark>;
@@ -113,7 +136,8 @@ export class ResultTableData {
113136
resultsMEM: Array<Array<TableResultValueEntry|null>>;
114137

115138
constructor(public allFrameworks: Array<Framework>, public allBenchmarks: Array<Benchmark>, public results: ResultLookup,
116-
public selectedFrameworks: Set<Framework>, public selectedBenchmarks: Set<Benchmark>, nonKeyed: boolean|undefined, sortKey: string) {
139+
public selectedFrameworks: Set<Framework>, public selectedBenchmarks: Set<Benchmark>, nonKeyed: boolean|undefined, sortKey: string,
140+
public compareWith: Framework|undefined) {
117141
this.frameworks = this.allFrameworks.filter(framework => (nonKeyed===undefined || framework.nonKeyed === nonKeyed) && selectedFrameworks.has(framework));
118142
this.update(sortKey);
119143
}
@@ -176,6 +200,7 @@ export class ResultTableData {
176200
}
177201
computeFactors(benchmark: Benchmark, clamp: boolean): Array<TableResultValueEntry|null> {
178202
let benchmarkResults = this.frameworks.map(f => this.results(benchmark, f));
203+
let compareWithResults = this.compareWith ? this.results(benchmark, this.compareWith) : undefined;
179204
let min = benchmarkResults.reduce((min, result) => result===null ? min : Math.min(min,result.mean), Number.POSITIVE_INFINITY);
180205
return this.frameworks.map(f => {
181206
let result = this.results(benchmark, f);
@@ -184,7 +209,35 @@ export class ResultTableData {
184209
let mean = result.mean;
185210
let factor = clamp ? Math.max(16, result.mean) / Math.max(16, min) : result.mean/min;
186211
let standardDeviation = result.standardDeviation;
187-
return new TableResultValueEntry(f.name, mean, standardDeviation, factor);
212+
213+
let statisticalResult = undefined;
214+
let statisticalCol = undefined;
215+
// X1,..,Xn: this Framework, Y1, ..., Yn: selected Framework
216+
// https://de.wikipedia.org/wiki/Zweistichproben-t-Test
217+
if (compareWithResults) {
218+
let n = result.count || 20;
219+
let m = result.count || 20;
220+
let s2 = (n-1)*Math.pow(result.standardDeviation,2) + (m-1)*Math.pow(compareWithResults.standardDeviation,2) / (n+m-2)
221+
let s = Math.sqrt(s2);
222+
let t = Math.sqrt((n * m) / (n + m))*(result.mean - compareWithResults.mean - 0)/s;
223+
let talpha = jStat.studentt.inv( 0.975, n + m - 2);
224+
statisticalResult = t.toFixed(3) + ": " +talpha.toFixed(3);
225+
let p = jStat.studentt.cdf( Math.abs(t), n + m -2 );
226+
statisticalCol = statisticComputeColor(t, p);
227+
statisticalResult += ": " + p.toFixed(3) + ";" + statisticalCol;
228+
if (result.mean < compareWithResults.mean) {
229+
// H0 X >= Y, H1 X < Y
230+
if (t < -talpha) {
231+
statisticalResult += " faster";
232+
}
233+
} else {
234+
// H0 X <= Y, H1 X > Y
235+
if (t > talpha) {
236+
statisticalResult += " slower";
237+
}
238+
}
239+
}
240+
return new TableResultValueEntry(f.name, mean, standardDeviation, factor, statisticalResult, statisticalCol);
188241
}
189242
});
190243
}

webdriver-ts-results/src/SelectBar.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export interface Props {
1919
frameworksNonKeyed: Array<Framework>;
2020
benchmarksCPU: Array<Benchmark>;
2121
benchmarksMEM: Array<Benchmark>;
22+
compareWith: Framework | undefined;
23+
selectComparison: (framework: string) => void;
2224
}
2325

2426
export class SelectBar extends React.Component<Props, void> {
@@ -34,7 +36,10 @@ export class SelectBar extends React.Component<Props, void> {
3436
frameworksKeyed,
3537
frameworksNonKeyed,
3638
benchmarksCPU,
37-
benchmarksMEM} = this.props;
39+
benchmarksMEM,
40+
compareWith,
41+
selectComparison
42+
} = this.props;
3843
return (
3944
<div>
4045
<DropDown label="Which frameworks?" width='1024px'>
@@ -73,6 +78,29 @@ export class SelectBar extends React.Component<Props, void> {
7378
Separate keyed and non-keyed
7479
</label>
7580
</div>
81+
<div className="hspan"/>
82+
<form className="form-inline" style={{display:"inline-block"}}>
83+
<div className="form-group">
84+
<div className="hspan"/>
85+
<select className="form-control" value={compareWith ? compareWith.name : ''} onChange={(evt) => selectComparison(evt.target.value)}>
86+
<option value=''>Compare with ...</option>
87+
<optgroup label="Keyed">
88+
{
89+
frameworksKeyed.map(f =>
90+
<option key={f.name} value={f.name}>{f.name}</option>
91+
)
92+
}
93+
</optgroup>
94+
<optgroup label="Non-keyed">
95+
{
96+
frameworksNonKeyed.map(f =>
97+
<option key={f.name} value={f.name}>{f.name}</option>
98+
)
99+
}
100+
</optgroup>
101+
</select>
102+
</div>
103+
</form>
76104
</div>
77105
);
78106
}

0 commit comments

Comments
 (0)