diff --git a/talks/matlab_vs_python/migp/demo_MIGP_NIFTI.m b/talks/matlab_vs_python/migp/demo_MIGP_NIFTI.m
new file mode 100644
index 0000000000000000000000000000000000000000..81fdfe00fa0a86a1bb2e3549c3c3de20217638af
--- /dev/null
+++ b/talks/matlab_vs_python/migp/demo_MIGP_NIFTI.m
@@ -0,0 +1,63 @@
+
+
+% MIGP - MELODIC's Incremental Group-PCA
+% Steve Smith, FMRIB, 2012-2013
+% not yet finally tweaked/evaluated, or published - please do not pass on.
+% adapted by Sean Fitz, 2020
+
+%%%%%%%%%%%%%%%%%%%%%%%%%% USER OPTIONS
+
+INlist=dir('data/cobre/fmri_*.nii.gz');  % list of input 4D NIFTI standard space datasets
+INmask='data/brain_mask.nii.gz';       % 3D NIFTI mask image
+GO='matMIGP';           % output filename
+dPCAint=550;           % internal number of components - typically 2-4 times number of timepoints in each run (if you have enough RAM for that)
+dPCAout=100;           % number of eigenvectors to output - should be less than dPCAint and more than the final ICA dimensionality
+
+%%%%%%%%%%%%%%%%%%%%%%%%%% END OF USER OPTIONS
+
+Nsub=length(INlist); [~,r]=sort(rand(Nsub,1));  % will process subjects in random order
+mask=read_avw(INmask); 
+masksize=size(mask); 
+mask=reshape(mask,prod(masksize),1);
+
+demean = @(x) x - repmat(mean(x,1),[size(x,1), 1, 1]);
+ss_svds = @(x,n) svds(x, n);
+
+for i = 1:Nsub
+
+    % read data
+    filename=[INlist(r(i)).folder, '/', INlist(r(i)).name];
+    grot=double(read_avw(filename)); 
+    grot=reshape(grot,prod(masksize),size(grot,4)); 
+    grot=demean(grot(mask~=0,:)');
+    
+    % var-norm
+    [uu,ss,vv]=ss_svds(grot,30);
+    vv(abs(vv)<2.3*std(vv(:)))=0;
+    stddevs=max(std(grot-uu*ss*vv'),0.001);
+    grot=grot./repmat(stddevs,size(grot,1),1);
+    
+    
+    if (i==1)
+        W=demean(grot); clear grot;
+    else
+        
+        % concat
+        W=[W; demean(grot)]; clear grot;
+        
+        % reduce W to dPCAint eigenvectors
+        if size(W,1)-10 > dPCAint
+            [uu,dd]=eigs(W*W',dPCAint);  
+            W=uu'*W; 
+            clear uu;
+        end
+
+    end
+    
+end
+
+grot=zeros(prod(masksize),dPCAout); 
+grot(mask~=0,:)=W(1:dPCAout,:)'; grot=reshape(grot,[masksize ,dPCAout]);
+save_avw(grot,GO,'f',[1 1 1 1]);
+system(sprintf('fslcpgeom %s %s -d',filename,GO));
+
diff --git a/talks/matlab_vs_python/migp/matlab_MIGP.ipynb b/talks/matlab_vs_python/migp/matlab_MIGP.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..830c173746af46ee6a603d0e700c26dbd6c7e19e
--- /dev/null
+++ b/talks/matlab_vs_python/migp/matlab_MIGP.ipynb
@@ -0,0 +1,80 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from nilearn import plotting\n",
+    "from nilearn import image\n",
+    "import nibabel as nb\n",
+    "import matplotlib.pyplot as plt\n",
+    "import numpy as np"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%%bash\n",
+    "\n",
+    "melodic -i matMIGP.nii.gz \\\n",
+    "\t--mask=data/brain_mask.nii.gz \\\n",
+    "\t-d 20 \\\n",
+    "\t-v \\\n",
+    "\t--nobet \\\n",
+    "\t--disableMigp \\\n",
+    "\t--varnorm \\\n",
+    "\t-o matMIGP_dim20.ica"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ics = nb.load('matMIGP_dim20.ica/melodic_IC.nii.gz')\n",
+    "\n",
+    "N = ics.shape[-1]\n",
+    "\n",
+    "fig, ax = plt.subplots(int(np.ceil((N/2))),2, figsize=(12, N))\n",
+    "\n",
+    "for img, ax0 in zip(image.iter_img(ics), ax.ravel()):\n",
+    "    coord = plotting.find_xyz_cut_coords(img, activation_threshold=2.3)\n",
+    "    plotting.plot_stat_map(img, cut_coords=coord, vmax=5, axes=ax0)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.4"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/talks/matlab_vs_python/migp/python_MIGP.ipynb b/talks/matlab_vs_python/migp/python_MIGP.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..f029476f0aacb22f53922c473df75a78788d27df
--- /dev/null
+++ b/talks/matlab_vs_python/migp/python_MIGP.ipynb
@@ -0,0 +1,168 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import glob\n",
+    "import random\n",
+    "import nibabel as nb\n",
+    "import numpy as np\n",
+    "from scipy.sparse.linalg import svds, eigs\n",
+    "import matplotlib.pyplot as plt\n",
+    "from nilearn import plotting\n",
+    "from nilearn import image"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "in_list = [nb.load(f) for f in glob.glob('data/cobre/fmri_*.nii.gz')]\n",
+    "in_mask = nb.load('data/brain_mask.nii.gz')\n",
+    "go = '3DMIGP'\n",
+    "dPCA_int = 550\n",
+    "dPCA_out = 100"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "random.shuffle(in_list)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "mask = in_mask.get_fdata().ravel()\n",
+    "\n",
+    "def demean(x):\n",
+    "    return x - np.mean(x, axis=0)\n",
+    "\n",
+    "for i, f in enumerate(in_list):\n",
+    "    \n",
+    "    print(i, end=' ')\n",
+    "    \n",
+    "    # read data\n",
+    "    grot = f.get_fdata()\n",
+    "    grot = np.reshape(grot, [-1, grot.shape[-1]])\n",
+    "    grot = grot[mask!=0, :].T\n",
+    "    grot = demean(grot)\n",
+    "    \n",
+    "    # var-norm\n",
+    "    [uu, ss, vt] = svds(grot, k=30)\n",
+    "    vt[np.abs(vt) < (2.3 * np.std(vt))] = 0;\n",
+    "    stddevs = np.maximum(np.std(grot - (uu @ np.diag(ss) @ vt), axis=0), 0.001)\n",
+    "    grot = grot/stddevs\n",
+    "    \n",
+    "    if i == 0:\n",
+    "        W = demean(grot)\n",
+    "    else:\n",
+    "        # concat\n",
+    "        W = np.concatenate((W, demean(grot)), axis=0)\n",
+    "        \n",
+    "        # reduce W to dPCA_int eigenvectors\n",
+    "        if W.shape[0]-10 > dPCA_int:\n",
+    "            uu = eigs(W@W.T, dPCA_int)[1]\n",
+    "            uu = np.real(uu)\n",
+    "            W = uu.T @ W\n",
+    "        \n",
+    "    f.uncache()\n",
+    "        \n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "grot = np.zeros([mask.shape[0], dPCA_out])\n",
+    "grot[mask!=0, :] = W[:dPCA_out, :].T\n",
+    "grot = np.reshape(grot, in_list[0].shape[:3] + (dPCA_out,))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "nb.Nifti1Image(grot, affine=in_list[0].affine, header=in_list[0].header).to_filename('pyMIGP.nii.gz')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%%bash\n",
+    "\n",
+    "melodic -i pyMIGP.nii.gz \\\n",
+    "\t--mask=data/brain_mask.nii.gz \\\n",
+    "\t-d 20 \\\n",
+    "\t-v \\\n",
+    "\t--nobet \\\n",
+    "\t--disableMigp \\\n",
+    "\t--varnorm \\\n",
+    "\t-o pymigp_dim20.ica"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ics = nb.load('pymigp_dim20.ica/melodic_IC.nii.gz')\n",
+    "\n",
+    "N = ics.shape[-1]\n",
+    "\n",
+    "fig, ax = plt.subplots(int(np.ceil((N/2))),2, figsize=(12, N))\n",
+    "\n",
+    "for img, ax0 in zip(image.iter_img(ics), ax.ravel()):\n",
+    "    coord = plotting.find_xyz_cut_coords(img, activation_threshold=1.5)\n",
+    "    plotting.plot_stat_map(img, cut_coords=coord, vmax=5, axes=ax0)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.4"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}