iam-git / WellMet (public) (License: MIT) (since 2021-08-31) (hash sha1)
WellMet is pure Python framework for spatial structural reliability analysis. Or, more specifically, for "failure probability estimation and detection of failure surfaces by adaptive sequential decomposition of the design domain".

/IS_stat.py (0907e38499eeca10471c7d104d4b4db30b8b7084) (28117 bytes) (mode 100644) (type blob)

#!/usr/bin/env python
# coding: utf-8

"""
"""


import numpy as np
import collections # for defaultdict
#from scipy import optimize # it was needed for spring solution
from .candybox import CandyBox
from scipy import stats # for sample_like()
from . import f_models # for sample_like()
from .spring import get_spring_solution # for new IS function, for ISSI in future
from .welford import Welford # for PushAndPull




def get_1DS_sample(f, x_sub):
    """1DS stands for 1D sampling.
    1DS is, actually, an importance sampling method
    that, actually, does not calculate IS weights
    as f_density / h_density, but
    (arbitrary, nonuniformly) divides interval to subintervals.
    By using CDF (SF, actually) transformes subintervals 
    to 0-1 uniform probability line.
    Then gets one node as middle point of every subinterval,
    weights therefore are just interval widths itself.
    No sampling imprecisions are introduced, 
    therefore no spring, no correction are needed. 
    Usage:
    Function takes: f, x
    f: univariate (1D) distribution (scipy-compatible)
    x_sub: coordinates (n+1) of subintervals.
    Returns: x, weights
    x: n coordinates
    weights: same as IS weights
    """
    # x: 1, 2, 3
    # SF: 0.1, 0.01, 0.001
    u_sub = f.sf(x_sub)
    weights = u_sub[:-1] - u_sub[1:]
    np.abs(weights, out=weights)
    u = (u_sub[:-1] + u_sub[1:])
    #č u musí být float
    #č takže dělení dvěma nemusí robiť potíže 
    np.divide(u, 2, out=u)
    x = f.isf(u)
    return x, weights

#č chcu udělat menší IS funkci s výrovnáním odhadů
#č Síce tohle už umí ISSI, 
#č třída ale neočekává celkovou pravděpodobnost odlišnou od nuly.
#č Taky jeji funkcionalita je hodně přebytečná pro Ghull.
#č Zkratka, chcu funkci, která by na vstupu brala:
#č 1. vahy IS (tj. funkce nebude řešít souřadnice a hustoty)
#č 2. roušku (omezíme se na nejčastější případ dvou vzajemně vyloučujících jevů) a
#č 3. celkovou pravděpodobnost
#č a posleze vracela výrovnané odhady

def get_IS_estimation(weights, mask, p_overall=1):
    """Function calculates Importance Sampling estimators
    of two mutually excludive events (typical for indicator or Heaviside step function). 
    It performs correction based on a priory known overall probability.
    It takes numpy array of IS weghts (PDF / weighting function density),
    numpy boolean array and a priory known overall probability.
    Returns corrected estimators for true_event and false_event"""
    
    #č vstupní kontroly
    if np.all(mask): #č všecko je pravda - není co řešit
        return p_overall, 0
    if not np.any(mask): #č není žádná pravda - není žádné překvapení
        return 0, p_overall
    
    Nsim = len(weights)
    # there are two events:
    true_masked = weights[mask]
    false_masked = weights[~mask]
    #č podle IS vzorečků
    #č když uživatel bude hodně snažit, tak
    #č v mat. očekaváních může zajistit nuly
    #č nechcu zatím řešit
    true_mean = np.sum(true_masked)/Nsim
    false_mean = np.sum(false_masked)/Nsim
    if (true_mean + false_mean) == p_overall:
        return true_mean, false_mean
    #č podle toho, co vidím ve vzorcích - mám pocit, že se
    #č nůlový rozptyl může vyskytnout pouze v případě Nsim == 1, což
    #č zde od uživatele neočakávám. Nebo spolu s nulovým průměrem,
    #č který zatím taky nechcu řešit.
    #č (Ten ISSI je teda hrozně перемудрёный, řeší bůhvíco)
    #č Zde to nepotřebujem, udělame všecko jednoduše.
    true_var = (np.sum(true_masked**2)/Nsim - true_mean**2) / Nsim
    false_var = (np.sum(false_masked**2)/Nsim - false_mean**2) / Nsim
    
    #č pošleme to pružině. Ta je obecnější, na větší počet jevů
    #č proto bere jako vstup numpy matice
    lenghts = np.array((true_mean, false_mean))
    flexibilities = np.array((true_var, false_var))
    #č vrací taky numpy
    ls = get_spring_solution(lenghts, flexibilities, L=p_overall)
    #č rozbalíme 
    #č (na rovinu, dělám to jen aby pak kód bylo možné jednoduše přečíst)
    true_corrected_mean, false_corrected_mean = ls
    
    return true_corrected_mean, false_corrected_mean #(*ls,)




# One does not simply get IS estimations...
#č Získat IS odhady není natolik snadné...
#č Ghull totiž integruje po částech
#č a mně příšlo, že nejsprávnější volbou
#č bude i odhady zpracovavat po částech.
#č Musel jsem tedy vytvořit třídu (Welford) pro
#č průběžný výpočet průměrů a rozptylů.
#č Teď na jeji bazí postavíme IS odhadovačku
class PushAndPull: #ё Тяни-Толкай
    """Class calculates corrected Importance Sampling estimators
    of two mutually excludive events (typical for indicator or Heaviside step function). 
    It performs correction based on a priory known overall probability.
    It takes numpy array of IS weghts (PDF / weighting function density),
    numpy boolean array and a priory known overall probability."""
    def __init__(self):
        self.true_wf = Welford()
        self.false_wf = Welford()
    
    def add(self, weights, mask):
        #č add_sparse() - právě pro IS,
        #č aby se uvnitř netrapilo nulama
        n = len(mask)
        self.true_wf.add_sparse(weights[mask], n)
        self.false_wf.add_sparse(weights[~mask], n)
       
    @property 
    def mean(self):
        return self.true_wf.mean, self.false_wf.mean
        
    @property 
    def var(self):
        #č otazkou je rozptyl čeho tady máme na mysli
        #č Já bych zde spíše očekával rozptyl průměru
        #č takže rozptyl vah IS vydělíme celkovým počtem měření
        assert self.true_wf.n == self.false_wf.n
        true_var = self.true_wf.s2 / self.true_wf.n
        false_var = self.false_wf.s2 / self.false_wf.n
        return true_var, false_var
    
    def correct_means(self, p_overall=1):
        """Method calculates Importance Sampling estimators
        of two mutually excludive events. 
        It performs correction based on a priory known overall probability.
        It takes as a optional parameter a priory known overall probability.
        Returns corrected estimators for true_event and false_event"""
        
        # there are two events:
        true_mean = self.true_wf.mean
        false_mean = self.false_wf.mean
        #č vstupní kontroly
        if (true_mean + false_mean) == p_overall:
            return true_mean, false_mean
        if (true_mean == 0) and (false_mean == 0):
            half = p_overall / 2
            return half, half #č půl na půl
        if true_mean == 0: 
            return 0, p_overall
        if false_mean == 0: 
            return p_overall, 0
        
        
        #č Nulové rozptyly neřeším - neumím představit, za jakých 
        #č podmínek by mohly ve skutečné aplikaci výskytnout 
        #č (variana s jediným vzorkem mně nezajimá)
        #č striktně vzato, musíme zde vydělit počtem měření
        #č ale vysledek se dělením na stejné číslo nezmění.
        #č Dokonce můžeme se spokojit i prostě součtemi kvadratů
        true_var = self.true_wf.S
        false_var = self.false_wf.S
        
        #č pošleme to pružině. Ta je obecnější, na větší počet jevů.
        #č Proto bere jako vstup numpy matice
        lenghts = np.array((true_mean, false_mean))
        flexibilities = np.array((true_var, false_var))
        #č vrací taky numpy
        ls = get_spring_solution(lenghts, flexibilities, L=p_overall)
        #č rozbalíme 
        #č (na rovinu, dělám to jen aby pak kód bylo možné jednoduše přečíst)
        true_corrected_mean, false_corrected_mean = ls
        
        return true_corrected_mean, false_corrected_mean #(*ls,)
    
    
    
    

#č ISS a ISSI by bylo vhod předělat na spring a 
#č vyhodit ty blbosti s půlením intervalů.
#č Nechám to na potom, po zakroku by bylo potřebné 
#č všecko dobře otestovat, teď na to nemám ani kapacitu, ani zajem
#č Goal&Ghull ISSI nepotřebuje.

# bisect function is taken from somewhere in internet. 
# https://www.quora.com/How-does-one-correctly-compare-two-floats-in-Python-to-test-if-they-are-equal
# Hope it is not big issue.
def bisect(f, target, low, high):
    low, high = float(low), float(high)
    while low != high:
        mid = (low + high) / 2
        f_mid = f(mid)
        if f_mid == target:
            return mid
        elif f_mid > target:
           high = mid if mid != high else low
        else:
           low = mid if mid != low else high
    return low
    

#ё нам позарез нужен ещё один, свой собственный словник 
#č ten, na rozdil od defaultdict'a, neuklada chybejicí složky do slovníku
class DefaultDict(dict):
    def __init__(self, default_value=None):
        self.default_value = default_value
        
    def __missing__(self, key):
        return self.default_value
    
    
#
#č deme na to, koťě!
#
    
    #č IS, (n-2)-krátá realizace, n>>2, n→∞
def IS(f, h_model, space_from_h='R', space_to_f='G',  Nsim=int(1e4)):
    """
    space_from_h
    space_to_f
    matching - v jakých souřadnicích robím merge a jaká PDF použiváme?
    """
    
    
    # zgenerujeme vzorky
    # nic zajimavýho
    h = h_model(Nsim)
    
    
    # tady musíme provést jeden trik
    # totež jako v IS_like - ve výsledku dycky dostaneme f_model
    to_sample = f.new_sample(getattr(h, space_from_h), space_to_f) # R-ko smerdžíme ako G-čko
    w = to_sample.pdf(space_to_f) / h.pdf(space_from_h) # snad je to správně
    
    # vahy máme
    # zabalme do boxu
    # zbytek už nejsou naši starosti
    return CandyBox(to_sample, w=w)
        


def get_norm_plan(nis, nvar, mean=0, std=1, design=None):
    """
    mean: [0.05, 2, 100500]
    std: [0.05, 2, 100500]
    
    design(nis, nvar) should return sampling plan in hypercube (U space)!
    """
    
    if design is None:
        sampling_plan_G = np.random.randn(nis, nvar)
    else: #оӵ я, ярам, гиперкуб. Кинлэсь меда Гаусс вӧлмэтын дизайн куром на?
        #č transformací z U prostoru dochází ke ztratě přesnosti 
        #č a omezenému na 8.2 poloměru v Gaussovském prostoru.
        #č Jenže jsem si uvědomil, že těžko na to narazím,
        #č projevilo by se to při počtu vzorků řadově 10**15,
        #č to bych musel tejden generovat vzorky
        sampling_plan_G = stats.norm.ppf(design(nis, nvar))
    
    
    #č pdf spočteme na původním Gaussovském designu
    # sample_pdf(sample / alpha) / np.prod(alpha)
    
    ## desired: pdf = np.prod(stats.norm.pdf(sampling_plan_G) / std, axis=1)
    pdf = stats.norm.pdf(sampling_plan_G)
    pdf = np.divide(pdf, std, out=pdf)
    pdf = np.prod(pdf, axis=1)
    
    
    #č a teď pustíme se do výpočtu souřadnic
    ## desired: sampling_plan_N = (sampling_plan_G * std) + mean
    sampling_plan_N = sampling_plan_G; del(sampling_plan_G)
    sampling_plan_N = np.multiply(sampling_plan_N, std, out=sampling_plan_N)
    sampling_plan_N = np.add(sampling_plan_N, mean, out=sampling_plan_N)
    
    return sampling_plan_N, pdf


def IS_norm(f, mean=0, std=1, sampling_space='G', nis=1000, design=None):
    """
    mean: [0.05, 2, 100500]
    std: [0.05, 2, 100500]
    
    design(nis, nvar) should return sampling plan in hypercube (U space)!
    """
    
    sampling_plan_N, pdf = get_norm_plan(nis, f.nvar, mean, std, design)
    
    #č tady musíme provést jeden trik
    #č totež jako v IS_like - ve výsledku dycky dostaneme f_model
    to_sample = f.new_sample(sampling_plan_N, sampling_space) #č naše N-ko smerdžíme ako G-čko
    w = to_sample.pdf(sampling_space) / pdf #č snad je to správně
    
    #č vahy máme
    #č zabalme do boxu
    #č zbytek už nejsou naši starosti
    return CandyBox(to_sample, w=w)



    # for simplex: d = nvar+2 
    # for cell: d = base_r**2
def IS_like(f_plan, sampling_space='G', weights=None, nis=1000, d=1, design=None):
    """
    takes sample and returns sampling plan with the same cov (in case d=1)
    covariance matrix we'll divide by d
    """
    
    plan = getattr(f_plan, sampling_space)
    
                
    S_bc = np.cov(plan, rowvar=False, bias=True, aweights=weights)
    barycenter = np.average(plan, axis=0, weights=weights)
    
    
    #č matika
    w, v = np.linalg.eig(S_bc)
    
    # use IS sampling density with center equal to the simplex's barycenter
    # set the minimum distance as the standard deviation of IS densisty
    #č u stats.norm zadáváme směrodatnou odchylku, to je asi správné
    sigmas = np.sqrt(w/d) 
    mean = 0
    h_plan_N, pdf_N = get_norm_plan(nis, f_plan.nvar, mean, sigmas, design)
    
    #ёӵ здесь уже так легко не отделаемся. Трансформовать кароно.
    h_plan_bc = (v @ h_plan_N.T).T
    h_plan_sing = h_plan_bc + barycenter
    
    
    
    #č sice to má nazev h_plan, ale nese rozdělení a hustoty v f-ku
    #
    #č jelikož výtvaříme nový vzorek z "čístých" souřadnic
    #č máme zaručeno, že h_plan vždy bude f_modelem (podle současného kódu CandyBox)
    h_plan = f_plan.new_sample(h_plan_sing, sampling_space) 
    ## desired: w = h_plan.pdf(sampling_space) / pdf_N # snad je to správně
    w = np.divide(h_plan.pdf(sampling_space), pdf_N, out=pdf_N); del(pdf_N)
    
    #č vahy máme
    #č zabalme do boxu
    #č zbytek už nejsou naši starosti
    return CandyBox(h_plan, w=w)


            
#č tím "TrueIS" bylo myšleno abosolutně matematicky korektní 
#č započítávání sad vzorků, vybraných z libovolných (navzajem odlišných)
#č rozdělení IS. 
#č To je v literatuře známo jako "multiple importance sampling"
#č Tahle třída (na rozdil od ISSI) musí udržovat v paměti souřadnice všech 
#č předchozích sad vzorku a jejich rozdělení.
#č Tím se to líší od ISSI, která počíta ze sad pouze statistiky 
#č a ukladá pouze průměry a rozptyly.
#č S touhle třídou se hralo v době prvních hraček s rejection samplingem
#č a bylo to spíše takový proof-of-concept, u kterého jsem uviděl, že funguje,
#č ale žádné extensivní testování nikdy nebylo provedeno a třída nikdy nebyla
#č skutečně použita v kódu WellMet.
#č Tehdy problemem bylo to, že pro učely rejection samplingu by bylo třeba
#č ukladat nejen souřadnice a rozdělení, ale ještě navic i stavy 
#č přislušných triangulací, což bylo už i pro mně moc. 

#č Má nějakou alfu, která "not used", netuším, k čemu sloužila.
#č Má metody pro výpočet vah a pro výpočet odhadů s (bacha!) úpravenými vzorečky. 
class TrueIS:
    def __init__(self, f, IS_mode='G'):
        self.f_sample = f()
        self.series_data = []
        self.IS_mode = IS_mode
        self.events = []
        
    def add_IS_serie(self, sample, events, alpha=1):
        """
        alpha is for rejection sampling, not used
        """
        self.series_data.append((sample, events, alpha))
        self.events += events
        if self.IS_mode == 'R':
            # jestli máme to právé vzorkovácí rozdělení - tak nemáme čo robiť
            self.f_sample.add_sample(sample) # smerdží se to po R
            # w like weights
            #wt.w = to_sample.pdf_R / wt.h.pdf_R
        else: #IS_mode == 'G':
            # tady musíme provést jeden trik
            self.f_sample.add_sample(sample.R, 'G') # R-ko smerdžíme ako G-čko
            #wt.w = to_sample.pdf_G / wt.h.pdf_R # snad je to správně
        
        
    def get_weights(self):
        accu_denom_w = np.zeros(self.f_sample.nsim, dtype=float)
        if self.IS_mode == 'R':
            for serie in self.series_data:
                h_sample, events, alpha = serie
                h = h_sample.new_sample(self.f_sample)
                accu_denom_w += h.pdf_R * h_sample.nsim
            return self.f_sample.pdf_R / accu_denom_w, self.events
                
        else: # IS_mode == 'G'
            for serie in self.series_data:
                h_sample, events, alpha = serie
                h = h_sample.new_sample(self.f_sample.G, 'R')
                accu_denom_w += h.pdf_R * h_sample.nsim
            return self.f_sample.pdf_G / accu_denom_w, self.events
                
        
    def get_means(self):
        w, events = self.get_weights()
        keys = np.unique(events)
        Nsim = len(w)
        
        means = np.empty(len(keys), dtype=float)
        vars = np.empty(len(keys), dtype=float)
        
        
        # tak bacha
        ev = np.array(events)
        for key, i in zip(keys, range(len(keys))):
                
            mask = ev==key
            ev_w = w[mask]
            # podle IS vzorečků (skoro)
            key_mean = np.sum(ev_w)
            key_var = (np.sum(ev_w**2)*Nsim - key_mean**2) / Nsim
                
            # uložime
            means[i] = key_mean
            vars[i] = key_var
            
            # vyfiltrujeme zbytek
            w = w[~mask]
            ev = ev[~mask]
        
        # kontrola  
        assert len(w)==0 and len(ev)==0, "Что за хренотень?!"
        
        return means, vars
        

class ISSI:
    """
    IS statistics = weights series + events
    ISSI calculates probabilities of repeated IS series
    with implicit (non-zero) variances
    
    zda se mi, že tím nelze nic zhoršit
    """
    #č mám pocit, že je tu někde bug pythonu
    #č ten prazdnej list [] přežije cokoliv
    def __init__(self, events=None):
        """
        """ 
        self.series_data = []
        if events is None:
            self.events = []
        else:
            self.events = events
        
    def add_IS_serie(self, weights, events, implicit_multiplicator=1):
        keys = np.unique(events)
        Nsim = len(weights)
        
        # vytvoříme slovník pro sadu vzorků, který implicitně
        # bude předpokládát průměr=0 a rozptyl 1/Nsim^2 
        # pro všecko, co se v sadě neobjevilo
        if Nsim == 1:
            # jedno meření je taky měření)
            implicit_var = implicit_multiplicator
        else:
            implicit_var = implicit_multiplicator*(1-1/Nsim)/Nsim**2
        serie = DefaultDict((0, implicit_var))
        
        # tak bacha
        w = np.array(weights)
        ev = np.array(events)
        for key in keys:
            if key not in self.events:
                self.events.append(key)
                
            mask = ev==key
            ev_w = w[mask]
            # podle IS vzorečků
            key_mean = np.sum(ev_w)/Nsim
            key_var = (np.sum(ev_w**2)/Nsim - key_mean**2) / Nsim
            if key_var == 0: # to nechcem
                key_var = implicit_var
                
            # uložime
            serie[key] = (key_mean, key_var)
            
            # vyfiltrujeme zbytek
            w = w[~mask]
            ev = ev[~mask]
        
        # kontrola  
        assert len(w)==0 and len(ev)==0, "Что за хренотень?!"
        
        self.series_data.append(serie)
        
        # ачиз значение утёз
        self.get_estimations()
        
        
        
    def add_single_event_data(self, weights, event, nis=None, implicit_multiplicator=np.inf):
        
        if nis is None:
            Nsim = len(weights)
        else:
            Nsim = nis
            
        # kontrola předpokladů
        assert len(weights) <= Nsim, "jseš jistej, že máš spravnej počet měření?"
        
        # vytvoříme slovník pro sadu vzorků, který implicitně
        # bude předpokládát průměr=0 a rozptyl 1/Nsim^2 
        # pro všecko, co se v sadě neobjevilo
        if Nsim == 1:
            # jedno meření je taky měření)
            implicit_var = implicit_multiplicator
        else:
            implicit_var = implicit_multiplicator*(1-1/Nsim)/Nsim**2
        serie = DefaultDict((0, implicit_var))
        
        
        w = np.array(weights)
        if event not in self.events:
            self.events.append(event)
            
        # podle IS vzorečků
        key_mean = np.sum(w)/Nsim
        key_var = (np.sum(w**2)/Nsim - key_mean**2) / Nsim
        if key_var == 0: # to nechcem
            key_var = implicit_var
            
        # uložime
        serie[event] = (key_mean, key_var)
        
        self.series_data.append(serie)
        
        #č nebudeme hned počítat odhady, nechť to dělá volající kód sam
        # ачиз значение утёз
        #self.get_estimations()
        #self.estimations = {self.events[i] : self.values[i] for i in range(len(self.events))}
    
        
    def get_means(self):
        # počet jevů
        # number of events
        n_ev = len(self.events)
        self.weighted_means = np.empty(n_ev, dtype=float)
        self.weighted_vars = np.empty(n_ev, dtype=float)
        
        # spočteme važené průměry a rozptyly
        for event, key in zip(self.events, range(n_ev)):
            key_accusum = 0
            key_accuweight = 0
            for serie in self.series_data:
                key_mean, key_var = serie[event]
                key_accusum += key_mean / key_var
                key_accuweight += 1/key_var
            
            if key_accuweight == 0:
                #č já vidím, že .get_estimations nuly skryje
                self.weighted_means[key] = 0
                self.weighted_vars[key] = np.inf
            else:
                self.weighted_means[key] = key_accusum / key_accuweight
                self.weighted_vars[key] = 1/key_accuweight
            
        
        return self.weighted_means, self.weighted_vars, np.array(self.events)
            
    def get_estimations(self):
    
        # p-čka. We are still talking about probabilities, right?    
        p, vars, __ = self.get_means() 
        
        sum_p = np.sum(p)
        if sum_p == 1: # а вдруг?
            self.values = p
            #♥оӵ ачиз значение утёз
            self.estimations = {self.events[i] : self.values[i] for i in range(len(self.events))}
            return p, np.array(self.events)
            
            # spring (non-linear) analogue
        # silu, kterou zatížíme system jen tak neseženeme
        elif sum_p > 1: # stlačit
            low_atF = -np.pi/2
            high_atF = 0
        else: # roztahnout
            low_atF = 0
            high_atF = np.pi/2
        
        # nuly nám udělaj problém    
        mask = p==0
        p = np.ma.array(p)
        vars = np.ma.array(p)
        p.mask = mask
        vars.mask = mask
        
        F = np.tan(bisect(lambda atF: np.sum(np.exp(np.tan(atF)*vars/p)*p), 1, low_atF, high_atF))
        corrected_means = p * np.exp(F * vars/p)
        
        corrected_means.mask = False
        self.values = corrected_means
        #♥ ачиз значение утёз
        self.estimations = {self.events[i] : self.values[i] for i in range(len(self.events))}
        return corrected_means, np.array(self.events)
        
        
    def delete_event_data(self, event):
        """
        pomocná funkce třeba pro případ změny (zpřesnění) nějakých jevů na straně modelu
        function doesn't recalculate estimations after that!
        """
        # prozatím nebudu kontrolovat, zda ten jev tam vůbec je
        self.events.remove(event)
        for serie in self.series_data:
            # vymažem jev
            if event in serie:
                serie.pop(event)
        
        #č krucinál, tady narazím už ne natolik triviálné chyby
        for i in range(len(self.series_data)-1, -1, -1): # reverse
            serie = self.series_data[i]
            #č zkontrolujem, zda v serii se ještě něco zůstalo
            if len(serie) == 0:
                #č jako doufám, že tam tato serie bude jedina 
                self.series_data.remove(serie)
                
        
        
        
        
        


class ISS:
    """
    IS statistics = weights series + events
    ISS calculates probabilities of repeated IS series
    """
    def __init__(self):
        """
        defaultdict zajistí, že na každý jev nás čeka seznam, do kterého 
        může přidávat průměr a rozptyl ze sady
        """ 
        self.data = collections.defaultdict(list)
        
    def add_IS_serie(self, weights, events):
        keys = np.unique(events)
        Nsim = len(weights)
        
        # tak bacha
        w = np.array(weights)
        ev = np.array(events)
        for key in keys:
            mask = ev==key
            ev_w = w[mask]
            # podle IS vzorečků
            key_mean = np.sum(ev_w)/Nsim
            key_var = (np.sum(ev_w**2)/Nsim - key_mean**2) / Nsim
            # uložime
            self.data[key].append((key_mean, key_var))
            
            # vyfiltrujeme zbytek
            w = w[~mask]
            ev = ev[~mask]
        
        # kontrola  
        assert len(w)==0 and len(ev)==0, "Что за хренотень?!"
        
        
        
    def get_means(self):
        # počet jevů
        # number of events
        weighted_means = []
        weighted_vars = []
        
        # spočteme važené průměry a rozptyly
        for key_data in self.data.values():
            key_accusum = 0
            key_accuweight = 0
            for key_mean, key_var in key_data:
                key_accusum += key_mean / key_var
                key_accuweight += 1/key_var
            
            weighted_means.append(key_accusum / key_accuweight)
            weighted_vars.append(1/key_accuweight)
            
            
        return weighted_means, weighted_vars, np.array(list(self.data.keys()))
            
    def get_estimations(self):
        weighted_means, weighted_vars, __ = self.get_means() 
        
        # p-čka. We are still talking about probabilities, right?    
        p = np.array(weighted_means)
        vars = np.array(weighted_vars)
        
        sum_p = np.sum(p)
        if sum_p == 1: # а вдруг?
            return p, np.array(list(self.data.keys()))
            
            # spring (non-linear) analogue
        # silu, kterou zatížíme system jen tak neseženeme
        elif sum_p > 1: # stlačit
            low_atF = -np.pi/2
            high_atF = 0
        else: # roztahnout
            low_atF = 0
            high_atF = np.pi/2
            
        F = np.tan(bisect(lambda atF: np.sum(np.exp(np.tan(atF)*vars/p)*p), 1, low_atF, high_atF))
        corrected_means = p * np.exp(F * vars/p)
        
        return corrected_means, np.array(list(self.data.keys()))
        
        
        
            
           # scipy проверяет брэкеты, чем вызывает overflow warning, раздражает
#        sol = optimize.root_scalar(lambda atF: np.sum(np.exp(np.tan(atF)*vars/p)*p)-1, bracket=(-np.pi/2, np.pi/2) )
#        F = np.tan(sol.root)
        
        
        
        
        
                # triangle analogue        
#        if len(weighted_means) == 2:
#           # tak je to jednoduchý
#           # it's piece of cake
#           # triangle analogue
#           a, b = *weighted_means
#           # derivations of Heron's formula
#           da = 8*a*b**2 + 6*a - 2*b - 2
#           db = 8*b*a**2 + 6*b - 2*a - 2
#           # matice koeficientů přetvořené podmínkové rovnice
#           B = np.array([[1,1], [da, db]])

#           u = 1 - np.sum(p)
#           # vzoreček z teorii chyb měření a vyrovnávacího počtu nefunguje
#           # dává aj záporné pravděpodobnosti
#           #k = (1 - np.sum(weighted_means))/np.sum(weighted_vars)
#           #corrected_means = weighted_means + weighted_vars*k


Mode Type Size Ref File
100644 blob 28117 0907e38499eeca10471c7d104d4b4db30b8b7084 IS_stat.py
100644 blob 6 0916b75b752887809bac2330f3de246c42c245cd __init__.py
100644 blob 72 458b7e2ca46acd9ec0d2caf3cc4d72e515bb73dc __main__.py
100644 blob 73368 3d245b8568158ac63c80fa0847631776a140db0f blackbox.py
100644 blob 11243 10c424c2ce5e8cdd0da97a5aba74c54d1ca71e0d candybox.py
100644 blob 29927 066a2d10ea1d21daa6feb79fa067e87941299ec4 convex_hull.py
100644 blob 102798 059ae717e71c651975673420cd8230fbef171e5e dicebox.py
100644 blob 36930 a775d1114bc205bbd1da0a10879297283cca0d4c estimation.py
100644 blob 34394 3f0ab9294a9352a071de18553aa687c2a9e6917a f_models.py
100644 blob 34445 e8d9b920f30714485f572ceb0054cb4bf196f733 g_models.py
100644 blob 20908 457329fe567f1c0a9950c21c7c494cccf38193cc ghull.py
100644 blob 2718 5d721d117448dbb96c554ea8f0e4651ffe9ac457 gp_plot.py
100644 blob 29393 96162a5d181b8307507ba2f44bafe984aa939163 lukiskon.py
100644 blob 2004 6ea8dc8f50a656c48f786d5a00bd6398276c9741 misc.py
040000 tree - 258a4950343c851f930f98d1b7a8bbd0ef132fea mplot
100644 blob 1462 437b0d372b6544c74fea0d2c480bb9fd218e1854 plot.py
100644 blob 2807 1feb1d43e90e027f35bbd0a6730ab18501cef63a plotly_plot.py
040000 tree - bfb2adfd17a5c916d2a132e2607f57f14561559e qt_gui
100644 blob 8566 5c8f8cc2a34798a0f25cb9bf50b5da8e86becf64 reader.py
100644 blob 4284 a0e0b4e593204ff6254f23a67652804db07800a6 samplebox.py
100644 blob 6558 df0e88ea13c95cd1463a8ba1391e27766b95c3a5 sball.py
100644 blob 6739 0b6f1878277910356c460674c04d35abd80acf13 schemes.py
100644 blob 76 11b2fde4aa744a1bc9fa1b419bdfd29a25c4d3e8 shapeshare.py
100644 blob 54884 fbe116dab4fc19bb7568102de21f53f15a8fc6bf simplex.py
100644 blob 13090 2b9681eed730ecfadc6c61b234d2fb19db95d87d spring.py
100644 blob 10953 da8a8aaa8cac328ec0d1320e83cb802b562864e2 stm_df.py
040000 tree - 97498f5c93e2ccccd058f8c6614eeb647014b54a testcases
100644 blob 2465 d829bff1dd721bdb8bbbed9a53db73efac471dac welford.py
100644 blob 25318 fcdabd880bf7199783cdb9c0c0ec88c9813a5b18 whitebox.py
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/iam-git/WellMet

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/iam-git/WellMet

Clone this repository using git:
git clone git://git.rocketgit.com/user/iam-git/WellMet

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main