__version__ = "1.0"

import numpy as np
from numpy import array as arr
from matplotlib.cm import get_cmap
from pandas import DataFrame


def prefix(num):
    """
    Convert number to nearest numbers with SI prefixes.
    :param num: the number to convert
    """
    # determine which range it lies in, r1/r2 means reduction 1 or reduction 2
    divisors = [1e-24 * pow(10, 3 * x) for x in range(17)]
    prefixes = list(reversed(['Yotta (Y)', 'Zetta (Z)', 'Exa (E)', 'Peta (P)', 'Tera (T)', 'Giga (G)', 'Mega (M)',
                              'Kilo (K)', '', 'Milli (m)', 'Micro ($\mu$)', 'Nano (n)', 'Pico (p)', 'Femto (f)',
                              'Atto (a)', 'Zepto (z)', 'Yocto (y)']))
    exp = np.floor(np.log10(np.abs(num)))
    if exp < 0:
        exp -= 3
    expIndex = int(exp / 3) + 8
    expIndex = 0 if expIndex < 0 else expIndex
    expIndex = len(prefixes)-1 if expIndex >= len(prefixes) else expIndex
    r1 = prefixes[expIndex]
    num1 = num / divisors[expIndex]
    if expIndex != len(prefixes):
        r2 = prefixes[expIndex + 1]
        num2 = num / divisors[expIndex + 1]
    else:
        num2 = None
    retStr = str(num1) + ' ' + r1
    if num2 is not None:
        retStr += '\nor\n' + str(num2) + ' ' + r2
    return retStr


def what(obj, callingLocals=locals()):
    """
    quick function to print name of input and value.
    If not for the default-Valued callingLocals, the function would always
    get the name as "obj", which is not what I want.

    :param obj: the object to print info for
    :param callingLocals: don't use, always should be locals().
    """
    name = "name not found"
    for k, v in list(callingLocals.items()):
        if v is obj:
            name = k
    if type(obj) == float:
        print(name, "=", "{:,}".format(obj))
    else:
        print(name, "=", obj)


def transpose(l):
    """
    Transpose a list.
    :param l: the list to be transposed
    :return: the tranposed list
    """
    return list(map(list, zip(*l)))


def getStats(data, printStats=False):
    """
    get some basic statistics about the input data, in the form of a pandas dataframe.
    :param data: the data to analyze
    :param printStats: an option to print the results
    :return: the dataframe containing the statistics
    """
    data = list(data)
    d = DataFrame()
    d['Avg'] = [np.mean(data)]
    d['len'] = [len(data)]
    d['min'] = [min(data)]
    d['max'] = [max(data)]
    d['std'] = [np.std(data)]
    d = d.transpose()
    d.columns = ['Stats']
    d = d.transpose()
    if printStats:
        print(d)
    return d


def getColors(num, rgb=False):
    """
    Get an array of colors, typically to use for plotting.

    :param rgb: an option to return the colors as an rgb tuple instead of a hex.
    :param num: number of colors to get
    :return: the array of colors, hex or rgb (see above)
    """
    cmapRGB = get_cmap('nipy_spectral', num)
    c = [cmapRGB(i)[:-1] for i in range(num)][1:]
    if rgb:
        return c
    # the negative of the first color
    c2 = [tuple(arr((1, 1, 1)) - arr(color)) for color in c]
    c = ['#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(color))) for color in c]
    c2 = ['#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(color))) for color in c2]
    return c, c2


def round_sig(x, sig=3):
    """
    round a float to some number of significant digits
    :param x: the numebr to round
    :param sig: the number of significant digits to use in the rounding
    :return the rounded number, as a float.
    """
    if np.isnan(x):
        x = 0
    try:
        return round(x, sig-int(np.floor(np.log10(abs(x)+2*np.finfo(float).eps)))-1)
    except ValueError:
        print(abs(x))


def getExp(val):
    return np.floor(np.log10(np.abs(val)))


def round_sig_str(x, sig=3):
    """
    round a float to some number of significant digits
    :param x: the numebr to round
    :param sig: the number of significant digits to use in the rounding
    :return the rounded number, as a string.
    """
    if sig<=0:
        return "0"
    if np.isnan(x):
        x = 0
    try:
        num = round(x, sig-int(np.floor(np.log10(abs(x)+2*np.finfo(float).eps)))-1)
        decimals = sig-getExp(num)-1
        if decimals <= 0:
            decimals = 0
        result = ("{0:."+str(int(decimals))+"f}").format(num)
        # make sure result has the correct number of significant digits given the precision.
        return result
    except ValueError:
        print(abs(x))


def errString(val, err, precision=3):
    """
    takes the input value and error and makes a nice error string. e.g.
    inputs of
    1.423, 0.086, 3 gives
    1.42(9)
    :param val:
    :param err:
    :param precision:
    :return:
    """
    valE = getExp(val)
    # determine number of values of err to show.
    errE = getExp(err)
    num = int(errE-valE+precision)
    if num < 0:
        num = 0
    expFactor = -getExp(err)+num-1
    if expFactor <= 0:
        expFactor = 0
    errNum = int(round(err*10**expFactor))
    result = round_sig_str(val, precision) + '(' + round_sig_str(errNum, num) + ')'
    return result

9ff4a71ac5d83161a5cdd90d2c00564f4e8b990b
<mask token>


def what(obj, callingLocals=locals()):
    """
    quick function to print name of input and value.
    If not for the default-Valued callingLocals, the function would always
    get the name as "obj", which is not what I want.

    :param obj: the object to print info for
    :param callingLocals: don't use, always should be locals().
    """
    name = 'name not found'
    for k, v in list(callingLocals.items()):
        if v is obj:
            name = k
    if type(obj) == float:
        print(name, '=', '{:,}'.format(obj))
    else:
        print(name, '=', obj)


def transpose(l):
    """
    Transpose a list.
    :param l: the list to be transposed
    :return: the tranposed list
    """
    return list(map(list, zip(*l)))


def getStats(data, printStats=False):
    """
    get some basic statistics about the input data, in the form of a pandas dataframe.
    :param data: the data to analyze
    :param printStats: an option to print the results
    :return: the dataframe containing the statistics
    """
    data = list(data)
    d = DataFrame()
    d['Avg'] = [np.mean(data)]
    d['len'] = [len(data)]
    d['min'] = [min(data)]
    d['max'] = [max(data)]
    d['std'] = [np.std(data)]
    d = d.transpose()
    d.columns = ['Stats']
    d = d.transpose()
    if printStats:
        print(d)
    return d


def getColors(num, rgb=False):
    """
    Get an array of colors, typically to use for plotting.

    :param rgb: an option to return the colors as an rgb tuple instead of a hex.
    :param num: number of colors to get
    :return: the array of colors, hex or rgb (see above)
    """
    cmapRGB = get_cmap('nipy_spectral', num)
    c = [cmapRGB(i)[:-1] for i in range(num)][1:]
    if rgb:
        return c
    c2 = [tuple(arr((1, 1, 1)) - arr(color)) for color in c]
    c = [('#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(
        color)))) for color in c]
    c2 = [('#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(
        color)))) for color in c2]
    return c, c2


<mask token>

<mask token>


def prefix(num):
    """
    Convert number to nearest numbers with SI prefixes.
    :param num: the number to convert
    """
    divisors = [(1e-24 * pow(10, 3 * x)) for x in range(17)]
    prefixes = list(reversed(['Yotta (Y)', 'Zetta (Z)', 'Exa (E)',
        'Peta (P)', 'Tera (T)', 'Giga (G)', 'Mega (M)', 'Kilo (K)', '',
        'Milli (m)', 'Micro ($\\mu$)', 'Nano (n)', 'Pico (p)', 'Femto (f)',
        'Atto (a)', 'Zepto (z)', 'Yocto (y)']))
    exp = np.floor(np.log10(np.abs(num)))
    if exp < 0:
        exp -= 3
    expIndex = int(exp / 3) + 8
    expIndex = 0 if expIndex < 0 else expIndex
    expIndex = len(prefixes) - 1 if expIndex >= len(prefixes) else expIndex
    r1 = prefixes[expIndex]
    num1 = num / divisors[expIndex]
    if expIndex != len(prefixes):
        r2 = prefixes[expIndex + 1]
        num2 = num / divisors[expIndex + 1]
    else:
        num2 = None
    retStr = str(num1) + ' ' + r1
    if num2 is not None:
        retStr += '\nor\n' + str(num2) + ' ' + r2
    return retStr


def what(obj, callingLocals=locals()):
    """
    quick function to print name of input and value.
    If not for the default-Valued callingLocals, the function would always
    get the name as "obj", which is not what I want.

    :param obj: the object to print info for
    :param callingLocals: don't use, always should be locals().
    """
    name = 'name not found'
    for k, v in list(callingLocals.items()):
        if v is obj:
            name = k
    if type(obj) == float:
        print(name, '=', '{:,}'.format(obj))
    else:
        print(name, '=', obj)


def transpose(l):
    """
    Transpose a list.
    :param l: the list to be transposed
    :return: the tranposed list
    """
    return list(map(list, zip(*l)))


def getStats(data, printStats=False):
    """
    get some basic statistics about the input data, in the form of a pandas dataframe.
    :param data: the data to analyze
    :param printStats: an option to print the results
    :return: the dataframe containing the statistics
    """
    data = list(data)
    d = DataFrame()
    d['Avg'] = [np.mean(data)]
    d['len'] = [len(data)]
    d['min'] = [min(data)]
    d['max'] = [max(data)]
    d['std'] = [np.std(data)]
    d = d.transpose()
    d.columns = ['Stats']
    d = d.transpose()
    if printStats:
        print(d)
    return d


def getColors(num, rgb=False):
    """
    Get an array of colors, typically to use for plotting.

    :param rgb: an option to return the colors as an rgb tuple instead of a hex.
    :param num: number of colors to get
    :return: the array of colors, hex or rgb (see above)
    """
    cmapRGB = get_cmap('nipy_spectral', num)
    c = [cmapRGB(i)[:-1] for i in range(num)][1:]
    if rgb:
        return c
    c2 = [tuple(arr((1, 1, 1)) - arr(color)) for color in c]
    c = [('#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(
        color)))) for color in c]
    c2 = [('#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(
        color)))) for color in c2]
    return c, c2


def round_sig(x, sig=3):
    """
    round a float to some number of significant digits
    :param x: the numebr to round
    :param sig: the number of significant digits to use in the rounding
    :return the rounded number, as a float.
    """
    if np.isnan(x):
        x = 0
    try:
        return round(x, sig - int(np.floor(np.log10(abs(x) + 2 * np.finfo(
            float).eps))) - 1)
    except ValueError:
        print(abs(x))


def getExp(val):
    return np.floor(np.log10(np.abs(val)))


def round_sig_str(x, sig=3):
    """
    round a float to some number of significant digits
    :param x: the numebr to round
    :param sig: the number of significant digits to use in the rounding
    :return the rounded number, as a string.
    """
    if sig <= 0:
        return '0'
    if np.isnan(x):
        x = 0
    try:
        num = round(x, sig - int(np.floor(np.log10(abs(x) + 2 * np.finfo(
            float).eps))) - 1)
        decimals = sig - getExp(num) - 1
        if decimals <= 0:
            decimals = 0
        result = ('{0:.' + str(int(decimals)) + 'f}').format(num)
        return result
    except ValueError:
        print(abs(x))


def errString(val, err, precision=3):
    """
    takes the input value and error and makes a nice error string. e.g.
    inputs of
    1.423, 0.086, 3 gives
    1.42(9)
    :param val:
    :param err:
    :param precision:
    :return:
    """
    valE = getExp(val)
    errE = getExp(err)
    num = int(errE - valE + precision)
    if num < 0:
        num = 0
    expFactor = -getExp(err) + num - 1
    if expFactor <= 0:
        expFactor = 0
    errNum = int(round(err * 10 ** expFactor))
    result = round_sig_str(val, precision) + '(' + round_sig_str(errNum, num
        ) + ')'
    return result

__version__ = '1.0'
<mask token>


def prefix(num):
    """
    Convert number to nearest numbers with SI prefixes.
    :param num: the number to convert
    """
    divisors = [(1e-24 * pow(10, 3 * x)) for x in range(17)]
    prefixes = list(reversed(['Yotta (Y)', 'Zetta (Z)', 'Exa (E)',
        'Peta (P)', 'Tera (T)', 'Giga (G)', 'Mega (M)', 'Kilo (K)', '',
        'Milli (m)', 'Micro ($\\mu$)', 'Nano (n)', 'Pico (p)', 'Femto (f)',
        'Atto (a)', 'Zepto (z)', 'Yocto (y)']))
    exp = np.floor(np.log10(np.abs(num)))
    if exp < 0:
        exp -= 3
    expIndex = int(exp / 3) + 8
    expIndex = 0 if expIndex < 0 else expIndex
    expIndex = len(prefixes) - 1 if expIndex >= len(prefixes) else expIndex
    r1 = prefixes[expIndex]
    num1 = num / divisors[expIndex]
    if expIndex != len(prefixes):
        r2 = prefixes[expIndex + 1]
        num2 = num / divisors[expIndex + 1]
    else:
        num2 = None
    retStr = str(num1) + ' ' + r1
    if num2 is not None:
        retStr += '\nor\n' + str(num2) + ' ' + r2
    return retStr


def what(obj, callingLocals=locals()):
    """
    quick function to print name of input and value.
    If not for the default-Valued callingLocals, the function would always
    get the name as "obj", which is not what I want.

    :param obj: the object to print info for
    :param callingLocals: don't use, always should be locals().
    """
    name = 'name not found'
    for k, v in list(callingLocals.items()):
        if v is obj:
            name = k
    if type(obj) == float:
        print(name, '=', '{:,}'.format(obj))
    else:
        print(name, '=', obj)


def transpose(l):
    """
    Transpose a list.
    :param l: the list to be transposed
    :return: the tranposed list
    """
    return list(map(list, zip(*l)))


def getStats(data, printStats=False):
    """
    get some basic statistics about the input data, in the form of a pandas dataframe.
    :param data: the data to analyze
    :param printStats: an option to print the results
    :return: the dataframe containing the statistics
    """
    data = list(data)
    d = DataFrame()
    d['Avg'] = [np.mean(data)]
    d['len'] = [len(data)]
    d['min'] = [min(data)]
    d['max'] = [max(data)]
    d['std'] = [np.std(data)]
    d = d.transpose()
    d.columns = ['Stats']
    d = d.transpose()
    if printStats:
        print(d)
    return d


def getColors(num, rgb=False):
    """
    Get an array of colors, typically to use for plotting.

    :param rgb: an option to return the colors as an rgb tuple instead of a hex.
    :param num: number of colors to get
    :return: the array of colors, hex or rgb (see above)
    """
    cmapRGB = get_cmap('nipy_spectral', num)
    c = [cmapRGB(i)[:-1] for i in range(num)][1:]
    if rgb:
        return c
    c2 = [tuple(arr((1, 1, 1)) - arr(color)) for color in c]
    c = [('#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(
        color)))) for color in c]
    c2 = [('#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(
        color)))) for color in c2]
    return c, c2


def round_sig(x, sig=3):
    """
    round a float to some number of significant digits
    :param x: the numebr to round
    :param sig: the number of significant digits to use in the rounding
    :return the rounded number, as a float.
    """
    if np.isnan(x):
        x = 0
    try:
        return round(x, sig - int(np.floor(np.log10(abs(x) + 2 * np.finfo(
            float).eps))) - 1)
    except ValueError:
        print(abs(x))


def getExp(val):
    return np.floor(np.log10(np.abs(val)))


def round_sig_str(x, sig=3):
    """
    round a float to some number of significant digits
    :param x: the numebr to round
    :param sig: the number of significant digits to use in the rounding
    :return the rounded number, as a string.
    """
    if sig <= 0:
        return '0'
    if np.isnan(x):
        x = 0
    try:
        num = round(x, sig - int(np.floor(np.log10(abs(x) + 2 * np.finfo(
            float).eps))) - 1)
        decimals = sig - getExp(num) - 1
        if decimals <= 0:
            decimals = 0
        result = ('{0:.' + str(int(decimals)) + 'f}').format(num)
        return result
    except ValueError:
        print(abs(x))


def errString(val, err, precision=3):
    """
    takes the input value and error and makes a nice error string. e.g.
    inputs of
    1.423, 0.086, 3 gives
    1.42(9)
    :param val:
    :param err:
    :param precision:
    :return:
    """
    valE = getExp(val)
    errE = getExp(err)
    num = int(errE - valE + precision)
    if num < 0:
        num = 0
    expFactor = -getExp(err) + num - 1
    if expFactor <= 0:
        expFactor = 0
    errNum = int(round(err * 10 ** expFactor))
    result = round_sig_str(val, precision) + '(' + round_sig_str(errNum, num
        ) + ')'
    return result

__version__ = '1.0'
import numpy as np
from numpy import array as arr
from matplotlib.cm import get_cmap
from pandas import DataFrame


def prefix(num):
    """
    Convert number to nearest numbers with SI prefixes.
    :param num: the number to convert
    """
    divisors = [(1e-24 * pow(10, 3 * x)) for x in range(17)]
    prefixes = list(reversed(['Yotta (Y)', 'Zetta (Z)', 'Exa (E)',
        'Peta (P)', 'Tera (T)', 'Giga (G)', 'Mega (M)', 'Kilo (K)', '',
        'Milli (m)', 'Micro ($\\mu$)', 'Nano (n)', 'Pico (p)', 'Femto (f)',
        'Atto (a)', 'Zepto (z)', 'Yocto (y)']))
    exp = np.floor(np.log10(np.abs(num)))
    if exp < 0:
        exp -= 3
    expIndex = int(exp / 3) + 8
    expIndex = 0 if expIndex < 0 else expIndex
    expIndex = len(prefixes) - 1 if expIndex >= len(prefixes) else expIndex
    r1 = prefixes[expIndex]
    num1 = num / divisors[expIndex]
    if expIndex != len(prefixes):
        r2 = prefixes[expIndex + 1]
        num2 = num / divisors[expIndex + 1]
    else:
        num2 = None
    retStr = str(num1) + ' ' + r1
    if num2 is not None:
        retStr += '\nor\n' + str(num2) + ' ' + r2
    return retStr


def what(obj, callingLocals=locals()):
    """
    quick function to print name of input and value.
    If not for the default-Valued callingLocals, the function would always
    get the name as "obj", which is not what I want.

    :param obj: the object to print info for
    :param callingLocals: don't use, always should be locals().
    """
    name = 'name not found'
    for k, v in list(callingLocals.items()):
        if v is obj:
            name = k
    if type(obj) == float:
        print(name, '=', '{:,}'.format(obj))
    else:
        print(name, '=', obj)


def transpose(l):
    """
    Transpose a list.
    :param l: the list to be transposed
    :return: the tranposed list
    """
    return list(map(list, zip(*l)))


def getStats(data, printStats=False):
    """
    get some basic statistics about the input data, in the form of a pandas dataframe.
    :param data: the data to analyze
    :param printStats: an option to print the results
    :return: the dataframe containing the statistics
    """
    data = list(data)
    d = DataFrame()
    d['Avg'] = [np.mean(data)]
    d['len'] = [len(data)]
    d['min'] = [min(data)]
    d['max'] = [max(data)]
    d['std'] = [np.std(data)]
    d = d.transpose()
    d.columns = ['Stats']
    d = d.transpose()
    if printStats:
        print(d)
    return d


def getColors(num, rgb=False):
    """
    Get an array of colors, typically to use for plotting.

    :param rgb: an option to return the colors as an rgb tuple instead of a hex.
    :param num: number of colors to get
    :return: the array of colors, hex or rgb (see above)
    """
    cmapRGB = get_cmap('nipy_spectral', num)
    c = [cmapRGB(i)[:-1] for i in range(num)][1:]
    if rgb:
        return c
    c2 = [tuple(arr((1, 1, 1)) - arr(color)) for color in c]
    c = [('#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(
        color)))) for color in c]
    c2 = [('#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(
        color)))) for color in c2]
    return c, c2


def round_sig(x, sig=3):
    """
    round a float to some number of significant digits
    :param x: the numebr to round
    :param sig: the number of significant digits to use in the rounding
    :return the rounded number, as a float.
    """
    if np.isnan(x):
        x = 0
    try:
        return round(x, sig - int(np.floor(np.log10(abs(x) + 2 * np.finfo(
            float).eps))) - 1)
    except ValueError:
        print(abs(x))


def getExp(val):
    return np.floor(np.log10(np.abs(val)))


def round_sig_str(x, sig=3):
    """
    round a float to some number of significant digits
    :param x: the numebr to round
    :param sig: the number of significant digits to use in the rounding
    :return the rounded number, as a string.
    """
    if sig <= 0:
        return '0'
    if np.isnan(x):
        x = 0
    try:
        num = round(x, sig - int(np.floor(np.log10(abs(x) + 2 * np.finfo(
            float).eps))) - 1)
        decimals = sig - getExp(num) - 1
        if decimals <= 0:
            decimals = 0
        result = ('{0:.' + str(int(decimals)) + 'f}').format(num)
        return result
    except ValueError:
        print(abs(x))


def errString(val, err, precision=3):
    """
    takes the input value and error and makes a nice error string. e.g.
    inputs of
    1.423, 0.086, 3 gives
    1.42(9)
    :param val:
    :param err:
    :param precision:
    :return:
    """
    valE = getExp(val)
    errE = getExp(err)
    num = int(errE - valE + precision)
    if num < 0:
        num = 0
    expFactor = -getExp(err) + num - 1
    if expFactor <= 0:
        expFactor = 0
    errNum = int(round(err * 10 ** expFactor))
    result = round_sig_str(val, precision) + '(' + round_sig_str(errNum, num
        ) + ')'
    return result

__version__ = "1.0"

import numpy as np
from numpy import array as arr
from matplotlib.cm import get_cmap
from pandas import DataFrame


def prefix(num):
    """
    Convert number to nearest numbers with SI prefixes.
    :param num: the number to convert
    """
    # determine which range it lies in, r1/r2 means reduction 1 or reduction 2
    divisors = [1e-24 * pow(10, 3 * x) for x in range(17)]
    prefixes = list(reversed(['Yotta (Y)', 'Zetta (Z)', 'Exa (E)', 'Peta (P)', 'Tera (T)', 'Giga (G)', 'Mega (M)',
                              'Kilo (K)', '', 'Milli (m)', 'Micro ($\mu$)', 'Nano (n)', 'Pico (p)', 'Femto (f)',
                              'Atto (a)', 'Zepto (z)', 'Yocto (y)']))
    exp = np.floor(np.log10(np.abs(num)))
    if exp < 0:
        exp -= 3
    expIndex = int(exp / 3) + 8
    expIndex = 0 if expIndex < 0 else expIndex
    expIndex = len(prefixes)-1 if expIndex >= len(prefixes) else expIndex
    r1 = prefixes[expIndex]
    num1 = num / divisors[expIndex]
    if expIndex != len(prefixes):
        r2 = prefixes[expIndex + 1]
        num2 = num / divisors[expIndex + 1]
    else:
        num2 = None
    retStr = str(num1) + ' ' + r1
    if num2 is not None:
        retStr += '\nor\n' + str(num2) + ' ' + r2
    return retStr


def what(obj, callingLocals=locals()):
    """
    quick function to print name of input and value.
    If not for the default-Valued callingLocals, the function would always
    get the name as "obj", which is not what I want.

    :param obj: the object to print info for
    :param callingLocals: don't use, always should be locals().
    """
    name = "name not found"
    for k, v in list(callingLocals.items()):
        if v is obj:
            name = k
    if type(obj) == float:
        print(name, "=", "{:,}".format(obj))
    else:
        print(name, "=", obj)


def transpose(l):
    """
    Transpose a list.
    :param l: the list to be transposed
    :return: the tranposed list
    """
    return list(map(list, zip(*l)))


def getStats(data, printStats=False):
    """
    get some basic statistics about the input data, in the form of a pandas dataframe.
    :param data: the data to analyze
    :param printStats: an option to print the results
    :return: the dataframe containing the statistics
    """
    data = list(data)
    d = DataFrame()
    d['Avg'] = [np.mean(data)]
    d['len'] = [len(data)]
    d['min'] = [min(data)]
    d['max'] = [max(data)]
    d['std'] = [np.std(data)]
    d = d.transpose()
    d.columns = ['Stats']
    d = d.transpose()
    if printStats:
        print(d)
    return d


def getColors(num, rgb=False):
    """
    Get an array of colors, typically to use for plotting.

    :param rgb: an option to return the colors as an rgb tuple instead of a hex.
    :param num: number of colors to get
    :return: the array of colors, hex or rgb (see above)
    """
    cmapRGB = get_cmap('nipy_spectral', num)
    c = [cmapRGB(i)[:-1] for i in range(num)][1:]
    if rgb:
        return c
    # the negative of the first color
    c2 = [tuple(arr((1, 1, 1)) - arr(color)) for color in c]
    c = ['#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(color))) for color in c]
    c2 = ['#%02x%02x%02x' % tuple(int(255 * color[i]) for i in range(len(color))) for color in c2]
    return c, c2


def round_sig(x, sig=3):
    """
    round a float to some number of significant digits
    :param x: the numebr to round
    :param sig: the number of significant digits to use in the rounding
    :return the rounded number, as a float.
    """
    if np.isnan(x):
        x = 0
    try:
        return round(x, sig-int(np.floor(np.log10(abs(x)+2*np.finfo(float).eps)))-1)
    except ValueError:
        print(abs(x))


def getExp(val):
    return np.floor(np.log10(np.abs(val)))


def round_sig_str(x, sig=3):
    """
    round a float to some number of significant digits
    :param x: the numebr to round
    :param sig: the number of significant digits to use in the rounding
    :return the rounded number, as a string.
    """
    if sig<=0:
        return "0"
    if np.isnan(x):
        x = 0
    try:
        num = round(x, sig-int(np.floor(np.log10(abs(x)+2*np.finfo(float).eps)))-1)
        decimals = sig-getExp(num)-1
        if decimals <= 0:
            decimals = 0
        result = ("{0:."+str(int(decimals))+"f}").format(num)
        # make sure result has the correct number of significant digits given the precision.
        return result
    except ValueError:
        print(abs(x))


def errString(val, err, precision=3):
    """
    takes the input value and error and makes a nice error string. e.g.
    inputs of
    1.423, 0.086, 3 gives
    1.42(9)
    :param val:
    :param err:
    :param precision:
    :return:
    """
    valE = getExp(val)
    # determine number of values of err to show.
    errE = getExp(err)
    num = int(errE-valE+precision)
    if num < 0:
        num = 0
    expFactor = -getExp(err)+num-1
    if expFactor <= 0:
        expFactor = 0
    errNum = int(round(err*10**expFactor))
    result = round_sig_str(val, precision) + '(' + round_sig_str(errNum, num) + ')'
    return result
