Module hydroinform.ts_tools

Source code
from __future__ import division
import pandas as pd
import numpy as np
import math
from scipy.interpolate import interp1d
from matplotlib import path
import matplotlib.pyplot as plt

def lazy_property(fn):
    '''Decorator that makes a property lazy-evaluated.
    '''
    attr_name = '_lazy_' + fn.__name__

    @property
    def _lazy_property(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)
    return _lazy_property


class HydroTs(object):
    def __init__(self, TimeSeries):
        self.time_series= TimeSeries
        self.__np_values = TimeSeries.values
        self.__np_index = TimeSeries.index.values
        self.__years = TimeSeries.index.year
        self.__values_range = range(len(self.__np_values))


    def EventFreq(self, Comparer):
        EventCount = {}
        EventLength = {}
        currentyear = self.__years[0]
        NumberOfEvents = 0;
        CurrentEventLengt = []
        InEvent = False

        for i in self.__values_range:
            #We got a new year. Store data
            if (self.__years[i] != currentyear):
                EventCount[currentyear]= NumberOfEvents
                EventLength[currentyear]= CurrentEventLengt
                currentyear = self.__years[i];
                NumberOfEvents = 0;
                CurrentEventLengt = [];
                CurrentEventLengt.append(0);
    
            if (Comparer(self.__np_values[i])):
            #This is a new event
                if not InEvent:
                    NumberOfEvents+=1
                    InEvent = True
                    CurrentEventLengt.append(0)
            #Increase number of days in event
                CurrentEventLengt[len(CurrentEventLengt) - 1]+=1
            else:
                InEvent = False
    
        if not currentyear in EventCount:
            EventCount[currentyear] = NumberOfEvents
            EventLength[currentyear] = CurrentEventLengt
        #First average the length on years, then average the averages
        #Average length on years:
        ayear=np.mean([v for k,v in EventCount.items()])
        lengthMoreThenZero=[v for k,v in EventLength.items() if len(v)>0]
        if len(lengthMoreThenZero)== 0:
            return [ayear, 0]
        return [ayear, np.mean([np.mean(v) for v in lengthMoreThenZero])]   

    @lazy_property
    def Q95(self):
        self.__set_percentiles()
        return self._lazy_Q95

    @lazy_property
    def Q90(self):
        self.__set_percentiles()
        return self._lazy_Q90


    @lazy_property
    def Q75(self):
        self.__set_percentiles()
        return self._lazy_Q75

    @lazy_property
    def Q50(self):
        self.__set_percentiles()
#        Q50 =self.time_series.median()
        return self._lazy_Q50

    @lazy_property
    def Q25(self):
        self.__set_percentiles()
        return self._lazy_Q25


    def __set_percentiles(self):
        q=[0.05, 0.1, 0.25,0.5,0.75]
        p= np.quantile(self.__np_values, q, interpolation ='linear')
        self._lazy_Q95 =p[0]
        self._lazy_Q90 =p[1]
        self._lazy_Q75=p[2]
        self._lazy_Q50=p[3]
        self._lazy_Q25=p[4]

    def fre25(self):
        return self.EventFreq(lambda v : v > self.Q25)[0]

    def fre75(self):
        return self.EventFreq(lambda v : v > self.Q75)[0]

    def fre1(self):
        return self.EventFreq(lambda v : v > self.Q50)[0]

    def fre3(self):
        return self.EventFreq(lambda v : v > 3*self.Q50)[0]

    def fre7(self):
        return self.EventFreq(lambda v : v > 7*self.Q50)[0]

    """
    Calculates the average number of timesteps where the value is above a threshold
    """
    def duration(self, Threshold):
        Durations = []
        Durations.append(0)

        for i in self.__values_range:
            if (self.__np_values[i] > Threshold):
                Durations[-1]+=1
            elif self.__np_values[i] <= Threshold and Durations[-1] > 0:
                Durations.append(0)
    
        if Durations[-1] == 0:
            del Durations[-1]
        if (len(Durations) == 0):
            return 0
        return np.mean(Durations)

    def values_above(self, Threshold):
        Durations = []
        Durations.append([])

        for i in self.__values_range:
            if (self.__np_values[i] > Threshold):
                Durations[-1].append(self.__np_values[i])
            elif self.__np_values[i] <= Threshold and len(Durations[-1]) > 0:
                Durations.append([])
    
        if len(Durations[-1]) == 0:
            del Durations[-1]
        return Durations




    """
    Calculates the average number of timesteps where the value is below a threshold
    """
    def DurationBelow(self, Threshold):
        Durations = []
        Durations.append(0)

        for i in self.__values_range:
            if (self.__np_values[i] < Threshold):
                Durations[-1]+=1
            elif self.__np_values[i] >= Threshold and Durations[-1] > 0:
                Durations.append(0)
    
        if Durations[-1] == 0:
            del Durations[-1]
        if (len(Durations) == 0):
            return 0
        return np.mean(Durations)


    def DVFIEQR(self, sin = 2):
        if self.Q50==0:
            return 0
        return 0.217 + 0.103 * sin + 0.02 * self.Q90/self.Q50 * self.EventFreq(lambda v : v > self.Q50)[0];


    def DVPIEQR(self):
        return 0.546 + 0.02 * self.fre25() - 0.019 * self.dur3() - 0.025 * self.fre75()


    def DFFV_EQR(self, sin = 2):
        p25 = self.Q75;
        p75 = self.Q25;
        return 0.811 * self.BaseFlowIndex() + 0.058 * sin + 0.05 * self.EventFreq(lambda v : v >= p75)[0] - 0.319 - 0.0413 * self.EventFreq(lambda v : v <= p25)[0];
    
    
    def medianmin(self):
        return self.time_series.resample("A").min().median()

    def medianmax(self):
        return self.time_series.resample("A").max().median()

    def medmin(self):
        return self.medianmin()/self.Q50

    def medmax(self):
        return self.medianmax()/self.Q50

    def mamin(self):
        return self.time_series.resample("A").min().mean()/self.Q50

    def mamax(self):
        return self.time_series.resample("A").max().mean()/self.Q50

    def mamax7(self):
        return self.__mamax( 7)

    def mamax30(self):
        return self.__mamax( 30)

    def fremedmin(self):
        val =self.medmin()
        return self.EventFreq( lambda v : v < val)[0]

    def mf(self):
        return self.time_series.mean()

    def cv(self):
        return self.time_series.var()

    def q1(self):
        return self.time_series.quantile(0.01, interpolation='linear')/self.Q50

    def q10(self):
        return self.time_series.quantile(0.1, interpolation='linear')/self.Q50

    def q25(self):
        return self.time_series.quantile(0.25, interpolation='linear')/self.Q50

    def q75(self):
        return self.time_series.quantile(0.75, interpolation='linear')/self.Q50

    def q90(self):
        return self.time_series.quantile(0.90, interpolation='linear')/self.Q50

    def durmedmin(self):
        val =medmin(self.time_series)
        return Duration(self.time_series, val)

    def dur75(self):
        val =self.Q75()
        return self.DurationBelow( val)

    def dur1(self):
        val =self.Q50
        return self.duration( val)

    def dur3(self):
        val =self.Q50
        return self.duration( 3*val)

    def dur7(self):
        val =self.Q50
        return self.duration( 7*val)

    def dur25(self):
        val =Q25(self.time_series)
        return Duration(self.time_series, val)

    def pea1(self):
        val = self.Q50
        events = self.values_above( val)
        return np.mean([np.mean(t) for t in events])/val

    def pea3(self):
        val =self.Q50
        return np.mean([t for t in self.time_series if t>3*val])/val

    def pea7(self):
        val =self.Q50
        return np.mean([t for t in self.time_series if t>7*val])/val

    def pea25(self):
        val =Q25(self.time_series)
        return np.mean([t for t in self.time_series if t>val])/val

    def norises(self):
        toreturn =0
        for i in range(0,len(self.time_series)-1):
            if(self.time_series.values[i]<self.time_series.values[i+1]):
                toreturn+=1
        return toreturn/len(self.time_series)

    def nofalls(self):
        toreturn =0
        for i in range(0,len(self.time_series)-1):
            if(self.time_series.values[i]>self.time_series.values[i+1]):
                toreturn+=1
        return toreturn/len(self.time_series)


    def EQR_DFFV(self):
        return 0.38 - 0.01 * self.dur75() + 0.12 * self.pea1() - 0.1*self.dur7()

    def EQR_DFFV2(self):
        return 2.85 +1.531*self.mamin() - 0.031* self.fremedmin() - 9.206*self.norises()


    def __mamax(self, windowsize):
        maximums=[]
        for year in range(self.time_series.index[0].year,self.time_series.index[-1].year+1):
            maximums.append(self.time_series[str(year)].rolling(windowsize, win_type='triang').sum().max())
        maximums =[max for max in maximums if not math.isnan(max)]
        return np.mean(maximums)/self.Q50


    """
    /// Calculates the Base flow Index(BFI) from http://nora.nerc.ac.uk/6050/1/IH_108.pdf
    /// The self.TimeSeries must contain daily values
    """
    def BaseFlowIndex(self):
        if len(self.time_series) == 0:
            return -1;

        nDays = 5

        localcount = 0
        nDaysMin = []
        locallist = [];
        for i in self.__values_range:
            if (localcount == nDays):
                min1 = min(locallist)
                nDaysMin.append([ i - nDays + locallist.index(min1) , min1])
                locallist = []
                localcount = 0
            locallist.append(self.__np_values[i]);
            localcount+=1
    

        Ordinates = []
        nDaysMin09 =[l[1]*0.9 for l in nDaysMin]
        for i in range(0, len(nDaysMin) - 2):
            centralvalue = nDaysMin09[i + 1];
            if centralvalue < nDaysMin[i][1]  and centralvalue < nDaysMin[i + 2][1]:
                Ordinates.append(nDaysMin[i + 1])
    

        interpolated = []

        if len(Ordinates) < 2:
            return 0

        i1 = Ordinates[0][0]
        i2 = Ordinates[-1][0]

        interpolated = np.interp(range(i1, i2),[o[0] for o in Ordinates],[o[1] for o in Ordinates])


        Vb = sum(interpolated)
        vals = self.__np_values[i1:i2 - i1+1]
        Va = sum(vals)


        #Why?
##        vv = 0;
##        for i in range(1,len(vals)):
##            vv += (vals[i] + vals[i -1]) / 2;
    
        vvb = 0;
        for i in range (0,len(Ordinates)-1):
            vvb += (Ordinates[i][1] + Ordinates[i + 1][1]) / 2 * (Ordinates[i + 1][0] - Ordinates[i][0]);

        return Vb / Va;

class GroundWaterLayer(object):
    def __init__(self, gvk, dkmlag,dkmomr):
        self.feature = gvk
        self.insidecoords =[]
        self.holes=[]
        self.dkmlag=dkmlag
        self.dkmomr = dkmomr
        if len(gvk.parts)>1:
            self.path = path.Path(gvk.points[0:gvk.parts[1]])
            for i in range(1, len(gvk.parts)-1):
                self.holes.append(path.Path(gvk.points[gvk.parts[i]:gvk.parts[i+1]]))
            self.holes.append(path.Path(gvk.points[gvk.parts[-1]:]))
        else:
            self.path = path.Path(gvk.points)

        self.sum=0
        
    def plot(self):
        self.outeredge()
        plt.plot([p.x for p in self.insidecoords],[p.y for p in self.insidecoords],'x')
        plt.plot(self.path.vertices[:,0],self.path.vertices[:,1])
        for hole in self.holes:
            plt.plot(hole.vertices[:,0],hole.vertices[:,1])
            
        #Directions
        plt.plot([p.x for p in self.south],[p.y-250 for p in self.south],'^')
        plt.plot([p.x+250 for p in self.east],[p.y for p in self.east],'<')
        plt.plot([p.x-250 for p in self.west],[p.y for p in self.west],'>')
        plt.plot([p.x for p in self.north],[p.y+250 for p in self.north],'v')
        
        plt.gca().axis('equal')
    

    def remove_holes(self):
        toremove=[]
        for hole in self.holes:
            for p in self.insidecoords:
                if hole.contains_point((p.x,p.y)):
                    toremove.append(p)
        
        for p in toremove:
            self.insidecoords.remove(p)
        
    def outeredge(self):
        self.north=[]
        self.south=[]
        self.east =[]
        self.west=[]
        dict_coord={}
        dict_coord_row={}
        for c in self.insidecoords:
            if not c.col in dict_coord:
                dict_coord[c.col]=[]
            dict_coord[c.col].append(c.row)
            if not c.row in dict_coord_row:
                dict_coord_row[c.row]=[]
            dict_coord_row[c.row].append(c.col)
        for c in self.insidecoords:
            if not c.row+1 in dict_coord[c.col]:
                self.north.append(c)
            if not c.row-1 in dict_coord[c.col]:
                self.south.append(c)
            if not c.col+1 in dict_coord_row[c.row]:
                self.east.append(c)
            if not c.col-1 in dict_coord_row[c.row]:
                self.west.append(c)




                
class GroundWaterBody(object):
    def __init__(self, name):
        self.name = name
        self.GroundWaterLayers = []
    
    def sort_layers(self):
        #remove layers without dfs-points
        self.GroundWaterLayers = [b for b in self.GroundWaterLayers if len(b.insidecoords)>0]
        
        if len(self.GroundWaterLayers)>0:
            self.GroundWaterLayers.sort(key=lambda gwl:gwl.insidecoords[0].layer, reverse=True)            
            self.points_from_above=self.__project()
            self.GroundWaterLayers.sort(key=lambda gwl:gwl.insidecoords[0].layer)            
            self.points_from_below=self.__project()
        else:
            self.points_from_above=[]
            self.points_from_below=[]

 
    def __project(self):            
        points_from_above=[]
        points_from_above.extend(self.GroundWaterLayers[0].insidecoords)
    
        if len(self.GroundWaterLayers)>1:
            dict_coord={}
            for c in points_from_above:
                if not c.col in dict_coord:
                    dict_coord[c.col]=[]
                dict_coord[c.col].append(c.row)
                  
            for i in range(1,len(self.GroundWaterLayers)):
                for c in self.GroundWaterLayers[i].insidecoords:
                    if not c.col in dict_coord:
                        dict_coord[c.col]=[]
                    if not c.row in dict_coord[c.col]:
                        points_from_above.append(c)
                        dict_coord[c.col].append(c.row)
        return points_from_above
            

        
    def plot(self):
        from mpl_toolkits.mplot3d import Axes3D
        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')
        for gwb in self.GroundWaterLayers:
            xs=[]
            ys=[]
            zs=[]
            for p in gwb.insidecoords:
                xs.append(p.x)
                ys.append(p.y)
                zs.append(p.layer)
                ax.scatter(xs, ys, zs)

        ax.set_xlabel('X Label')
        ax.set_ylabel('Y Label')
        ax.set_zlabel('Z Label')
        
        plt.show()

Functions

def lazy_property(fn)

Decorator that makes a property lazy-evaluated.

Source code
def lazy_property(fn):
    '''Decorator that makes a property lazy-evaluated.
    '''
    attr_name = '_lazy_' + fn.__name__

    @property
    def _lazy_property(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)
    return _lazy_property

Classes

class GroundWaterBody (name)
Source code
class GroundWaterBody(object):
    def __init__(self, name):
        self.name = name
        self.GroundWaterLayers = []
    
    def sort_layers(self):
        #remove layers without dfs-points
        self.GroundWaterLayers = [b for b in self.GroundWaterLayers if len(b.insidecoords)>0]
        
        if len(self.GroundWaterLayers)>0:
            self.GroundWaterLayers.sort(key=lambda gwl:gwl.insidecoords[0].layer, reverse=True)            
            self.points_from_above=self.__project()
            self.GroundWaterLayers.sort(key=lambda gwl:gwl.insidecoords[0].layer)            
            self.points_from_below=self.__project()
        else:
            self.points_from_above=[]
            self.points_from_below=[]

 
    def __project(self):            
        points_from_above=[]
        points_from_above.extend(self.GroundWaterLayers[0].insidecoords)
    
        if len(self.GroundWaterLayers)>1:
            dict_coord={}
            for c in points_from_above:
                if not c.col in dict_coord:
                    dict_coord[c.col]=[]
                dict_coord[c.col].append(c.row)
                  
            for i in range(1,len(self.GroundWaterLayers)):
                for c in self.GroundWaterLayers[i].insidecoords:
                    if not c.col in dict_coord:
                        dict_coord[c.col]=[]
                    if not c.row in dict_coord[c.col]:
                        points_from_above.append(c)
                        dict_coord[c.col].append(c.row)
        return points_from_above
            

        
    def plot(self):
        from mpl_toolkits.mplot3d import Axes3D
        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')
        for gwb in self.GroundWaterLayers:
            xs=[]
            ys=[]
            zs=[]
            for p in gwb.insidecoords:
                xs.append(p.x)
                ys.append(p.y)
                zs.append(p.layer)
                ax.scatter(xs, ys, zs)

        ax.set_xlabel('X Label')
        ax.set_ylabel('Y Label')
        ax.set_zlabel('Z Label')
        
        plt.show()

Methods

def plot(self)
Source code
def plot(self):
    from mpl_toolkits.mplot3d import Axes3D
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    for gwb in self.GroundWaterLayers:
        xs=[]
        ys=[]
        zs=[]
        for p in gwb.insidecoords:
            xs.append(p.x)
            ys.append(p.y)
            zs.append(p.layer)
            ax.scatter(xs, ys, zs)

    ax.set_xlabel('X Label')
    ax.set_ylabel('Y Label')
    ax.set_zlabel('Z Label')
    
    plt.show()
def sort_layers(self)
Source code
def sort_layers(self):
    #remove layers without dfs-points
    self.GroundWaterLayers = [b for b in self.GroundWaterLayers if len(b.insidecoords)>0]
    
    if len(self.GroundWaterLayers)>0:
        self.GroundWaterLayers.sort(key=lambda gwl:gwl.insidecoords[0].layer, reverse=True)            
        self.points_from_above=self.__project()
        self.GroundWaterLayers.sort(key=lambda gwl:gwl.insidecoords[0].layer)            
        self.points_from_below=self.__project()
    else:
        self.points_from_above=[]
        self.points_from_below=[]
class GroundWaterLayer (gvk, dkmlag, dkmomr)
Source code
class GroundWaterLayer(object):
    def __init__(self, gvk, dkmlag,dkmomr):
        self.feature = gvk
        self.insidecoords =[]
        self.holes=[]
        self.dkmlag=dkmlag
        self.dkmomr = dkmomr
        if len(gvk.parts)>1:
            self.path = path.Path(gvk.points[0:gvk.parts[1]])
            for i in range(1, len(gvk.parts)-1):
                self.holes.append(path.Path(gvk.points[gvk.parts[i]:gvk.parts[i+1]]))
            self.holes.append(path.Path(gvk.points[gvk.parts[-1]:]))
        else:
            self.path = path.Path(gvk.points)

        self.sum=0
        
    def plot(self):
        self.outeredge()
        plt.plot([p.x for p in self.insidecoords],[p.y for p in self.insidecoords],'x')
        plt.plot(self.path.vertices[:,0],self.path.vertices[:,1])
        for hole in self.holes:
            plt.plot(hole.vertices[:,0],hole.vertices[:,1])
            
        #Directions
        plt.plot([p.x for p in self.south],[p.y-250 for p in self.south],'^')
        plt.plot([p.x+250 for p in self.east],[p.y for p in self.east],'<')
        plt.plot([p.x-250 for p in self.west],[p.y for p in self.west],'>')
        plt.plot([p.x for p in self.north],[p.y+250 for p in self.north],'v')
        
        plt.gca().axis('equal')
    

    def remove_holes(self):
        toremove=[]
        for hole in self.holes:
            for p in self.insidecoords:
                if hole.contains_point((p.x,p.y)):
                    toremove.append(p)
        
        for p in toremove:
            self.insidecoords.remove(p)
        
    def outeredge(self):
        self.north=[]
        self.south=[]
        self.east =[]
        self.west=[]
        dict_coord={}
        dict_coord_row={}
        for c in self.insidecoords:
            if not c.col in dict_coord:
                dict_coord[c.col]=[]
            dict_coord[c.col].append(c.row)
            if not c.row in dict_coord_row:
                dict_coord_row[c.row]=[]
            dict_coord_row[c.row].append(c.col)
        for c in self.insidecoords:
            if not c.row+1 in dict_coord[c.col]:
                self.north.append(c)
            if not c.row-1 in dict_coord[c.col]:
                self.south.append(c)
            if not c.col+1 in dict_coord_row[c.row]:
                self.east.append(c)
            if not c.col-1 in dict_coord_row[c.row]:
                self.west.append(c)

Methods

def outeredge(self)
Source code
def outeredge(self):
    self.north=[]
    self.south=[]
    self.east =[]
    self.west=[]
    dict_coord={}
    dict_coord_row={}
    for c in self.insidecoords:
        if not c.col in dict_coord:
            dict_coord[c.col]=[]
        dict_coord[c.col].append(c.row)
        if not c.row in dict_coord_row:
            dict_coord_row[c.row]=[]
        dict_coord_row[c.row].append(c.col)
    for c in self.insidecoords:
        if not c.row+1 in dict_coord[c.col]:
            self.north.append(c)
        if not c.row-1 in dict_coord[c.col]:
            self.south.append(c)
        if not c.col+1 in dict_coord_row[c.row]:
            self.east.append(c)
        if not c.col-1 in dict_coord_row[c.row]:
            self.west.append(c)
def plot(self)
Source code
def plot(self):
    self.outeredge()
    plt.plot([p.x for p in self.insidecoords],[p.y for p in self.insidecoords],'x')
    plt.plot(self.path.vertices[:,0],self.path.vertices[:,1])
    for hole in self.holes:
        plt.plot(hole.vertices[:,0],hole.vertices[:,1])
        
    #Directions
    plt.plot([p.x for p in self.south],[p.y-250 for p in self.south],'^')
    plt.plot([p.x+250 for p in self.east],[p.y for p in self.east],'<')
    plt.plot([p.x-250 for p in self.west],[p.y for p in self.west],'>')
    plt.plot([p.x for p in self.north],[p.y+250 for p in self.north],'v')
    
    plt.gca().axis('equal')
def remove_holes(self)
Source code
def remove_holes(self):
    toremove=[]
    for hole in self.holes:
        for p in self.insidecoords:
            if hole.contains_point((p.x,p.y)):
                toremove.append(p)
    
    for p in toremove:
        self.insidecoords.remove(p)
class HydroTs (TimeSeries)
Source code
class HydroTs(object):
    def __init__(self, TimeSeries):
        self.time_series= TimeSeries
        self.__np_values = TimeSeries.values
        self.__np_index = TimeSeries.index.values
        self.__years = TimeSeries.index.year
        self.__values_range = range(len(self.__np_values))


    def EventFreq(self, Comparer):
        EventCount = {}
        EventLength = {}
        currentyear = self.__years[0]
        NumberOfEvents = 0;
        CurrentEventLengt = []
        InEvent = False

        for i in self.__values_range:
            #We got a new year. Store data
            if (self.__years[i] != currentyear):
                EventCount[currentyear]= NumberOfEvents
                EventLength[currentyear]= CurrentEventLengt
                currentyear = self.__years[i];
                NumberOfEvents = 0;
                CurrentEventLengt = [];
                CurrentEventLengt.append(0);
    
            if (Comparer(self.__np_values[i])):
            #This is a new event
                if not InEvent:
                    NumberOfEvents+=1
                    InEvent = True
                    CurrentEventLengt.append(0)
            #Increase number of days in event
                CurrentEventLengt[len(CurrentEventLengt) - 1]+=1
            else:
                InEvent = False
    
        if not currentyear in EventCount:
            EventCount[currentyear] = NumberOfEvents
            EventLength[currentyear] = CurrentEventLengt
        #First average the length on years, then average the averages
        #Average length on years:
        ayear=np.mean([v for k,v in EventCount.items()])
        lengthMoreThenZero=[v for k,v in EventLength.items() if len(v)>0]
        if len(lengthMoreThenZero)== 0:
            return [ayear, 0]
        return [ayear, np.mean([np.mean(v) for v in lengthMoreThenZero])]   

    @lazy_property
    def Q95(self):
        self.__set_percentiles()
        return self._lazy_Q95

    @lazy_property
    def Q90(self):
        self.__set_percentiles()
        return self._lazy_Q90


    @lazy_property
    def Q75(self):
        self.__set_percentiles()
        return self._lazy_Q75

    @lazy_property
    def Q50(self):
        self.__set_percentiles()
#        Q50 =self.time_series.median()
        return self._lazy_Q50

    @lazy_property
    def Q25(self):
        self.__set_percentiles()
        return self._lazy_Q25


    def __set_percentiles(self):
        q=[0.05, 0.1, 0.25,0.5,0.75]
        p= np.quantile(self.__np_values, q, interpolation ='linear')
        self._lazy_Q95 =p[0]
        self._lazy_Q90 =p[1]
        self._lazy_Q75=p[2]
        self._lazy_Q50=p[3]
        self._lazy_Q25=p[4]

    def fre25(self):
        return self.EventFreq(lambda v : v > self.Q25)[0]

    def fre75(self):
        return self.EventFreq(lambda v : v > self.Q75)[0]

    def fre1(self):
        return self.EventFreq(lambda v : v > self.Q50)[0]

    def fre3(self):
        return self.EventFreq(lambda v : v > 3*self.Q50)[0]

    def fre7(self):
        return self.EventFreq(lambda v : v > 7*self.Q50)[0]

    """
    Calculates the average number of timesteps where the value is above a threshold
    """
    def duration(self, Threshold):
        Durations = []
        Durations.append(0)

        for i in self.__values_range:
            if (self.__np_values[i] > Threshold):
                Durations[-1]+=1
            elif self.__np_values[i] <= Threshold and Durations[-1] > 0:
                Durations.append(0)
    
        if Durations[-1] == 0:
            del Durations[-1]
        if (len(Durations) == 0):
            return 0
        return np.mean(Durations)

    def values_above(self, Threshold):
        Durations = []
        Durations.append([])

        for i in self.__values_range:
            if (self.__np_values[i] > Threshold):
                Durations[-1].append(self.__np_values[i])
            elif self.__np_values[i] <= Threshold and len(Durations[-1]) > 0:
                Durations.append([])
    
        if len(Durations[-1]) == 0:
            del Durations[-1]
        return Durations




    """
    Calculates the average number of timesteps where the value is below a threshold
    """
    def DurationBelow(self, Threshold):
        Durations = []
        Durations.append(0)

        for i in self.__values_range:
            if (self.__np_values[i] < Threshold):
                Durations[-1]+=1
            elif self.__np_values[i] >= Threshold and Durations[-1] > 0:
                Durations.append(0)
    
        if Durations[-1] == 0:
            del Durations[-1]
        if (len(Durations) == 0):
            return 0
        return np.mean(Durations)


    def DVFIEQR(self, sin = 2):
        if self.Q50==0:
            return 0
        return 0.217 + 0.103 * sin + 0.02 * self.Q90/self.Q50 * self.EventFreq(lambda v : v > self.Q50)[0];


    def DVPIEQR(self):
        return 0.546 + 0.02 * self.fre25() - 0.019 * self.dur3() - 0.025 * self.fre75()


    def DFFV_EQR(self, sin = 2):
        p25 = self.Q75;
        p75 = self.Q25;
        return 0.811 * self.BaseFlowIndex() + 0.058 * sin + 0.05 * self.EventFreq(lambda v : v >= p75)[0] - 0.319 - 0.0413 * self.EventFreq(lambda v : v <= p25)[0];
    
    
    def medianmin(self):
        return self.time_series.resample("A").min().median()

    def medianmax(self):
        return self.time_series.resample("A").max().median()

    def medmin(self):
        return self.medianmin()/self.Q50

    def medmax(self):
        return self.medianmax()/self.Q50

    def mamin(self):
        return self.time_series.resample("A").min().mean()/self.Q50

    def mamax(self):
        return self.time_series.resample("A").max().mean()/self.Q50

    def mamax7(self):
        return self.__mamax( 7)

    def mamax30(self):
        return self.__mamax( 30)

    def fremedmin(self):
        val =self.medmin()
        return self.EventFreq( lambda v : v < val)[0]

    def mf(self):
        return self.time_series.mean()

    def cv(self):
        return self.time_series.var()

    def q1(self):
        return self.time_series.quantile(0.01, interpolation='linear')/self.Q50

    def q10(self):
        return self.time_series.quantile(0.1, interpolation='linear')/self.Q50

    def q25(self):
        return self.time_series.quantile(0.25, interpolation='linear')/self.Q50

    def q75(self):
        return self.time_series.quantile(0.75, interpolation='linear')/self.Q50

    def q90(self):
        return self.time_series.quantile(0.90, interpolation='linear')/self.Q50

    def durmedmin(self):
        val =medmin(self.time_series)
        return Duration(self.time_series, val)

    def dur75(self):
        val =self.Q75()
        return self.DurationBelow( val)

    def dur1(self):
        val =self.Q50
        return self.duration( val)

    def dur3(self):
        val =self.Q50
        return self.duration( 3*val)

    def dur7(self):
        val =self.Q50
        return self.duration( 7*val)

    def dur25(self):
        val =Q25(self.time_series)
        return Duration(self.time_series, val)

    def pea1(self):
        val = self.Q50
        events = self.values_above( val)
        return np.mean([np.mean(t) for t in events])/val

    def pea3(self):
        val =self.Q50
        return np.mean([t for t in self.time_series if t>3*val])/val

    def pea7(self):
        val =self.Q50
        return np.mean([t for t in self.time_series if t>7*val])/val

    def pea25(self):
        val =Q25(self.time_series)
        return np.mean([t for t in self.time_series if t>val])/val

    def norises(self):
        toreturn =0
        for i in range(0,len(self.time_series)-1):
            if(self.time_series.values[i]<self.time_series.values[i+1]):
                toreturn+=1
        return toreturn/len(self.time_series)

    def nofalls(self):
        toreturn =0
        for i in range(0,len(self.time_series)-1):
            if(self.time_series.values[i]>self.time_series.values[i+1]):
                toreturn+=1
        return toreturn/len(self.time_series)


    def EQR_DFFV(self):
        return 0.38 - 0.01 * self.dur75() + 0.12 * self.pea1() - 0.1*self.dur7()

    def EQR_DFFV2(self):
        return 2.85 +1.531*self.mamin() - 0.031* self.fremedmin() - 9.206*self.norises()


    def __mamax(self, windowsize):
        maximums=[]
        for year in range(self.time_series.index[0].year,self.time_series.index[-1].year+1):
            maximums.append(self.time_series[str(year)].rolling(windowsize, win_type='triang').sum().max())
        maximums =[max for max in maximums if not math.isnan(max)]
        return np.mean(maximums)/self.Q50


    """
    /// Calculates the Base flow Index(BFI) from http://nora.nerc.ac.uk/6050/1/IH_108.pdf
    /// The self.TimeSeries must contain daily values
    """
    def BaseFlowIndex(self):
        if len(self.time_series) == 0:
            return -1;

        nDays = 5

        localcount = 0
        nDaysMin = []
        locallist = [];
        for i in self.__values_range:
            if (localcount == nDays):
                min1 = min(locallist)
                nDaysMin.append([ i - nDays + locallist.index(min1) , min1])
                locallist = []
                localcount = 0
            locallist.append(self.__np_values[i]);
            localcount+=1
    

        Ordinates = []
        nDaysMin09 =[l[1]*0.9 for l in nDaysMin]
        for i in range(0, len(nDaysMin) - 2):
            centralvalue = nDaysMin09[i + 1];
            if centralvalue < nDaysMin[i][1]  and centralvalue < nDaysMin[i + 2][1]:
                Ordinates.append(nDaysMin[i + 1])
    

        interpolated = []

        if len(Ordinates) < 2:
            return 0

        i1 = Ordinates[0][0]
        i2 = Ordinates[-1][0]

        interpolated = np.interp(range(i1, i2),[o[0] for o in Ordinates],[o[1] for o in Ordinates])


        Vb = sum(interpolated)
        vals = self.__np_values[i1:i2 - i1+1]
        Va = sum(vals)


        #Why?
##        vv = 0;
##        for i in range(1,len(vals)):
##            vv += (vals[i] + vals[i -1]) / 2;
    
        vvb = 0;
        for i in range (0,len(Ordinates)-1):
            vvb += (Ordinates[i][1] + Ordinates[i + 1][1]) / 2 * (Ordinates[i + 1][0] - Ordinates[i][0]);

        return Vb / Va;

Instance variables

var Q25
Source code
@property
def _lazy_property(self):
    if not hasattr(self, attr_name):
        setattr(self, attr_name, fn(self))
    return getattr(self, attr_name)
var Q50
Source code
@property
def _lazy_property(self):
    if not hasattr(self, attr_name):
        setattr(self, attr_name, fn(self))
    return getattr(self, attr_name)
var Q75
Source code
@property
def _lazy_property(self):
    if not hasattr(self, attr_name):
        setattr(self, attr_name, fn(self))
    return getattr(self, attr_name)
var Q90
Source code
@property
def _lazy_property(self):
    if not hasattr(self, attr_name):
        setattr(self, attr_name, fn(self))
    return getattr(self, attr_name)
var Q95
Source code
@property
def _lazy_property(self):
    if not hasattr(self, attr_name):
        setattr(self, attr_name, fn(self))
    return getattr(self, attr_name)

Methods

def BaseFlowIndex(self)
Source code
    def BaseFlowIndex(self):
        if len(self.time_series) == 0:
            return -1;

        nDays = 5

        localcount = 0
        nDaysMin = []
        locallist = [];
        for i in self.__values_range:
            if (localcount == nDays):
                min1 = min(locallist)
                nDaysMin.append([ i - nDays + locallist.index(min1) , min1])
                locallist = []
                localcount = 0
            locallist.append(self.__np_values[i]);
            localcount+=1
    

        Ordinates = []
        nDaysMin09 =[l[1]*0.9 for l in nDaysMin]
        for i in range(0, len(nDaysMin) - 2):
            centralvalue = nDaysMin09[i + 1];
            if centralvalue < nDaysMin[i][1]  and centralvalue < nDaysMin[i + 2][1]:
                Ordinates.append(nDaysMin[i + 1])
    

        interpolated = []

        if len(Ordinates) < 2:
            return 0

        i1 = Ordinates[0][0]
        i2 = Ordinates[-1][0]

        interpolated = np.interp(range(i1, i2),[o[0] for o in Ordinates],[o[1] for o in Ordinates])


        Vb = sum(interpolated)
        vals = self.__np_values[i1:i2 - i1+1]
        Va = sum(vals)


        #Why?
##        vv = 0;
##        for i in range(1,len(vals)):
##            vv += (vals[i] + vals[i -1]) / 2;
    
        vvb = 0;
        for i in range (0,len(Ordinates)-1):
            vvb += (Ordinates[i][1] + Ordinates[i + 1][1]) / 2 * (Ordinates[i + 1][0] - Ordinates[i][0]);

        return Vb / Va;
def DFFV_EQR(self, sin=2)
Source code
def DFFV_EQR(self, sin = 2):
    p25 = self.Q75;
    p75 = self.Q25;
    return 0.811 * self.BaseFlowIndex() + 0.058 * sin + 0.05 * self.EventFreq(lambda v : v >= p75)[0] - 0.319 - 0.0413 * self.EventFreq(lambda v : v <= p25)[0];
def DVFIEQR(self, sin=2)
Source code
def DVFIEQR(self, sin = 2):
    if self.Q50==0:
        return 0
    return 0.217 + 0.103 * sin + 0.02 * self.Q90/self.Q50 * self.EventFreq(lambda v : v > self.Q50)[0];
def DVPIEQR(self)
Source code
def DVPIEQR(self):
    return 0.546 + 0.02 * self.fre25() - 0.019 * self.dur3() - 0.025 * self.fre75()
def DurationBelow(self, Threshold)
Source code
def DurationBelow(self, Threshold):
    Durations = []
    Durations.append(0)

    for i in self.__values_range:
        if (self.__np_values[i] < Threshold):
            Durations[-1]+=1
        elif self.__np_values[i] >= Threshold and Durations[-1] > 0:
            Durations.append(0)

    if Durations[-1] == 0:
        del Durations[-1]
    if (len(Durations) == 0):
        return 0
    return np.mean(Durations)
def EQR_DFFV(self)
Source code
def EQR_DFFV(self):
    return 0.38 - 0.01 * self.dur75() + 0.12 * self.pea1() - 0.1*self.dur7()
def EQR_DFFV2(self)
Source code
def EQR_DFFV2(self):
    return 2.85 +1.531*self.mamin() - 0.031* self.fremedmin() - 9.206*self.norises()
def EventFreq(self, Comparer)
Source code
def EventFreq(self, Comparer):
    EventCount = {}
    EventLength = {}
    currentyear = self.__years[0]
    NumberOfEvents = 0;
    CurrentEventLengt = []
    InEvent = False

    for i in self.__values_range:
        #We got a new year. Store data
        if (self.__years[i] != currentyear):
            EventCount[currentyear]= NumberOfEvents
            EventLength[currentyear]= CurrentEventLengt
            currentyear = self.__years[i];
            NumberOfEvents = 0;
            CurrentEventLengt = [];
            CurrentEventLengt.append(0);

        if (Comparer(self.__np_values[i])):
        #This is a new event
            if not InEvent:
                NumberOfEvents+=1
                InEvent = True
                CurrentEventLengt.append(0)
        #Increase number of days in event
            CurrentEventLengt[len(CurrentEventLengt) - 1]+=1
        else:
            InEvent = False

    if not currentyear in EventCount:
        EventCount[currentyear] = NumberOfEvents
        EventLength[currentyear] = CurrentEventLengt
    #First average the length on years, then average the averages
    #Average length on years:
    ayear=np.mean([v for k,v in EventCount.items()])
    lengthMoreThenZero=[v for k,v in EventLength.items() if len(v)>0]
    if len(lengthMoreThenZero)== 0:
        return [ayear, 0]
    return [ayear, np.mean([np.mean(v) for v in lengthMoreThenZero])]   
def cv(self)
Source code
def cv(self):
    return self.time_series.var()
def dur1(self)
Source code
def dur1(self):
    val =self.Q50
    return self.duration( val)
def dur25(self)
Source code
def dur25(self):
    val =Q25(self.time_series)
    return Duration(self.time_series, val)
def dur3(self)
Source code
def dur3(self):
    val =self.Q50
    return self.duration( 3*val)
def dur7(self)
Source code
def dur7(self):
    val =self.Q50
    return self.duration( 7*val)
def dur75(self)
Source code
def dur75(self):
    val =self.Q75()
    return self.DurationBelow( val)
def duration(self, Threshold)
Source code
def duration(self, Threshold):
    Durations = []
    Durations.append(0)

    for i in self.__values_range:
        if (self.__np_values[i] > Threshold):
            Durations[-1]+=1
        elif self.__np_values[i] <= Threshold and Durations[-1] > 0:
            Durations.append(0)

    if Durations[-1] == 0:
        del Durations[-1]
    if (len(Durations) == 0):
        return 0
    return np.mean(Durations)
def durmedmin(self)
Source code
def durmedmin(self):
    val =medmin(self.time_series)
    return Duration(self.time_series, val)
def fre1(self)
Source code
def fre1(self):
    return self.EventFreq(lambda v : v > self.Q50)[0]
def fre25(self)
Source code
def fre25(self):
    return self.EventFreq(lambda v : v > self.Q25)[0]
def fre3(self)
Source code
def fre3(self):
    return self.EventFreq(lambda v : v > 3*self.Q50)[0]
def fre7(self)
Source code
def fre7(self):
    return self.EventFreq(lambda v : v > 7*self.Q50)[0]
def fre75(self)
Source code
def fre75(self):
    return self.EventFreq(lambda v : v > self.Q75)[0]
def fremedmin(self)
Source code
def fremedmin(self):
    val =self.medmin()
    return self.EventFreq( lambda v : v < val)[0]
def mamax(self)
Source code
def mamax(self):
    return self.time_series.resample("A").max().mean()/self.Q50
def mamax30(self)
Source code
def mamax30(self):
    return self.__mamax( 30)
def mamax7(self)
Source code
def mamax7(self):
    return self.__mamax( 7)
def mamin(self)
Source code
def mamin(self):
    return self.time_series.resample("A").min().mean()/self.Q50
def medianmax(self)
Source code
def medianmax(self):
    return self.time_series.resample("A").max().median()
def medianmin(self)
Source code
def medianmin(self):
    return self.time_series.resample("A").min().median()
def medmax(self)
Source code
def medmax(self):
    return self.medianmax()/self.Q50
def medmin(self)
Source code
def medmin(self):
    return self.medianmin()/self.Q50
def mf(self)
Source code
def mf(self):
    return self.time_series.mean()
def nofalls(self)
Source code
def nofalls(self):
    toreturn =0
    for i in range(0,len(self.time_series)-1):
        if(self.time_series.values[i]>self.time_series.values[i+1]):
            toreturn+=1
    return toreturn/len(self.time_series)
def norises(self)
Source code
def norises(self):
    toreturn =0
    for i in range(0,len(self.time_series)-1):
        if(self.time_series.values[i]<self.time_series.values[i+1]):
            toreturn+=1
    return toreturn/len(self.time_series)
def pea1(self)
Source code
def pea1(self):
    val = self.Q50
    events = self.values_above( val)
    return np.mean([np.mean(t) for t in events])/val
def pea25(self)
Source code
def pea25(self):
    val =Q25(self.time_series)
    return np.mean([t for t in self.time_series if t>val])/val
def pea3(self)
Source code
def pea3(self):
    val =self.Q50
    return np.mean([t for t in self.time_series if t>3*val])/val
def pea7(self)
Source code
def pea7(self):
    val =self.Q50
    return np.mean([t for t in self.time_series if t>7*val])/val
def q1(self)
Source code
def q1(self):
    return self.time_series.quantile(0.01, interpolation='linear')/self.Q50
def q10(self)
Source code
def q10(self):
    return self.time_series.quantile(0.1, interpolation='linear')/self.Q50
def q25(self)
Source code
def q25(self):
    return self.time_series.quantile(0.25, interpolation='linear')/self.Q50
def q75(self)
Source code
def q75(self):
    return self.time_series.quantile(0.75, interpolation='linear')/self.Q50
def q90(self)
Source code
def q90(self):
    return self.time_series.quantile(0.90, interpolation='linear')/self.Q50
def values_above(self, Threshold)
Source code
def values_above(self, Threshold):
    Durations = []
    Durations.append([])

    for i in self.__values_range:
        if (self.__np_values[i] > Threshold):
            Durations[-1].append(self.__np_values[i])
        elif self.__np_values[i] <= Threshold and len(Durations[-1]) > 0:
            Durations.append([])

    if len(Durations[-1]) == 0:
        del Durations[-1]
    return Durations