"""
Utils that do not need their own category.
"""
import datetime
import sys
import time
import traceback
import numpy as np
from ast import literal_eval
from termcolor import cprint
__all__ = [
'convert_string',
'convert_string_auto',
'enumerate_label',
'iqr',
'is_numeric',
'print_exception_info',
]
[docs]def convert_string(value, target_type):
"""
Convert a string to the specified data type.
Supported types: strings, numbers, tuples, lists, dicts, booleans, and None.
Args:
value (str): string to be converted.
target_type (str): string specifying the type to convert the string to (from type(a).__name__)
Returns:
converted_value: value converted to corresponding data type.
"""
# Verify input is a string.
if type(value) is not str:
raise TypeError('Invalid type {}. Expected str.'.format(type(value)))
# None.
if target_type == 'NoneType':
converted_value = None
# NaN.
elif target_type in ['int', 'float'] and value == 'nan':
converted_value = np.nan
# String.
elif target_type == 'str':
converted_value = value
# Use literal_eval, which converts strings, numbers, tuples, lists, dicts, booleans, and None.
elif target_type in ['int', 'float', 'tuple', 'list', 'dict', 'bool']:
converted_value = literal_eval(value)
elif target_type == 'type' and "<class 'numpy.float" in value:
# We have a type parameter specifying a numpy float type. We convert it to a string, e.g. 'np.float32'.
v = value.replace('<class ', '').replace('>', '').replace("'", "").replace('numpy', 'np')
converted_value = eval(v)
else:
raise NotImplementedError('Unsupported target type "{}".'.format(target_type))
return converted_value
[docs]def convert_string_auto(value):
"""
Convert a string to the corresponding data type, automatically chosing the data type.
Supported types: strings, numbers, tuples, lists, dicts, booleans, and None.
Note that if the first character of value is alphabetical, it is considered a string, unless its 'False', 'True',
or 'None'.
E.g.
a = convert_string('10')
print(a) # 10
print(type(a)) # <class 'int'>
Args:
value (str): string to be converted.
Returns:
converted_value: value converted to corresponding data type.
"""
# Verify input is a string.
if type(value) is not str:
raise TypeError('Invalid type {}. Expected str.'.format(type(value)))
# Convert empty strings and 'None' to None.
if not value or value == 'None':
# value is an empty string.
converted_value = None
# Check if value is bool.
elif value == 'False':
converted_value = False
elif value == 'True':
converted_value = True
# Check if value is a string by checking if the first character is alphabetic.
elif value[0].isalpha():
converted_value = value
# Use literal_eval, which converts strings, numbers, tuples, lists, dicts, booleans, and None.
else:
converted_value = literal_eval(value)
return converted_value
[docs]def enumerate_label(n, label='Label'):
"""
Enumerate n labels with text label.
Args:
n (int): number of labels to create.
label (str, optional): label text.
Returns:
(list of str): enumerated labels.
"""
return ['{} {}'.format(label, i+1) for i in range(n)]
[docs]def iqr(x, *args, **kwargs):
"""
Compute the inter quartile range.
Ignores nan values.
Args:
x (np.ndarray): array with the data.
*args (optional): positional arguments for np.percentile.
**kwargs (optional): keyword arguments for np.percentile.
Returns:
(float): IQR.
"""
q75, q25 = np.nanpercentile(x, [75, 25], *args, **kwargs)
return q75 - q25
[docs]def is_numeric(s):
"""
Check if a string is numeric.
Args:
s (str): string to check.
Returns:
(bool): True or False indicating whether the string is a number.
"""
try:
float(s)
return True
except ValueError:
return False
class Logger(object):
"""
Print both to output window and a log file.
From https://stackoverflow.com/questions/14906764/how-to-redirect-stdout-to-both-file-and-console-with-scripting
"""
def __init__(self, log_fp=None):
if log_fp is None:
unid = datetime.datetime.now().strftime('%y%m%d%H%M%S')
log_fp = 'logs_{}.log'.format(unid)
self.terminal = sys.stdout
self.log = open(log_fp, "a")
def write(self, message):
self.terminal.write(message)
self.log.write(message)
def flush(self):
# this flush method is needed for python 3 compatibility.
# this handles the flush command by doing nothing.
# you might want to specify some extra behavior here.
pass
[docs]def print_exception_info(e):
"""
Print error message and traceback of error e which is handled/caught.
Args:
e (Exception-derived): exception to print.
Examples:
>>> a = [1, 2, 3]
>>> try:
... idx = a.index(0)
... except ValueError as ex:
... print_exception_info(ex)
... idx = None
>>> print(idx)
None
"""
cprint('Handling {}:'.format(e.__class__.__name__), 'red')
time.sleep(0.01) # Make time for printing.
traceback.print_exc()
time.sleep(0.01) # Make time for printing.
def print_to_all(msg, *args):
"""
Print message to all outputs.
Will direct stdout to each of the element in *args and print the message.
In the end will set stdout to the original one (the one that was active when calling the function).
Args:
msg (str): message to print.
*args: all outputs to print to.
"""
for out in args:
print(msg, file=out)