Skip to content
Snippets Groups Projects
Commit ed967df6 authored by Joshua Steer's avatar Joshua Steer
Browse files

PCA docs

parent 24b1f108
No related branches found
No related tags found
No related merge requests found
Pipeline #295 passed
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Fri Jul 20 15:03:12 2018 Package for using AmpScan to run mean-centered principal component analysis
on a group of scans
@author: js22g12 Copyright: Joshua Steer 2018, Joshua.Steer@soton.ac.uk
""" """
import os import os
...@@ -12,18 +12,52 @@ from .registration import registration ...@@ -12,18 +12,52 @@ from .registration import registration
class pca(object): class pca(object):
r"""
Principal Component Analysis methods for a training data set of AmpObjects
Used to investigate modes of variation across the population
Examples
--------
>>> p = pca()
>>> p.importFolder('/path/')
>>> p.baseline('dir/baselinefh.stl')
>>> p.register(save = '/regpath/')
>>> p.pca()
>>> sfs = [0, 0.1, -0.5 ... 0]
>>> newS = p.newShape(sfs)
"""
def __init__(self): def __init__(self):
self.shapes = [] self.shapes = []
def setBaseline(self, baseline): def setBaseline(self, baseline):
r"""
Function to set the baseline mesh used for registration of the
pca training data meshes
Parameters
----------
baseline: str
The file handle of the stl file used as the baseline
"""
self.baseline = AmpObject(baseline, 'limb') self.baseline = AmpObject(baseline, 'limb')
def importFolder(self, path, unify=True): def importFolder(self, path, unify=True):
r""" r"""
Function to import multiple stl files from folder Function to import multiple stl files from folder into the pca object
Parameters
----------
path: str
The path to the folder containing the stl files to be used as the
training data for the PCA model
unify: bool, default True
Designmate whether to unify the vertices on stl import
""" """
self.fnames = [f for f in os.listdir(path) if f.endswith('.stl')] self.fnames = [f for f in os.listdir(path) if f.endswith('.stl')]
self.shapes = [AmpObject(path + f, 'limb', unify=unify) for f in self.fnames] self.shapes = [AmpObject(path + f, 'limb', unify=unify) for f in self.fnames]
...@@ -32,14 +66,32 @@ class pca(object): ...@@ -32,14 +66,32 @@ class pca(object):
def sliceFiles(self, height): def sliceFiles(self, height):
r""" r"""
Function to slice Function to run a planar trim on all the training data for the PCA
model
Parameters
----------
height: float
The hight of the z slice plane
""" """
for s in self.shapes: for s in self.shapes:
s.planarTrim(height) s.planarTrim(height)
def register(self, scale=None, save=None, baseline=True): def register(self, scale=None, save=None, baseline=True):
r""" r"""
Function to register all the shapes to a baseline Register all the AmpObject training data to the baseline AmpObject
Parameters
----------
scale: float, default None
scale parameter used for the registration
save: str, default None
If not None, this will save the registered
baseline: bool, default True
If True, the baseline AmpObject will also be included in the PCA
model
""" """
self.registered = [] self.registered = []
for t in self.shapes: for t in self.shapes:
...@@ -56,7 +108,9 @@ class pca(object): ...@@ -56,7 +108,9 @@ class pca(object):
def pca(self): def pca(self):
r""" r"""
Function to run mean centered pca on the registered data Function to run mean centered pca using a singular value decomposition
method
""" """
self.pca_mean = self.X.mean(axis=1) self.pca_mean = self.X.mean(axis=1)
X_meanC = self.X - self.pca_mean[:, None] X_meanC = self.X - self.pca_mean[:, None]
...@@ -69,10 +123,19 @@ class pca(object): ...@@ -69,10 +123,19 @@ class pca(object):
Function to calculate a new shape based upon the eigenvalues Function to calculate a new shape based upon the eigenvalues
or stdevs or stdevs
Notes Parameters
----- ----------
Update so works with stdevs sfs: array_like
An array of scaling factors to generate the new shape. This
must be of the same length as the number of principal components
scale: str, default 'eigs'
A string to indicate whether the sfs correspond to mode energy or
to standard deviations about the mean
""" """
try: len(sfs) == len(self.pc_stdevs)
except: ValueError('sfs must be of the same length as the number of '
'principal components')
if scale == 'eigs': if scale == 'eigs':
sf = (self.pca_U * sfs).sum(axis=1) sf = (self.pca_U * sfs).sum(axis=1)
elif scale == 'std': elif scale == 'std':
......
...@@ -64,10 +64,17 @@ class registration(object): ...@@ -64,10 +64,17 @@ class registration(object):
subset: array_like, default None subset: array_like, default None
Indicies of the baseline nodes to include in the registration, default is none so Indicies of the baseline nodes to include in the registration, default is none so
all are used all are used
scale: float, default None
If not None scale the baseline mesh to match the target mesh in the z-direction,
the value of scale will be used as a plane from which the nodes are scaled.
Nodes with a higher z value will not be scaled.
smooth: int, default 1 smooth: int, default 1
Indicate number of laplacian smooth steps in between the steps Indicate number of laplacian smooth steps in between the steps
fixBrim: bool, default False fixBrim: bool, default False
If True, the nodes on the brim line will not be included in the smooth If True, the nodes on the brim line will not be included in the smooth
error: bool, default False
If True, the polarity will be included when calculating the distance
between the target and baseline mesh
""" """
# Calc FaceCentroids # Calc FaceCentroids
...@@ -126,7 +133,6 @@ class registration(object): ...@@ -126,7 +133,6 @@ class registration(object):
self.reg.calcNorm() self.reg.calcNorm()
self.reg.calcStruct() self.reg.calcStruct()
# self.reg.values[:] = self.calcError(False)
self.reg.values[:] = self.calcError(error) self.reg.values[:] = self.calcError(error)
def calcError(self, direct=True): def calcError(self, direct=True):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment