# Source code for pygimli.utils.base

# -*- coding: utf-8 -*-
"""Pygimli base functions.

Some needs to be sorted. Need to fit nameing conventions!
"""
import os
import time

import numpy as np
from math import sqrt

import pygimli as pg

[docs]def rms(v, axis=None):
"""Compute the root mean square."""
### abs for complex values
return np.sqrt(np.mean(np.abs(v)**2, axis))

[docs]def nanrms(v, axis=None):
"""Compute the root mean square excluding nan values."""
### abs for complex values
return np.sqrt(np.nanmean(np.abs(v)**2, axis))

[docs]def rmsWithErr(a, b, err, errtol=1):
"""Compute (abs-)root mean square of values with error above a threshold"""
fi = pg.find(err < errtol)
return rms(a[fi] - b[fi])

def rrmsWithErr(a, b, err, errtol=1):
"""Compute root mean square of values with error above a threshold"""
fi = pg.find(err < errtol)
return rms((a[fi]-b[fi])/a[fi])

[docs]def gmat2numpy(mat):
"""Convert pygimli matrix into numpy.array.

TODO implement correct rval
"""
nmat = np.zeros((len(mat), len(mat[0])))
for i, row in enumerate(mat):
nmat[i] = row
return nmat

[docs]def numpy2gmat(nmat):
"""Convert numpy.array into pygimli RMatrix.

TODO implement correct rval
"""
gmat = pg.RMatrix()
for arr in nmat:
gmat.push_back(arr)  # pg.asvector(arr))
return gmat

[docs]def rndig(a, ndig=3):
"""Round float using a number of counting digits."""
if np.abs(a) < 1e-4:
return a
else:
return np.around(a, ndig - int(np.ceil(np.log10(np.abs(a) + 1e-4))))

[docs]def num2str(a, fmtstr='%g'):
"""List of strings (deprecated, for backward-compatibility)."""
return [fmtstr % rndig(ai) for ai in a]

[docs]def inthist(a, vals, bins=None, islog=False):
"""Return point of integral (cumulative) histogram, e.g.
inthist(a, [25, 50, 75]) provides quartiles and median of an array"""
if bins is None:
bins = int(np.min((np.round(len(a) / 20), 10)))

if islog:
hists, edges = np.histogram(np.log(a), bins=bins)
else:
hists, edges = np.histogram(a, bins=bins)

cums = np.cumsum(np.hstack((0., hists))) / np.sum(hists) * 100.
out = np.interp(vals, cums, edges)
if islog:
return np.exp(out)
else:
return out

[docs]def interperc(a, trimval=3.0, islog=False, bins=None):
"""Return symmetric interpercentiles for alpha-trim outliers, e.g.
interperc(a, 3) returns range of inner 94% (useful for colorscales)."""
return inthist(a, np.array([trimval, 100. - trimval]),
bins=bins, islog=islog)

[docs]def interpExtrap(x, xp, yp):
"""numpy.interp interpolation function extended by linear extrapolation."""
y = np.interp(x, xp, yp)
y = np.where(x < xp[0], yp[0]+(x-xp[0])*(yp[0]-yp[1])/(xp[0]-xp[1]), y)
return np.where(x > xp[-1], yp[-1]+(x-xp[-1])*(yp[-1]-yp[-2]) /
(xp[-1]-xp[-2]), y)

[docs]def saveResult(fname, data, rrms=None, chi2=None, mode='w'):
"""Save rms/chi2 results into filename."""
with open(fname, mode) as f:
np.savetxt(f, data)
if rrms is not None:
f.write('\nrrms:{}\n'.format(rrms))
if chi2 is not None:
f.write('\nchi2:{}\n'.format(chi2))

[docs]def getSavePath(folder=None, subfolder='', now=None):
"""TODO."""
if folder is None:
path = createResultFolder(subfolder, now)
else:
path = createfolders([folder, subfolder])
return path

[docs]def createResultFolder(subfolder, now=None):
"""Create a result Folder."""
result = createDateTimeString(now)
return createfolders(['./', result, subfolder])

[docs]def createDateTimeString(now=None):
"""Return datetime as string (e.g. for saving results)."""
if now is None:
now = time.localtime()
return str(now.tm_year) + str(now.tm_mon).zfill(2) + \
str(now.tm_mday).zfill(2) + '-' + \
str(now.tm_hour).zfill(2) + '.' + \
str(now.tm_min).zfill(2)

[docs]def createfolders(foldername_list):
"""Create the folder structure specified by the list."""
path = ''

for s in foldername_list:
if s != '/':
path = path + s + '/'

try:
os.makedirs(path)
except OSError as e:
if os.path.exists(path):