#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Tue Nov 26 17:42:33 2019 @author: brandt """ import random import numpy as np from helpers import ParticleBinSorter class SubsamplingMethod(object): def __init__(self, particleConatainer, desiredFraction: float = 0.2): super(SubsamplingMethod, self).__init__() self.particleContainer = particleConatainer self.fraction = desiredFraction @property def label(self) -> str: """ A specific label that can be used for plots, for instance. :return: """ raise NotImplementedError def apply_subsampling_method(self) -> tuple: """ Takes all particles from the supplied particle conatiner and returns a new list of particles that were measured by applying that subsampling procedure. Also, the actualy measured fraction is returned. (The desired fraction may not always be achievable) :returns actuallyMeasuredFraction, listOfSubParticles: """ raise NotImplementedError class RandomSampling(SubsamplingMethod): @property def label(self) -> str: return 'Random Subsampling' def apply_subsampling_method(self): numOrigParticles = len(self.particleContainer.particles) numParticles = self._get_number_of_random_particles(numOrigParticles) subParticles = random.sample(self.particleContainer.particles, numParticles) return self.fraction, subParticles def _get_number_of_random_particles(self, numTotalParticles): return np.int(np.ceil(numTotalParticles * self.fraction)) class IvlevaSubsampling(SubsamplingMethod): def __init__(self, particleContainer, sigma=1.65, mpFraction=0.01, errorMargin=0.1): super(IvlevaSubsampling, self).__init__(particleContainer) self.sigma = sigma self.estimatedMPFraction = mpFraction self.errorMargin = errorMargin @property def label(self) -> str: return 'Random fraction, Anger et al.' def apply_subsampling_method(self): N = self.particleContainer.getNumberOfParticles() numParticlesMeasured = self._get_ivleva_fraction(N) subParticles = random.sample(self.particleContainer.particles, numParticlesMeasured) fractionMeasured = numParticlesMeasured/N return fractionMeasured, subParticles def _get_ivleva_fraction(self, N): P = self.estimatedMPFraction e = P * self.errorMargin numParticlesMeasured = np.ceil(P*(1 - P) / (e**2/self.sigma**2 + P*(1-P)/N)) return np.int(numParticlesMeasured) class SizeBinFractioning(SubsamplingMethod): def __init__(self, particleConatiner, desiredfraction: float = 0.2): super(SizeBinFractioning, self).__init__(particleConatiner, desiredfraction) self.sorter: ParticleBinSorter = ParticleBinSorter() @property def label(self) -> str: return 'SizeBin Random Subsampling' def apply_subsampling_method(self): subParticlesPerBin: list = self._get_subParticles_per_bin(self.particleContainer.particles) subParticles: list = [] for subParticleList in subParticlesPerBin: for particle in subParticleList: subParticles.append(particle) return self.fraction, subParticles def _get_subParticles_per_bin(self, particleList: list): particlesInBins: list = self.sorter.sort_particles_into_bins(particleList) subParticlesPerBin: list = [] for particles in particlesInBins: numParticlesInBin: int = len(particles) numSubParticlesPerBin: int = np.int(np.round(numParticlesInBin * self.fraction)) if numSubParticlesPerBin == 0 and numParticlesInBin > 0: numSubParticlesPerBin = 1 subParticlesInBin: list = random.sample(particles, numSubParticlesPerBin) subParticlesPerBin.append(subParticlesInBin) return subParticlesPerBin