├── tests ├── __init__.py ├── __pycache__ │ ├── .DS_Store │ └── test_ica_pytest.cpython-27-PYTEST.pyc └── testing_pytest.py ├── MANIFEST.in ├── __init__.py ├── README ├── setup.py ├── LICENSE ├── sources.py └── fastica.py /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README LICENSE 2 | 3 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['fastica','sources'] 2 | -------------------------------------------------------------------------------- /tests/__pycache__/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelahunginjeet/pyica/HEAD/tests/__pycache__/.DS_Store -------------------------------------------------------------------------------- /tests/__pycache__/test_ica_pytest.cpython-27-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelahunginjeet/pyica/HEAD/tests/__pycache__/test_ica_pytest.cpython-27-PYTEST.pyc -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Author : Kevin S. Brown (kevin.s.brown@uconn.edu, github.com/thelahunginjeet) 2 | 3 | A pure python package for Independent Component Analysis (ICA). Currently, only fixed-point FastICA is supported. 4 | 5 | See the documentation in the modules for detailed usage and function arguments. Assuming you've cloned into a location python knows about and you want to extract 6 | n <= N sources from a data matrix X of size N x T, you can do 7 | 8 | import pyica.fastica as ica 9 | A,W,S = ica.fastica(X,n) 10 | 11 | All source code is made available under the BSD-3 license. 12 | 13 | There is currently minimal testing code in the tests/ directory. There are two ways to run the tests. If you have pytest, run: 14 | 15 | >py.test -v testing_pytest.py 16 | 17 | If you don't have pytest, you can run the test via distutils: 18 | 19 | >python setup.py test 20 | -------------------------------------------------------------------------------- /tests/testing_pytest.py: -------------------------------------------------------------------------------- 1 | from pyica import fastica 2 | from pyica import sources 3 | import numpy as np 4 | 5 | 6 | class TestICA(): 7 | 8 | def setup(self): 9 | self.signals = np.vstack([np.sin([x/20.0 for x in range(1,1001)]),(1.0 + np.mod(range(1000),200) - 100.0)/100.0]) 10 | self.mixing = np.array([[0.291, 0.6557], [-0.5439, 0.5572]]) 11 | self.X = np.dot(self.mixing,self.signals) 12 | self.A,self.W,self.S = fastica.fastica(self.X,2,maxIterations=10000) 13 | 14 | def test_W_orthogonality(self): 15 | assert np.allclose(np.dot(self.W.T,self.W),np.eye(2),atol=1.0e-06),"python: W^TW not within 1e-06 of I" 16 | 17 | def test_S_recovery(self): 18 | from scipy.linalg import det 19 | assert np.allclose(1.0,np.abs(det(np.corrcoef(self.S,self.signals)[0:2,2:])),atol=1.0e-03),"python: |det(rho(ShatT,S))| not within 1e-03 of unity" 20 | 21 | 22 | class TestSources(): 23 | 24 | def setup(self): 25 | self.S = sources.unitsources() 26 | 27 | def test_S_size(self): 28 | assert self.S.shape[0] == 3,"sources: incorrect number of test sources generated" 29 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup,Command 4 | 5 | class PyTest(Command): 6 | user_options = [] 7 | def initialize_options(self): 8 | pass 9 | def finalize_options(self): 10 | pass 11 | def run(self): 12 | import sys,subprocess 13 | errno = subprocess.call([sys.executable,'tests/runtests.py']) 14 | raise SystemExit(errno) 15 | 16 | setup(name='pyica', 17 | version='1.1.0', 18 | description='Pure Python Package for FastICA', 19 | author='Kevin Brown', 20 | author_email='kevin.s.brown@uconn.edu', 21 | url='https://github.com/thelahunginjeet/pyica', 22 | packages=['pyica'], 23 | package_dir = {'pyica': ''}, 24 | package_data = {'pyica' : ['tests/*.py']}, 25 | cmdclass = {'test': PyTest}, 26 | license='BSD-3', 27 | classifiers=[ 28 | 'License :: OSI Approved :: BSD-3 License', 29 | 'Intended Audience :: Developers', 30 | 'Intended Audience :: Scientists', 31 | 'Programming Language :: Python', 32 | 'Topic :: Blind Source Separation', 33 | 'Topic :: Signal Processing', 34 | ], 35 | ) 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | @author: Kevin S. Brown, University of Connecticut 2 | 3 | This source code is provided under the BSD-3 license, duplicated as follows: 4 | 5 | Copyright (c) 2013, Kevin S. Brown 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, this 15 | list of conditions and the following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | 3. Neither the name of the University of Connecticut nor the names of its contributors 19 | may be used to endorse or promote products derived from this software without specific 20 | prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 23 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 24 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 25 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 28 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 29 | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | 32 | I am indebted to the FastICA code written by Pierre Lafaye de Micheaux, Stefan van der Walt, 33 | and Gael Varoquaux. (Their original code had a "do whatever you want with this" license). 34 | I humbly suggest you use this code rather than theirs (if you can find theirs); I believe mine 35 | to be actively maintained and more extensively tested. 36 | -------------------------------------------------------------------------------- /sources.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Pure python FastICA code; both deflation and parallel extraction are implemented. Currently 3 | only fixed-point extraction (no gradient calculations). 4 | 5 | Created on Mar 2, 2011 6 | 7 | @author: Kevin S. Brown, University of Connecticut 8 | 9 | This source code is provided under the BSD-3 license, duplicated as follows: 10 | 11 | Copyright (c) 2013, Kevin S. Brown 12 | All rights reserved. 13 | 14 | Redistribution and use in source and binary forms, with or without modification, 15 | are permitted provided that the following conditions are met: 16 | 17 | 1. Redistributions of source code must retain the above copyright notice, this 18 | list of conditions and the following disclaimer. 19 | 20 | 2. Redistributions in binary form must reproduce the above copyright notice, this 21 | list of conditions and the following disclaimer in the documentation and/or other 22 | materials provided with the distribution. 23 | 24 | 3. Neither the name of the University of Connecticut nor the names of its contributors 25 | may be used to endorse or promote products derived from this software without specific 26 | prior written permission. 27 | 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 29 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 30 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 31 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 34 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 35 | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | ''' 37 | 38 | 39 | from numpy.random import randn,rand,randint,gamma,laplace 40 | from numpy import pi 41 | from numpy import tan,arctanh,sinh,arcsinh,power,sqrt,log 42 | from numpy import shape,newaxis,vstack 43 | 44 | 45 | def unitsources(nSources=(1,1,1),nSamples=1024,subType='dblcosh',supType='invcosh'): 46 | ''' 47 | Generates a sum(nSources) x nSamples array of sources; each row is a source, and they all have 48 | zero mean and unit variance (up to sampling errors). 49 | 50 | Parameters: 51 | ---------- 52 | nSources : tuple, optional 53 | nSources[0] : number of SubGaussian sources (zero is allowed) 54 | nSources[1] : number of SuperGaussian sources (zero is allowed) 55 | nSources[2] : number of Gaussian sources (zero is allowed) 56 | 57 | nSamples : number of samples (length of each row), optional 58 | 59 | subType : string, optional 60 | type of subgaussian distribution from which to sample; unrecognized 61 | distribution types default to 'dblcosh' 62 | 63 | supType : string, optional 64 | type of supergaussian distribution from which to sample; unrecognized 65 | distribution types default to 'invcosh' 66 | 67 | 68 | Output: 69 | ---------- 70 | S : numpy array 71 | first nSources[0] rows are SubGaussian (starting at 0) 72 | next nSources[1] rows are SuperGaussian 73 | next nSources[2] rows are Gaussian 74 | 75 | ''' 76 | # for function dispatching 77 | superGaussTable = {'invcosh': sinvcosh, 'laplace': slaplace, 'logistic': slogistic, 78 | 'exparcsinh': sexparcsinh} 79 | subGaussTable = {'dblcosh': sdblcosh, 'expsinh': sexpsinh, 'gg' : sgg} 80 | sourceList = [] 81 | # subgaussians 82 | try: 83 | s = subGaussTable[subType](nSources[0],nSamples) 84 | except KeyError: 85 | # default to dblcosh 86 | s = sdblcosh(nSources[0],nSamples) 87 | sourceList.append(s) 88 | # supergaussians 89 | try: 90 | s = superGaussTable[supType](nSources[1],nSamples) 91 | except KeyError: 92 | # default to invcosh 93 | s = sinvcosh(nSources[1],nSamples) 94 | sourceList.append(s) 95 | # gaussians 96 | sourceList.append(sgauss(nSources[2],nSamples)) 97 | 98 | return vstack([x for x in sourceList if len(x) > 0]) 99 | 100 | 101 | 102 | """ Gaussian distribution """ 103 | 104 | def sgauss(nSources,nSamples): 105 | ''' 106 | Generates an nSources x nSamples array of Gaussian sources with mean zero and unit variance. 107 | Simply wraps the appropriate function from numpy.rand. 108 | ''' 109 | return randn(nSources,nSamples) 110 | 111 | 112 | """ SuperGaussian distributions """ 113 | 114 | def sinvcosh(nSources,nSamples): 115 | ''' 116 | Generates an nSources x nSamples array of sources distributed according to: 117 | 118 | p(x) = (2*cosh(pi*x/2))^-1 119 | 120 | This yields a set of superGaussian sources (more peaked than Gaussian), and 121 | each source will have zero mean and unit variance. 122 | ''' 123 | return (4/pi)*arctanh(tan((pi/2)*(rand(nSources,nSamples) - 0.5))) 124 | 125 | 126 | def slaplace(nSources,nSamples): 127 | ''' 128 | Generates an nSources x nSamples array of sources which are Laplace distributed. 129 | 130 | p(x) = (1/sqrt(2))*exp(-sqrt(2)*x) 131 | ''' 132 | s = laplace(size=(nSources,nSamples)) 133 | return s/s.std(axis=1)[:,newaxis] 134 | 135 | 136 | def slogistic(nSources,nSamples): 137 | ''' 138 | Generates an nSources x nSamples array of logistically distributed random 139 | variates with mean zero and unit variance: 140 | 141 | p(x) = pi*sech^2(pi*x/(2*sqrt(3)))/(4*sqrt(3)) 142 | 143 | ''' 144 | return -(sqrt(3)/pi)*log(1.0/rand(nSources,nSamples) - 1.0) 145 | 146 | 147 | def sexparcsinh(nSources,nSamples): 148 | ''' 149 | Generates and nSources by nSamples array of sources distributed according to: 150 | 151 | p(x) = (1/sqrt(2*pi*a^2*(1+x/a)^2))*exp(-(arcsinh(y/a)^2)/2) 152 | 153 | This yields a set of superGaussian sources; each source is standardized to 154 | have unit variance (p(x) above does not). 155 | ''' 156 | s = sinh(randn(nSources,nSamples)) 157 | return s/s.std(axis=1)[:,newaxis] 158 | 159 | 160 | """ SubGaussian distributions """ 161 | 162 | def sdblcosh(nSources,nSamples): 163 | ''' 164 | Generates an nSources x nSamples array of sources distributed according to: 165 | 166 | p(x) = exp(-x^2)*cosh(x*sqrt(2))/sqrt(pi*e) 167 | 168 | This yields a set of subGaussian sources (flatter top that Gaussian), and 169 | each source will have zero mean and unit variance. 170 | ''' 171 | return (.5**.5)*(randn(nSources,nSamples) + randspin(size=(nSources,nSamples))) 172 | 173 | 174 | def sexpsinh(nSources,nSamples): 175 | ''' 176 | Generates an nSources x nSamples set of sources distributed according to: 177 | 178 | p(x) = sqrt((1 + sinh(x)^2)/(2*pi))*exp(-sinh^2(x)/2) 179 | 180 | Each source is standardize to have unit variance. 181 | ''' 182 | s = arcsinh(randn(nSources,nSamples)) 183 | return s/s.std(axis=1)[:,newaxis] 184 | 185 | 186 | def sgg(nSources,nSamples): 187 | ''' 188 | Generates an nSources x nSamples set of sources distributed according to: 189 | 190 | p(x) = (8/(2*Gamma[1/8]))*exp(-x^8), 191 | 192 | i.e. a generalized Gaussian distribution with shape parameter = 8. 193 | ''' 194 | gg = randspin(size=(nSources,nSamples))*power(abs(gamma(shape=1.0/8.0,scale=1.0,size=(nSources,nSamples))),1.0/8.0) 195 | return gg/gg.std(axis=1)[:,newaxis] 196 | 197 | 198 | """ Accessory functions """ 199 | 200 | def randbit(size=None): 201 | ''' 202 | Generates an array of shape size of random {0,1} bits. 203 | ''' 204 | if size is None: 205 | return randint(2) 206 | else: 207 | return randint(2,size=size) 208 | 209 | def randspin(size=None): 210 | ''' 211 | Generates an array of shape size of random {-1,1} spin variables. 212 | ''' 213 | return 2*randbit(size=size) - 1; 214 | -------------------------------------------------------------------------------- /fastica.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Pure python FastICA code; both deflation and parallel extraction are implemented. Currently 3 | only fixed-point extraction (no gradient calculations). 4 | 5 | Created on Mar 2, 2011 6 | 7 | @author: Kevin S. Brown, University of Connecticut 8 | 9 | This source code is provided under the BSD-3 license, duplicated as follows: 10 | 11 | Copyright (c) 2013, Kevin S. Brown 12 | All rights reserved. 13 | 14 | Redistribution and use in source and binary forms, with or without modification, 15 | are permitted provided that the following conditions are met: 16 | 17 | 1. Redistributions of source code must retain the above copyright notice, this 18 | list of conditions and the following disclaimer. 19 | 20 | 2. Redistributions in binary form must reproduce the above copyright notice, this 21 | list of conditions and the following disclaimer in the documentation and/or other 22 | materials provided with the distribution. 23 | 24 | 3. Neither the name of the University of Connecticut nor the names of its contributors 25 | may be used to endorse or promote products derived from this software without specific 26 | prior written permission. 27 | 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 29 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 30 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 31 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 34 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 35 | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | 37 | I am indebted to the FastICA code written by Pierre Lafaye de Micheaux, Stefan van der Walt, 38 | and Gael Varoquaux. (Their original code had a "do whatever you want with this" license). 39 | I humbly suggest you use this code rather than theirs (if you can find theirs); I believe mine 40 | to be actively maintained and more extensively tested. 41 | ''' 42 | 43 | 44 | from numpy import tanh,exp,sqrt 45 | from numpy import max as npmax 46 | from numpy import abs as npabs 47 | from numpy import dot,diag,newaxis,zeros,array 48 | from numpy.random import randn 49 | from scipy.linalg import svd,qr,pinv2 50 | 51 | 52 | def lc(x,alpha=1.0): 53 | """ 54 | Tanh nonlinearity (tanh(alpha*x)). 55 | """ 56 | return tanh(alpha*x) 57 | 58 | def lcp(x,alpha=1.0): 59 | """ 60 | Derivative of tanh nonlinearity (see lc(x,alpha)). 61 | """ 62 | return alpha*(1.0 - tanh(alpha*x)**2) 63 | 64 | def gauss(x,alpha=1.0): 65 | """ 66 | Gaussian nonlinearity. 67 | """ 68 | return x*exp(-(alpha*x**2)/2.0) 69 | 70 | def gaussp(x,alpha=1.0): 71 | """ 72 | Derivative of gaussian nonlinearity (see gauss(x,alpha)). 73 | """ 74 | return (1.0 - alpha*x**2)*exp(-(alpha*x**2)/2.0) 75 | 76 | def cube(x,alpha=1.0): 77 | """ 78 | Cubic nonlinearity. 79 | """ 80 | return x**3 81 | 82 | def cubep(x,alpha=0.0): 83 | """ 84 | Derivative of cubic nonlinearity (see cube(x,alpha)). 85 | """ 86 | return 3*x**2 87 | 88 | def skew(x,alpha=0.0): 89 | """ 90 | Skew (quadratic) nonlinearity. 91 | """ 92 | return x**2 93 | 94 | def skewp(x,alpha=0.0): 95 | """ 96 | Derivative of skew(quadratic) nonlinearity (see skew(x,alpha)). 97 | """ 98 | return 2*x 99 | 100 | 101 | """Preprocessing functions.""" 102 | def rowcenter(X): 103 | """ 104 | Removes the mean of each row of matrix X, returning those means and the 105 | centered matrix. 106 | """ 107 | rowmeans = X.mean(axis=-1) 108 | return rowmeans, X - rowmeans[:, newaxis] 109 | 110 | 111 | def whiteningmatrix(X,n): 112 | """Performs dimensionality reduction (via PCA) and returns the whitening/dewhitening 113 | matrices for the data in matrix X. 114 | 115 | Whitening matrix will be of size n x X.shape[0] 116 | Dewhitening matrix will be of size X.shape[0] x n 117 | 118 | INPUT: 119 | X : numpy array of size N x p 120 | n : resulting dimension of whitened matrix (n <= N) 121 | 122 | Usage : Kw,Kd = whiteningmatrix(X,n) 123 | """ 124 | if(n > X.shape[0]): 125 | n = X.shape[0] 126 | U,D,Vt = svd(dot(X,X.T)/X.shape[1],full_matrices=False) 127 | return dot(diag(1.0/sqrt(D[0:n])),U[:,0:n].T),dot(U[:,0:n],diag(sqrt(D[0:n]))) 128 | 129 | 130 | """Decorrelation methods.""" 131 | def decorrelation_gs(w,W,p): 132 | """ 133 | Gram-schmidt orthogonalization of w against the first p rows of W. 134 | """ 135 | w = w - (W[0:p,:]*dot(W[0:p,:],w.T)[:,newaxis]).sum(axis=0) 136 | w = w/sqrt(dot(w,w)) 137 | return w 138 | 139 | def decorrelation_witer(W): 140 | """ 141 | Iterative MDUM decorrelation that avoids matrix inversion. 142 | """ 143 | lim = 1.0 144 | tol = 1.0e-05 145 | W = W/(W**2).sum() 146 | while lim > tol: 147 | W1 = (3.0/2.0)*W - 0.5*dot(dot(W,W.T),W) 148 | lim = npmax(npabs(npabs(diag(dot(W1,W.T))) - 1.0)) 149 | W = W1 150 | return W 151 | 152 | def decorrelation_mdum(W): 153 | """ 154 | Minimum distance unitary mapping decorrelation. 155 | """ 156 | U,D,VT = svd(W) 157 | Y = dot(dot(U,diag(1.0/D)),U.T) 158 | return dot(Y,W) 159 | 160 | 161 | """FastICA algorithms.""" 162 | def ica_def(X, tolerance, g, gprime, orthog, alpha, maxIterations, Winit): 163 | """Deflationary FastICA using Gram-Schmidt decorrelation at each step. This 164 | function is not meant to be directly called; it is wrapped by fastica().""" 165 | n,p = X.shape 166 | W = Winit 167 | # j is the index of the extracted component 168 | for j in xrange(n): 169 | w = Winit[j, :] 170 | it = 1 171 | lim = tolerance + 1 172 | while ((lim > tolerance) & (it < maxIterations)): 173 | wtx = dot(w, X) 174 | gwtx = g(wtx, alpha) 175 | g_wtx = gprime(wtx, alpha) 176 | w1 = (X * gwtx).mean(axis=1) - g_wtx.mean() * w 177 | w1 = decorrelation_gs(w1, W, j) 178 | lim = npabs(npabs((w1 * w).sum()) - 1.0) 179 | w = w1 180 | it = it + 1 181 | W[j, :] = w 182 | return W 183 | 184 | def ica_par_fp(X, tolerance, g, gprime, orthog, alpha, maxIterations, Winit): 185 | """Parallel FastICA; orthog sets the method of unmixing vector decorrelation. This 186 | fucntion is not meant to be directly called; it is wrapped by fastica().""" 187 | n,p = X.shape 188 | W = orthog(Winit) 189 | lim = tolerance + 1 190 | it = 1 191 | while ((lim > tolerance) and (it < maxIterations)): 192 | wtx = dot(W,X) 193 | gwtx = g(wtx,alpha) 194 | g_wtx = gprime(wtx,alpha) 195 | W1 = dot(gwtx,X.T)/p - dot(diag(g_wtx.mean(axis=1)),W) 196 | W1 = orthog(W1) 197 | lim = npmax(npabs(npabs(diag(dot(W1,W.T))) - 1.0)) 198 | W = W1 199 | it = it + 1 200 | return W 201 | 202 | 203 | 204 | def fastica(X, nSources=None, algorithm="parallel fp", decorrelation="mdum", nonlinearity="logcosh", alpha=1.0, maxIterations=500, tolerance=1e-05, Winit=None, scaled=True): 205 | """Perform FastICA on data matrix X. All algorithms currently implemented are fixed point iteration 206 | (as opposed to gradient descent) methods. For default usage (good for many applications), simply call: 207 | 208 | A,W,S = fastica(X,nSources) 209 | 210 | Parameters: 211 | ------------ 212 | X: numpy array, size n x p 213 | array of data to unmix - n variables, p observations 214 | 215 | nSources: int, optional 216 | number of sources to estimate. if nSources < n, dimensionality reduction (via pca) 217 | is performed. If nSources = None, full-rank (nSources = n) decomposition is performed. 218 | 219 | algorithm: string, optional 220 | 'deflation' : use deflational (one-at-a-time) component extraction 221 | 'parallel fp' : parallel extraction, fixed point iteration 222 | 223 | decorrelation: string, optional 224 | decorrelation method for parallel extraction. (Not adjustable for deflationary ICA.) 225 | 'mdum' : minimum distance unitary mapping 226 | 'witer' : like mdum, but without matrix inversion 227 | 228 | nonlinearity: string,optional 229 | function used in the approximate negentropy. Should be one of: 230 | 'logcosh' : G(u) = (1/a) log[cosh[a*u]] 231 | 'exp' : G(u) = -(1/a) exp(-a*u^2/2) 232 | 'skew' : G(u) = u^3/3 233 | 'kurtosis' : G(u) = u^4/4 234 | 235 | alpha : float,optional 236 | parameter for 'logcosh' and 'exp' nonlinearities. Should be in [1,2] 237 | 238 | maxIterations: int, optional 239 | maximum number of iterations 240 | 241 | tolerance: float, optional 242 | tolerance at which the unmixing matrix is considered to have converged 243 | 244 | Winit: numpy array, size nSources x n 245 | initial guess for the unmixing matrix 246 | 247 | scaled : bool, optional 248 | if scaled == True, the output sources are rescaled to have unit standard deviation 249 | and the data units will be in the mixing matrix 250 | 251 | Output: 252 | ----------- 253 | A : (n x nSources) 254 | mixing matrix (X = A*S) 255 | 256 | W : (nSources x n) 257 | unmixing matrix (W*X = S) 258 | 259 | S : (nSources x p) 260 | matrix of independent sources 261 | 262 | """ 263 | algorithm_funcs = {'parallel fp':ica_par_fp, 'deflation':ica_def} 264 | orthog_funcs = {'mdum': decorrelation_mdum, 'witer': decorrelation_witer} 265 | 266 | if (alpha < 1) or (alpha > 2): 267 | raise ValueError("alpha must be in [1,2]") 268 | 269 | if nonlinearity == 'logcosh': 270 | g = lc 271 | gprime = lcp 272 | elif nonlinearity == 'exp': 273 | g = gauss 274 | gprime = gaussp 275 | elif nonlinearity == 'skew': 276 | g = skew 277 | gprime = skewp 278 | else: 279 | g = cube 280 | gprime = cubep 281 | 282 | nmix,nsamp = X.shape 283 | 284 | # default is to do full-rank decomposition 285 | if nSources is None: 286 | nSources = nmix 287 | if Winit is None: 288 | # start with a random orthogonal decomposition 289 | Winit = randn(nSources,nSources) 290 | 291 | # preprocessing (centering/whitening/pca) 292 | rowmeansX,X = rowcenter(X) 293 | Kw,Kd = whiteningmatrix(X,nSources) 294 | X = dot(Kw,X) 295 | 296 | # pass through kwargs 297 | kwargs = {'tolerance': tolerance,'g': g,'gprime': gprime,'orthog':orthog_funcs[decorrelation],'alpha': alpha,'maxIterations': maxIterations,'Winit': Winit} 298 | func = algorithm_funcs[algorithm] 299 | 300 | # run ICA 301 | W = func(X, **kwargs) 302 | 303 | # consruct the sources - means are not restored 304 | S = dot(W,X) 305 | 306 | # mixing matrix 307 | A = pinv2(dot(W,Kw)) 308 | 309 | if scaled == True: 310 | S = S/S.std(axis=-1)[:,newaxis] 311 | A = A*S.std(axis=-1)[newaxis,:] 312 | 313 | return A,W,S 314 | 315 | 316 | --------------------------------------------------------------------------------