Skip to content

Commit 367ce26

Browse files
committed
Refactor to use matrix for absolute differences
This fixes a bug in the calculation of the test statistic for non-square matrices due to index mismatches.
1 parent c38feec commit 367ce26

6 files changed

Lines changed: 90 additions & 11 deletions

File tree

lib/node_modules/@stdlib/stats/chi2test/lib/absdiff.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
// MODULES //
2222

2323
var Float64Array = require( '@stdlib/array/float64' );
24+
var array = require( '@stdlib/ndarray/array' );
2425
var abs = require( '@stdlib/math/base/special/abs' );
2526

2627

@@ -31,21 +32,25 @@ var abs = require( '@stdlib/math/base/special/abs' );
3132
*
3233
* @param {Matrix} x - first input matrix
3334
* @param {Matrix} y - second input matrix
34-
* @returns {Float64Array} output array
35+
* @returns {Matrix} matrix containing the absolute differences
3536
*/
3637
function absdiff( x, y ) {
3738
var out;
3839
var i;
3940
var j;
4041
var M;
4142
var N;
43+
var v;
4244

43-
out = new Float64Array( x.length );
4445
M = x.shape[ 0 ];
4546
N = x.shape[ 1 ];
47+
out = array( new Float64Array( M * N ), {
48+
'shape': [ M, N ]
49+
});
4650
for ( i = 0; i < M; i++ ) {
4751
for ( j = 0; j < N; j++ ) {
48-
out[ ( i*M ) + j ] = abs( x.get( i, j ) - y.get( i, j ) );
52+
v = abs( x.get( i, j ) - y.get( i, j ) );
53+
out.set( i, j, v );
4954
}
5055
}
5156
return out;

lib/node_modules/@stdlib/stats/chi2test/lib/main.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
* limitations under the License.
1717
*/
1818

19+
/* eslint-disable no-underscore-dangle */
20+
1921
'use strict';
2022

2123
// MODULES //
@@ -73,7 +75,9 @@ function chi2test( x, options ) {
7375
var err;
7476
var out;
7577
var N;
78+
var e;
7679
var i;
80+
var j;
7781

7882
if ( isArrayArray( x ) ) {
7983
x = array( x );
@@ -106,19 +110,21 @@ function chi2test( x, options ) {
106110
if ( opts.correct && nrow === 2 && ncol === 2 ) {
107111
// Apply Yates' continuity correction:
108112
minAbs = incrmin();
109-
for ( i = 0; i < absDiff.length; i++ ) {
110-
minAbs( absDiff[ i ] );
113+
for ( i = 0; i < absDiff._buffer.length; i++ ) {
114+
minAbs( absDiff._buffer[ i ] );
111115
}
112116
yates = min( 0.5, minAbs() );
113-
for ( i = 0; i < absDiff.length; i++ ) {
114-
absDiff[ i ] -= yates;
117+
for ( i = 0; i < absDiff._buffer.length; i++ ) {
118+
absDiff._buffer[ i ] -= yates;
115119
}
116120
}
117-
for ( i = 0; i < absDiff.length; i++ ) {
118-
absDiff[ i ] *= absDiff[ i ];
119-
absDiff[ i ] /= means.data[ i ];
121+
for ( i = 0; i < nrow; i++) {
122+
for ( j = 0; j < ncol; j++ ) {
123+
e = absDiff.get( i, j ) * absDiff.get( i, j ) / means.get( i, j );
124+
absDiff.set( i, j, e );
125+
}
120126
}
121-
stat = gsum( absDiff.length, absDiff, 1 );
127+
stat = gsum( absDiff.length, absDiff._buffer, 1 );
122128
param = ( nrow - 1 ) * ( ncol - 1 );
123129
pval = 1 - chisqCDF( stat, param );
124130

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"x":[[15,30,5],[20,35,15]],"pValue":0.241160644293652,"statistic":2.84458398744113}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"x":[[15,30,15],[20,35,15],[30,45,25],[35,50,30]],"pValue":0.961583207657537,"statistic":1.46813287815126}

lib/node_modules/@stdlib/stats/chi2test/test/fixtures/r/runner.R

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,20 @@ main <- function() {
9090
uncorrected <- chisq.test( x = x, correct = FALSE );
9191
uncorrected <- list( x = x, pValue = uncorrected$p.value, statistic = uncorrected$statistic );
9292

93+
x <- matrix( c( 15, 30, 5, 20, 35, 15 ), ncol = 3, byrow = TRUE );
94+
more_cols <- chisq.test( x = x );
95+
more_cols <- list( x = x, pValue = more_cols$p.value, statistic = more_cols$statistic );
96+
97+
x <- matrix( c( 15, 30, 15, 20, 35, 15, 30, 45, 25, 35, 50, 30 ), ncol = 3, byrow = TRUE );
98+
more_rows <- chisq.test( x = x );
99+
more_rows <- list( x = x, pValue = more_rows$p.value, statistic = more_rows$statistic );
100+
93101
# Convert fixture data to JSON:
94102
counts <- to_json( counts );
95103
corrected <- to_json( corrected );
96104
uncorrected <- to_json( uncorrected );
105+
more_cols <- to_json( more_cols );
106+
more_rows <- to_json( more_rows );
97107

98108
# Write the data to file...
99109
filepath <- get_filepath( "counts.json" );
@@ -104,6 +114,12 @@ main <- function() {
104114

105115
filepath <- get_filepath( "uncorrected.json" );
106116
write( uncorrected, filepath );
117+
118+
filepath <- get_filepath( "more_rows.json" );
119+
write( more_rows, filepath );
120+
121+
filepath <- get_filepath( "more_cols.json" );
122+
write( more_cols, filepath );
107123
}
108124

109125
main();

lib/node_modules/@stdlib/stats/chi2test/test/test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ var chi2test = require( './../lib' );
3333
var counts = require( './fixtures/r/counts.json' );
3434
var corrected = require( './fixtures/r/corrected.json' );
3535
var uncorrected = require( './fixtures/r/uncorrected.json' );
36+
var moreCols = require( './fixtures/r/more_cols.json' );
37+
var moreRows = require( './fixtures/r/more_rows.json' );
3638

3739

3840
// TESTS //
@@ -237,6 +239,54 @@ tape( 'the function correctly computes the chi-square independence test for a tw
237239
t.end();
238240
});
239241

242+
tape( 'the function correctly computes the chi-square independence test for a two-way contingency table with more columns than rows', function test( t ) {
243+
var expected;
244+
var delta;
245+
var tol;
246+
var out;
247+
var x;
248+
249+
x = array( moreCols.x );
250+
out = chi2test( x );
251+
252+
// Tested against R:
253+
expected = moreCols.pValue;
254+
delta = abs( out.pValue - expected );
255+
tol = 8.0 * EPS * abs( expected );
256+
t.ok( delta <= tol, 'within tolerance. x: '+x+'. pValue: '+out.pValue+'. E: '+expected+'. Δ: '+delta+'. tol: '+tol );
257+
258+
expected = moreCols.statistic;
259+
delta = abs( out.statistic - expected );
260+
tol = 2.0 * EPS * abs( expected );
261+
t.ok( delta <= tol, 'within tolerance. x: '+x+'. statistic: '+out.statistic+'. E: '+expected+'. Δ: '+delta+'. tol: '+tol );
262+
263+
t.end();
264+
});
265+
266+
tape( 'the function correctly computes the chi-square independence test for a two-way contingency table with more rows than columns', function test( t ) {
267+
var expected;
268+
var delta;
269+
var tol;
270+
var out;
271+
var x;
272+
273+
x = array( moreRows.x );
274+
out = chi2test( x );
275+
276+
// Tested against R:
277+
expected = moreRows.pValue;
278+
delta = abs( out.pValue - expected );
279+
tol = 2.0 * EPS * abs( expected );
280+
t.ok( delta <= tol, 'within tolerance. x: '+x+'. pValue: '+out.pValue+'. E: '+expected+'. Δ: '+delta+'. tol: '+tol );
281+
282+
expected = moreRows.statistic;
283+
delta = abs( out.statistic - expected );
284+
tol = 2.0 * EPS * abs( expected );
285+
t.ok( delta <= tol, 'within tolerance. x: '+x+'. statistic: '+out.statistic+'. E: '+expected+'. Δ: '+delta+'. tol: '+tol );
286+
287+
t.end();
288+
});
289+
240290
tape( 'the function returns the correct decision giving different values of `alpha`', function test( t ) {
241291
var out;
242292
var x;

0 commit comments

Comments
 (0)