"""Auxiliary functions."""
import math
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from scipy import optimize
# TODO: We should make pypricing library installable so we can import pypricing.pricing.functions.py.
# TODO: Meanwhile we have copied this file in this repository.
[docs]
def check_all_nonnumeric(arr):
"""Check if all elements in a numpy array or Python list are non-numeric.
This function tries to convert each element in the array or list to a float. If the
conversion raises a ValueError or TypeError, or if the value is nan, it means the
element is non-numeric, so the function continues to the next element. If the
conversion does not raise an exception and the value is not nan, it means the element
is numeric, so the function immediately returns False. If the function finishes
checking all elements without finding a numeric one, it returns True.
Parameters
----------
arr : numpy.ndarray or list
The array or list to check.
Returns
-------
bool
True if all elements are non-numeric, False otherwise.
"""
for i in arr:
try:
val = float(i)
if not math.isnan(val):
return False
except (ValueError, TypeError):
continue
return True
[docs]
def find_root(func, x0, interval, tolerance=10**-8, fprime=None):
"""Find the root of a given function, using several methods.
Each method is tried in turn until one succeeds.
If none succeeds, we plot the function in interval.
Parameters
----------
func : callable
The function for which the root is to be computed.
x0 : float
Initial guess for the root.
interval : list
Interval [a,b] for ridder, bisecction and brentq.
tolerance : float
If func(solution)>tolerance an exception is raised.
fprime : callable, optional
The derivative of the function. If not provided, the Newton method will use the secant method.
Returns
-------
float
The root found by the successful method (unless all methods failed).
"""
methods = [
("fixed_point", optimize.fixed_point),
("newton (Secant)", optimize.newton),
("newton (Newton-Raphson)", optimize.newton),
("bisection", optimize.bisect),
("brentq", optimize.brentq),
("ridder", optimize.ridder),
]
root = None
for name, method in methods:
try:
if name == "fixed_point":
root = method(lambda x: x - func(x), x0)
elif name == "newton (Secant)":
root = method(func, x0, fprime=None)
elif name == "newton (Newton-Raphson)":
root = method(func, x0, fprime=fprime)
else:
root = method(func, interval[0], interval[1])
# print(f"Method {name} succeeded with root {root}")
break # if method succeeded, stop trying the rest
except Exception:
pass
if root is None:
x_vals = np.linspace(interval[0], interval[1], 200)
y_vals = [
func(x) for x in x_vals
] # Note that this code snippet is intentionally not vectorized.
fig = go.Figure(data=go.Scatter(x=x_vals, y=y_vals))
fig.update_layout(title="Plot of func", xaxis_title="x", yaxis_title="y")
fig.show()
raise Exception("All methods failed")
else:
if np.abs(func(root)) > tolerance: # Maybe another tolerance can be chosen.
raise Exception("The numerical error is too large.")
else:
return root
[docs]
def contains_word(string_list, word):
"""Check if strings in the given list contain the specified word. Words in each string are separated by underscores.
Parameters
----------
string_list : list of str
The list of strings where each string has words separated by underscores.
word : str
The word to search for within the strings.
Returns
-------
list of str
A list of strings from the input `string_list` that contain the specified `word`.
Examples
--------
>>> contains_word(['word1_word2', 'word3_word4', 'word2_word5'], 'word2')
['word1_word2', 'word2_word5']
"""
return [s for s in string_list if word in s.split("_")]