Instruction:
Implement a function in Python 3 to
return url for call method.

Response:
def get_url(self, method=None, **kwargs):
        """Return url for call method.

        :param method (optional): `str` method name.
        :returns: `str` URL.
        """
        kwargs.setdefault('v', self.__version)

        if self.__token is not None:
            kwargs.setdefault('access_token', self.__token)

        return 'https://api.vk.com/method/{}?{}'.format(
            method or self.__method, urlencode(kwargs)
        )


Instruction:
Implement a function in Python 3 to
send request to API.

Response:
def request(self, method, **kwargs):
        """
        Send request to API.

        :param method: `str` method name.
        :returns: `dict` response.
        """
        kwargs.setdefault('v', self.__version)

        if self.__token is not None:
            kwargs.setdefault('access_token', self.__token)

        return requests.get(self.get_url(method, **kwargs)).json()


Instruction:
Make a summary of the following Python 3 code
def authentication(login, password):
    session = requests.Session()
    response = session.get('https://m.vk.com')
    url = re.search(r'action="([^\"]+)"', response.text).group(1)
    data = {'email': login, 'pass': password}
    response = session.post(url, data=data)
    return session

Response: Authenticate on vk. com.


Instruction:
Can you tell what is the following Python 3 function doing
def create_from_array(self, blockname, array, Nfile=None, memorylimit=1024 * 1024 * 256):
        size = len(array)

        # sane value -- 32 million items per physical file
        sizeperfile = 32 * 1024 * 1024

        if Nfile is None:
            Nfile = (size + sizeperfile - 1) // sizeperfile

        dtype = numpy.dtype((array.dtype, array.shape[1:]))

        itemsize = dtype.itemsize
        # we will do some chunking

        # write memorylimit bytes at most (256M bytes)
        # round to 1024 items
        itemlimit = memorylimit // dtype.itemsize // 1024 * 1024

        with self.create(blockname, dtype, size, Nfile) as b:
            for i in range(0, len(array), itemlimit):
                b.write(i, numpy.array(array[i:i+itemlimit]))

        return self.open(blockname)

Response: create a new block from array like objects


Instruction:
Write a Python 3 script to
refresh the list of blocks to the disk collectively

Response:
def refresh(self):
        """ Refresh the list of blocks to the disk, collectively """
        if self.comm.rank == 0:
            self._blocks = self.list_blocks()
        else:
            self._blocks = None
        self._blocks = self.comm.bcast(self._blocks)


Instruction:
Write a Python 3 script for
creating a new block from array like objects

Response:
def create_from_array(self, blockname, array, Nfile=None, memorylimit=1024 * 1024 * 256):
        """ create a block from array like objects
            The operation is well defined only if array is at most 2d.

            Parameters
            ----------
            array : array_like,
                array shall have a scalar dtype.
            blockname : string
                name of the block
            Nfile : int or None
                number of physical files. if None, 32M items per file
                is used.
            memorylimit : int
                number of bytes to use for the buffering. relevant only if
                indexing on array returns a copy (e.g. IO or dask array)

        """
        size = self.comm.allreduce(len(array))

        # sane value -- 32 million items per physical file
        sizeperfile = 32 * 1024 * 1024

        if Nfile is None:
            Nfile = (size + sizeperfile - 1) // sizeperfile

        offset = sum(self.comm.allgather(len(array))[:self.comm.rank])
        dtype = numpy.dtype((array.dtype, array.shape[1:]))

        itemsize = dtype.itemsize
        # we will do some chunking

        # write memorylimit bytes at most (256M bytes)
        # round to 1024 items
        itemlimit = memorylimit // dtype.itemsize // 1024 * 1024

        with self.create(blockname, dtype, size, Nfile) as b:
            for i in range(0, len(array), itemlimit):
                b.write(offset + i, numpy.array(array[i:i+itemlimit]))

        return self.open(blockname)


Instruction:
Write a Python 3 script for
checking if the value is a string type and attempts to convert it to a boolean otherwise returns the original value.

Response:
def maybebool(value):
    '''
    If `value` is a string type, attempts to convert it to a boolean
    if it looks like it might be one, otherwise returns the value
    unchanged. The difference between this and
    :func:`pyramid.settings.asbool` is how non-bools are handled: this
    returns the original value, whereas `asbool` returns False.
    '''
    if isinstance(value, six.string_types) and value.lower() in booly:
        return asbool(value)  # pragma: no cover
    return value


Instruction:
Make a summary of the following Python 3 code
def format_data(self, data, scale=True):
        if len(self.analytes) == 1:
            # if single analyte
            d = nominal_values(data[self.analytes[0]])
            ds = np.array(list(zip(d, np.zeros(len(d)))))
        else:
            # package multiple analytes
            d = [nominal_values(data[a]) for a in self.analytes]
            ds = np.vstack(d).T

        # identify all nan values
        finite = np.isfinite(ds).sum(1) == ds.shape[1]
        # remember which values are sampled
        sampled = np.arange(data[self.analytes[0]].size)[finite]
        # remove all nan values
        ds = ds[finite]

        if scale:
            ds = self.scaler.transform(ds)

        return ds, sampled

Response: Function for converting a dict of data to an array suitable for sklearn.


Instruction:
Given the following Python 3 function, write the documentation
def fitting_data(self, data):
        ds_fit, _ = self.format_data(data, scale=False)

        # define scaler
        self.scaler = preprocessing.StandardScaler().fit(ds_fit)

        # scale data and return
        return self.scaler.transform(ds_fit)

Response: Function to format data for cluster fitting.


Instruction:
How would you explain what the following Python 3 function does
def fit_kmeans(self, data, n_clusters, **kwargs):
        km = cl.KMeans(n_clusters=n_clusters, **kwargs)
        km.fit(data)
        return km

Response: Fit KMeans clustering algorithm to data.


Instruction:
Given the following Python 3 function, write the documentation
def fit_meanshift(self, data, bandwidth=None, bin_seeding=False, **kwargs):
        if bandwidth is None:
            bandwidth = cl.estimate_bandwidth(data)
        ms = cl.MeanShift(bandwidth=bandwidth, bin_seeding=bin_seeding)
        ms.fit(data)
        return ms

Response: Fit MeanShift clustering algorithm to data.


Instruction:
Write a Python 3 function that can
fit classifiers from large dataset.

Response:
def fit(self, data, method='kmeans', **kwargs):
        """
        fit classifiers from large dataset.

        Parameters
        ----------
        data : dict
            A dict of data for clustering. Must contain
            items with the same name as analytes used for
            clustering.
        method : str
            A string defining the clustering method used. Can be:

            * 'kmeans' : K-Means clustering algorithm
            * 'meanshift' : Meanshift algorithm

        n_clusters : int
            *K-Means only*. The numebr of clusters to identify
        bandwidth : float
            *Meanshift only.*
            The bandwidth value used during clustering.
            If none, determined automatically. Note:
            the data are scaled before clutering, so
            this is not in the same units as the data.
        bin_seeding : bool
            *Meanshift only.*
            Whether or not to use 'bin_seeding'. See
            documentation for `sklearn.cluster.MeanShift`.
        **kwargs :
            passed to `sklearn.cluster.MeanShift`.

        Returns
        -------
        list
        """
        self.method = method
        ds_fit = self.fitting_data(data)
        mdict = {'kmeans': self.fit_kmeans,
                 'meanshift': self.fit_meanshift}
        clust = mdict[method]

        self.classifier = clust(data=ds_fit, **kwargs)

        # sort cluster centers by value of first column, to avoid random variation.
        c0 = self.classifier.cluster_centers_.T[self.sort_by]
        self.classifier.cluster_centers_ = self.classifier.cluster_centers_[np.argsort(c0)]

        # recalculate the labels, so it's consistent with cluster centers
        self.classifier.labels_ = self.classifier.predict(ds_fit)
        self.classifier.ulabels_ = np.unique(self.classifier.labels_)

        return


Instruction:
Here you have a function in Python 3, explain what it does
def predict(self, data):
        size = data[self.analytes[0]].size
        ds, sampled = self.format_data(data)

        # predict clusters
        cs = self.classifier.predict(ds)
        # map clusters to original index
        clusters = self.map_clusters(size, sampled, cs)

        return clusters

Response: Predict new data with cluster identities.


Instruction:
Write a Python 3 script to
translate cluster identity back to original data size.

Response:
def map_clusters(self, size, sampled, clusters):
        """
        Translate cluster identity back to original data size.

        Parameters
        ----------
        size : int
            size of original dataset
        sampled : array-like
            integer array describing location of finite values
            in original data.
        clusters : array-like
            integer array of cluster identities

        Returns
        -------
        list of cluster identities the same length as original
        data. Where original data are non-finite, returns -2.

        """
        ids = np.zeros(size, dtype=int)
        ids[:] = -2

        ids[sampled] = clusters

        return ids


Instruction:
Write a Python 3 script for
sorting clusters by concentration of a particular analyte.

Response:
def sort_clusters(self, data, cs, sort_by):
        """
        Sort clusters by the concentration of a particular analyte.

        Parameters
        ----------
        data : dict
            A dataset containing sort_by as a key.
        cs : array-like
            An array of clusters, the same length as values of data.
        sort_by : str
            analyte to sort the clusters by

        Returns
        -------
        array of clusters, sorted by mean value of sort_by analyte.
        """
        # label the clusters according to their contents
        sdat = data[sort_by]

        means = []
        nclusts = np.arange(cs.max() + 1)
        for c in nclusts:
            means.append(np.nanmean(sdat[cs == c]))

        # create ranks
        means = np.array(means)
        rank = np.zeros(means.size)
        rank[np.argsort(means)] = np.arange(means.size)

        csn = cs.copy()
        for c, o in zip(nclusts, rank):
            csn[cs == c] = o

        return csn


Instruction:
How would you implement a function in Python 3 that
returns a datetime oject from a string with optional time format.

Response:
def get_date(datetime, time_format=None):
    """
    Return a datetime oject from a string, with optional time format.

    Parameters
    ----------
    datetime : str
        Date-time as string in any sensible format.
    time_format : datetime str (optional)
        String describing the datetime format. If missing uses
        dateutil.parser to guess time format.
    """
    if time_format is None:
        t = du.parser.parse(datetime)
    else:
        t = dt.datetime.strftime(datetime, time_format)
    return t


Instruction:
Can you create a Python 3 function that
returns the total number of data points in values of dict.

Response:
def get_total_n_points(d):
    """
    Returns the total number of data points in values of dict.

    Paramters
    ---------
    d : dict
    """
    n = 0
    for di in d.values():
        n += len(di)
    return n


Instruction:
Can you generate a brief explanation for the following Python 3 code
def get_total_time_span(d):

    tmax = 0
    for di in d.values():
        if di.uTime.max() > tmax:
            tmax = di.uTime.max()

    return tmax

Response: Returns the total length of analysis.


Instruction:
Here you have a function in Python 3, explain what it does
def unitpicker(a, llim=0.1, denominator=None, focus_stage=None):

    if not isinstance(a, (int, float)):
        a = nominal_values(a)
        a = np.percentile(a[~np.isnan(a)], 25)

    if denominator is not None:
        pd = pretty_element(denominator)
    else:
        pd = ''

    if focus_stage == 'calibrated':
        udict = {0: 'mol/mol ' + pd,
                 1: 'mmol/mol ' + pd,
                 2: '$\mu$mol/mol ' + pd,
                 3: 'nmol/mol ' + pd,
                 4: 'pmol/mol ' + pd,
                 5: 'fmol/mol ' + pd}
    elif focus_stage == 'ratios':
        udict = {0: 'counts/count ' + pd,
                 1: '$10^{-3}$ counts/count ' + pd,
                 2: '$10^{-6}$ counts/count ' + pd,
                 3: '$10^{-9}$ counts/count ' + pd,
                 4: '$10^{-12}$ counts/count ' + pd,
                 5: '$10^{-15}$ counts/count ' + pd}
    elif focus_stage in ('rawdata', 'despiked', 'bkgsub'):
        udict = udict = {0: 'counts',
                         1: '$10^{-3}$ counts',
                         2: '$10^{-6}$ counts',
                         3: '$10^{-9}$ counts',
                         4: '$10^{-12}$ counts',
                         5: '$10^{-15}$ counts'}
    else:
        udict = {0: '', 1: '', 2: '', 3: '', 4: '', 5: ''}

    a = abs(a)
    n = 0
    if a < llim:
        while a < llim:
            a *= 1000
            n += 1
    return float(1000**n), udict[n]

Response: Returns a new random number generator that can be used to plot the data in a new random number generator.


Instruction:
Write a Python 3 function for
returning a string with the name of the current language element.

Response:
def pretty_element(s):
    """
    Returns formatted element name.

    Parameters
    ----------
    s : str
        of format [A-Z][a-z]?[0-9]+

    Returns
    -------
    str
        LaTeX formatted string with superscript numbers.
    """
    el = re.match('.*?([A-z]{1,3}).*?', s).groups()[0]
    m = re.match('.*?([0-9]{1,3}).*?', s).groups()[0]

    return '$^{' + m + '}$' + el


Instruction:
Write a Python 3 function for
converting analytes in format 27Al to Al27.

Response:
def analyte_2_namemass(s):
    """
    Converts analytes in format '27Al' to 'Al27'.

    Parameters
    ----------
    s : str
        of format [A-z]{1,3}[0-9]{1,3}

    Returns
    -------
    str
        Name in format [0-9]{1,3}[A-z]{1,3}
    """
    el = re.match('.*?([A-z]{1,3}).*?', s).groups()[0]
    m = re.match('.*?([0-9]{1,3}).*?', s).groups()[0]

    return el + m


Instruction:
Can you create a Python 3 function that
converts analytes in format Al27 to 27Al.

Response:
def analyte_2_massname(s):
    """
    Converts analytes in format 'Al27' to '27Al'.

    Parameters
    ----------
    s : str
        of format [0-9]{1,3}[A-z]{1,3}

    Returns
    -------
    str
        Name in format [A-z]{1,3}[0-9]{1,3}
    """
    el = re.match('.*?([A-z]{1,3}).*?', s).groups()[0]
    m = re.match('.*?([0-9]{1,3}).*?', s).groups()[0]

    return m + el


Instruction:
Can you generate a brief explanation for the following Python 3 code
def collate_data(in_dir, extension='.csv', out_dir=None):
    if out_dir is None:
        out_dir = './' + re.search('^\.(.*)', extension).groups(0)[0]

    if not os.path.isdir(out_dir):
        os.mkdir(out_dir)

    for p, d, fs in os.walk(in_dir):
        for f in fs:
            if extension in f:
                shutil.copy(p + '/' + f, out_dir + '/' + f)
    return

Response: This function collates all csv files in a directory into a single directory.


Instruction:
Make a summary of the following Python 3 code
def bool_2_indices(a):
    if any(a):
        lims = []
        lims.append(np.where(a[:-1] != a[1:])[0])

        if a[0]:
            lims.append([0])
        if a[-1]:
            lims.append([len(a) - 1])
        lims = np.concatenate(lims)
        lims.sort()

        return np.reshape(lims, (lims.size // 2, 2))
    else:
        return None

Response: Convert boolean array into a 2D array of start stop pairs.


Instruction:
Can you generate the documentation for the following Python 3 function
def enumerate_bool(bool_array, nstart=0):
    ind = bool_2_indices(bool_array)
    ns = np.full(bool_array.size, nstart, dtype=int)
    for n, lims in enumerate(ind):
        ns[lims[0]:lims[-1] + 1] = nstart + n + 1
    return ns

Response: Enumerate contiguous booleans in array.


Instruction:
Can you create a Python 3 function that
generates boolean array from list of limit tuples.

Response:
def tuples_2_bool(tuples, x):
    """
    Generate boolean array from list of limit tuples.

    Parameters
    ----------
    tuples : array_like
        [2, n] array of (start, end) values
    x : array_like
        x scale the tuples are mapped to

    Returns
    -------
    array_like
        boolean array, True where x is between each pair of tuples.
    """
    if np.ndim(tuples) == 1:
        tuples = [tuples]

    out = np.zeros(x.size, dtype=bool)
    for l, u in tuples:
        out[(x > l) & (x < u)] = True
    return out


Instruction:
Here you have a function in Python 3, explain what it does
def rolling_window(a, window, pad=None):
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
    strides = a.strides + (a.strides[-1], )
    out = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
    # pad shape
    if window % 2 == 0:
        npre = window // 2 - 1
        npost = window // 2
    else:
        npre = npost = window // 2
    if isinstance(pad, str):
        if pad == 'ends':
            prepad = np.full((npre, window), a[0])
            postpad = np.full((npost, window), a[-1])
        elif pad == 'mean_ends':
            prepad = np.full((npre, window), np.mean(a[:(window // 2)]))
            postpad = np.full((npost, window), np.mean(a[-(window // 2):]))
        elif pad == 'repeat_ends':
            prepad = np.full((npre, window), out[0])
            postpad = np.full((npost, window), out[0])
        else:
            raise ValueError("If pad is a string, it must be either 'ends', 'mean_ends' or 'repeat_ends'.")

        return np.concatenate((prepad, out, postpad))
    elif pad is not None:
        pre_blankpad = np.empty(((npre, window)))
        pre_blankpad[:] = pad
        post_blankpad = np.empty(((npost, window)))
        post_blankpad[:] = pad
        return np.concatenate([pre_blankpad, out, post_blankpad])
    else:
        return out

Response: Return a rolling window of a array of data.


Instruction:
Write a Python 3 script for
functioning to efficiently calculate the rolling mean of a numpy array using stride_tricks

Response:
def fastsmooth(a, win=11):
    """
    Returns rolling - window smooth of a.

    Function to efficiently calculate the rolling mean of a numpy
    array using 'stride_tricks' to split up a 1D array into an ndarray of
    sub - sections of the original array, of dimensions [len(a) - win, win].

    Parameters
    ----------
    a : array_like
        The 1D array to calculate the rolling gradient of.
    win : int
        The width of the rolling window.

    Returns
    -------
    array_like
        Gradient of a, assuming as constant integer x - scale.
    """
    # check to see if 'window' is odd (even does not work)
    if win % 2 == 0:
        win += 1  # add 1 to window if it is even.
    kernel = np.ones(win) / win
    npad = int((win - 1) / 2)
    spad = np.full(npad + 1, np.mean(a[:(npad + 1)]))
    epad = np.full(npad - 1, np.mean(a[-(npad - 1):]))
    return np.concatenate([spad, np.convolve(a, kernel, 'valid'), epad])


Instruction:
Given the following Python 3 function, write the documentation
def fastgrad(a, win=11):
    # check to see if 'window' is odd (even does not work)
    if win % 2 == 0:
        win += 1  # subtract 1 from window if it is even.
    # trick for efficient 'rolling' computation in numpy
    # shape = a.shape[:-1] + (a.shape[-1] - win + 1, win)
    # strides = a.strides + (a.strides[-1], )
    # wins = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
    wins = rolling_window(a, win, 'ends')
    # apply rolling gradient to data
    a = map(lambda x: np.polyfit(np.arange(win), x, 1)[0], wins)

    return np.array(list(a))

Response: Function to efficiently calculate the rolling - window gradient of a 1D array.


Instruction:
How would you code a function in Python 3 to
calculate gradients of values in x in a dict of items.

Response:
def calc_grads(x, dat, keys=None, win=5):
    """
    Calculate gradients of values in dat.

    Parameters
    ----------
    x : array like
        Independent variable for items in dat.
    dat : dict
        {key: dependent_variable} pairs
    keys : str or array-like
        Which keys in dict to calculate the gradient of.
    win : int
        The side of the rolling window for gradient calculation

    Returns
    -------
    dict of gradients
    """
    if keys is None:
        keys = dat.keys()

    def grad(xy):
        if (~np.isnan(xy)).all():
            try:
                return np.polyfit(xy[0], xy[1], 1)[0]
            except ValueError:
                return np.nan
        else:
            return np.nan

    xs = rolling_window(x, win, pad='repeat_ends')
    grads = Bunch()
    for k in keys:
        d = nominal_values(rolling_window(dat[k], win, pad='repeat_ends'))

        grads[k] = np.array(list(map(grad, zip(xs, d))))

    return grads


Instruction:
Write a Python 3 script to
function to find local minima.

Response:
def findmins(x, y):
    """ Function to find local minima.

    Parameters
    ----------
    x, y : array_like
        1D arrays of the independent (x) and dependent (y) variables.

    Returns
    -------
    array_like
        Array of points in x where y has a local minimum.
    """
    return x[np.r_[False, y[1:] < y[:-1]] & np.r_[y[:-1] < y[1:], False]]


Instruction:
Can you tell what is the following Python 3 function doing
def stack_keys(ddict, keys, extra=None):
    if isinstance(keys, str):
        d = [ddict[keys]]
    else:
        d = [ddict[k] for k in keys]
    if extra is not None:
        d = extra + d
    return np.vstack(d).T

Response: Stacks the keys of a dict into a single array of shape len ( keys ).


Instruction:
Here you have a function in Python 3, explain what it does
def cluster_meanshift(data, bandwidth=None, bin_seeding=False, **kwargs):
    if bandwidth is None:
        bandwidth = cl.estimate_bandwidth(data)

    ms = cl.MeanShift(bandwidth=bandwidth, bin_seeding=bin_seeding, **kwargs)
    ms.fit(data)

    labels = ms.labels_

    return labels, [np.nan]

Response: Identify clusters using Meanshift algorithm.


Instruction:
Make a summary of the following Python 3 code
def cluster_kmeans(data, n_clusters, **kwargs):
    km = cl.KMeans(n_clusters, **kwargs)
    kmf = km.fit(data)

    labels = kmf.labels_

    return labels, [np.nan]

Response: Identify clusters using K - Means algorithm.


Instruction:
How would you code a function in Python 3 to
identify clusters using DBSCAN algorithm. Parameters ---------- data : array_like array of size [n_samples, n_features]. eps : float The minimum 'distance' points must be apart for them to be in the same cluster. Defaults to 0.3. Note: If the data are normalised (they should be for DBSCAN) this is in terms of total sample variance. Normalised data have a mean of 0 and a variance of 1. min_samples : int The minimum number of samples within distance `eps` required to be considered as an independent cluster. n_clusters : int The number of clusters expected. If specified, `eps` will be incrementally reduced until the expected number of clusters is found. maxiter : int The maximum number of iterations DBSCAN will run. Returns ------- dict boolean array for each identified cluster and core samples.

Response:
def cluster_DBSCAN(data, eps=None, min_samples=None,
                   n_clusters=None, maxiter=200, **kwargs):
    """
    Identify clusters using DBSCAN algorithm.

    Parameters
    ----------
    data : array_like
        array of size [n_samples, n_features].
    eps : float
        The minimum 'distance' points must be apart for them to be in the
        same cluster. Defaults to 0.3. Note: If the data are normalised
        (they should be for DBSCAN) this is in terms of total sample
        variance.  Normalised data have a mean of 0 and a variance of 1.
    min_samples : int
        The minimum number of samples within distance `eps` required
        to be considered as an independent cluster.
    n_clusters : int
        The number of clusters expected. If specified, `eps` will be
        incrementally reduced until the expected number of clusters is
        found.
    maxiter : int
        The maximum number of iterations DBSCAN will run.

    Returns
    -------
    dict
        boolean array for each identified cluster and core samples.
    """
    if n_clusters is None:
        if eps is None:
            eps = 0.3
        db = cl.DBSCAN(eps=eps, min_samples=min_samples, **kwargs).fit(data)
    else:
        clusters = 0
        eps_temp = 1 / .95
        niter = 0
        while clusters < n_clusters:
            clusters_last = clusters
            eps_temp *= 0.95
            db = cl.DBSCAN(eps=eps_temp, min_samples=min_samples, **kwargs).fit(data)
            clusters = (len(set(db.labels_)) -
                        (1 if -1 in db.labels_ else 0))
            if clusters < clusters_last:
                eps_temp *= 1 / 0.95
                db = cl.DBSCAN(eps=eps_temp, min_samples=min_samples, **kwargs).fit(data)
                clusters = (len(set(db.labels_)) -
                            (1 if -1 in db.labels_ else 0))
                warnings.warn(('\n\n***Unable to find {:.0f} clusters in '
                                'data. Found {:.0f} with an eps of {:.2e}'
                                '').format(n_clusters, clusters, eps_temp))
                break
            niter += 1
            if niter == maxiter:
                warnings.warn(('\n\n***Maximum iterations ({:.0f}) reached'
                                ', {:.0f} clusters not found.\nDeacrease '
                                'min_samples or n_clusters (or increase '
                                'maxiter).').format(maxiter, n_clusters))
                break

    labels = db.labels_

    core_samples_mask = np.zeros_like(labels)
    core_samples_mask[db.core_sample_indices_] = True

    return labels, core_samples_mask


Instruction:
Create a Python 3 function for
returning list of SRMS defined in the SRM database

Response:
def get_defined_srms(srm_file):
    """
    Returns list of SRMS defined in the SRM database
    """
    srms = read_table(srm_file)
    return np.asanyarray(srms.index.unique())


Instruction:
Given the following Python 3 function, write the documentation
def read_configuration(config='DEFAULT'):
    # read configuration file
    _, conf = read_latoolscfg()
    # if 'DEFAULT', check which is the default configuration
    if config == 'DEFAULT':
        config = conf['DEFAULT']['config']

    # grab the chosen configuration
    conf = dict(conf[config])
    # update config name with chosen
    conf['config'] = config
    return conf

Response: Read LAtools configuration file and return parameters as dict.


Instruction:
Given the following Python 3 function, write the documentation
def read_latoolscfg():
    config_file = pkgrs.resource_filename('latools', 'latools.cfg')
    cf = configparser.ConfigParser()
    cf.read(config_file)
    return config_file, cf

Response: Reads the config file returns a ConfigParser object.


Instruction:
Can you generate the documentation for the following Python 3 function
def print_all():
    # read configuration file
    _, conf = read_latoolscfg()

    default = conf['DEFAULT']['config']

    pstr = '\nCurrently defined LAtools configurations:\n\n'
    for s in conf.sections():
        if s == default:
            pstr += s + ' [DEFAULT]\n'
        elif s == 'REPRODUCE':
            pstr += s + ' [DO NOT ALTER]\n'
        else:
            pstr += s + '\n'

        for k, v in conf[s].items():
            if k != 'config':
                if v[:9] == 'resources':
                    v = pkgrs.resource_filename('latools', v)
                pstr += '   ' + k + ': ' + v + '\n'
        pstr += '\n'

    print(pstr)
    return

Response: Prints all currently defined LAtools configurations.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def copy_SRM_file(destination=None, config='DEFAULT'):
    # find SRM file from configuration
    conf = read_configuration()

    src = pkgrs.resource_filename('latools', conf['srmfile'])

    # work out destination path (if not given)
    if destination is None:
        destination = './LAtools_' + conf['config'] + '_SRMTable.csv'

    if os.path.isdir(destination):
        destination += 'LAtools_' + conf['config'] + '_SRMTable.csv'

    copyfile(src, destination)

    print(src + ' \n    copied to:\n      ' + destination)
    return

Response: Copy the default SRM file to the specified location.


Instruction:
Can you create a Python 3 function that
creates a new configuration file and returns it.

Response:
def create(config_name, srmfile=None, dataformat=None, base_on='DEFAULT', make_default=False):
    """
    Adds a new configuration to latools.cfg.

    Parameters
    ----------
    config_name : str
        The name of the new configuration. This should be descriptive
        (e.g. UC Davis Foram Group)
    srmfile : str (optional)
        The location of the srm file used for calibration.
    dataformat : str (optional)
        The location of the dataformat definition to use.
    base_on : str
        The name of the existing configuration to base the new one on.
        If either srm_file or dataformat are not specified, the new
        config will copy this information from the base_on config.
    make_default : bool
        Whether or not to make the new configuration the default
        for future analyses. Default = False.

    Returns
    -------
    None
    """

    base_config = read_configuration(base_on)

    # read config file
    config_file, cf = read_latoolscfg()

    # if config doesn't already exist, create it.
    if config_name not in cf.sections():
        cf.add_section(config_name)
    # set parameter values
    if dataformat is None:
        dataformat = base_config['dataformat']
    cf.set(config_name, 'dataformat', dataformat)

    if srmfile is None:
        srmfile = base_config['srmfile']
    cf.set(config_name, 'srmfile', srmfile)

    # make the parameter set default, if requested
    if make_default:
        cf.set('DEFAULT', 'config', config_name)

    with open(config_file, 'w') as f:
        cf.write(f)

    return


Instruction:
Can you generate a brief explanation for the following Python 3 code
def change_default(config):
    config_file, cf = read_latoolscfg()

    if config not in cf.sections():
        raise ValueError("\n'{:s}' is not a defined configuration.".format(config))

    if config == 'REPRODUCE':
        pstr = ('Are you SURE you want to set REPRODUCE as your default configuration?\n' +
                '     ... this is an odd thing to be doing.')
    else:
        pstr = ('Are you sure you want to change the default configuration from {:s}'.format(cf['DEFAULT']['config']) +
                'to {:s}?'.format(config))

    response = input(pstr + '\n> [N/y]: ')

    if response.lower() == 'y':
        cf.set('DEFAULT', 'config', config)
        with open(config_file, 'w') as f:
            cf.write(f)
        print('  Default changed!')
    else:
        print('  Done nothing.')

Response: Change the default configuration of the current language.


Instruction:
Write a Python 3 script to
return boolean arrays where a < threshold and a < threshold.

Response:
def threshold(values, threshold):
    """
    Return boolean arrays where a >= and < threshold.

    Parameters
    ----------
    values : array-like
        Array of real values.
    threshold : float
        Threshold value

    Returns
    -------
    (below, above) : tuple or boolean arrays
    """
    values = nominal_values(values)
    return (values < threshold, values >= threshold)


Instruction:
Here you have a function in Python 3, explain what it does
def exclude_downhole(filt, threshold=2):
    cfilt = filt.copy()

    inds = bool_2_indices(~filt)

    rem = (np.diff(inds) >= threshold)[:, 0]

    if any(rem):
        if inds[rem].shape[0] > 1:
            limit = inds[rem][1, 0]
            cfilt[limit:] = False

    return cfilt

Response: Exclude all data after the first excluded portion.


Instruction:
Can you tell what is the following Python 3 function doing
def trim(ind, start=1, end=0):

    return np.roll(ind, start) & np.roll(ind, -end)

Response: Removes points from the start and end of True regions.


Instruction:
Write a Python 3 script for
setting the focus attribute of the object.

Response:
def setfocus(self, focus):
        """
        Set the 'focus' attribute of the data file.

        The 'focus' attribute of the object points towards data from a
        particular stage of analysis. It is used to identify the 'working
        stage' of the data. Processing functions operate on the 'focus'
        stage, so if steps are done out of sequence, things will break.

        Names of analysis stages:

        * 'rawdata': raw data, loaded from csv file when object
          is initialised.
        * 'despiked': despiked data.
        * 'signal'/'background': isolated signal and background data,
          padded with np.nan. Created by self.separate, after
          signal and background regions have been identified by
          self.autorange.
        * 'bkgsub': background subtracted data, created by
          self.bkg_correct
        * 'ratios': element ratio data, created by self.ratio.
        * 'calibrated': ratio data calibrated to standards, created by
          self.calibrate.

        Parameters
        ----------
        focus : str
            The name of the analysis stage desired.

        Returns
        -------
        None
        """
        self.focus = self.data[focus]
        self.focus_stage = focus

        self.__dict__.update(self.focus)


Instruction:
Implement a function in Python 3 to
apply exponential decay and noise despike to data.

Response:
def despike(self, expdecay_despiker=True, exponent=None,
                noise_despiker=True, win=3, nlim=12., maxiter=3):
        """
        Applies expdecay_despiker and noise_despiker to data.

        Parameters
        ----------
        expdecay_despiker : bool
            Whether or not to apply the exponential decay filter.
        exponent : None or float
            The exponent for the exponential decay filter. If None,
            it is determined automatically using `find_expocoef`.
        noise_despiker : bool
            Whether or not to apply the standard deviation spike filter.
        win : int
            The rolling window over which the spike filter calculates
            the trace statistics.
        nlim : float
            The number of standard deviations above the rolling mean
            that data are excluded.
        maxiter : int
            The max number of times that the fitler is applied.

        Returns
        -------
        None

        """
        if not hasattr(self, 'despiked'):
            self.data['despiked'] = Bunch()

        out = {}
        for a, v in self.focus.items():
            if 'time' not in a.lower():
                sig = v.copy()  # copy data
                if expdecay_despiker:
                    if exponent is not None:
                        sig = proc.expdecay_despike(sig, exponent, self.tstep, maxiter)
                    else:
                        warnings.warn('exponent is None - either provide exponent, or run at `analyse`\nlevel to automatically calculate it.')

                if noise_despiker:
                    sig = proc.noise_despike(sig, int(win), nlim, maxiter)
                out[a] = sig

        self.data['despiked'].update(out)
        # recalculate total counts
        self.data['total_counts'] = sum(self.data['despiked'].values())
        self.setfocus('despiked')
        return


Instruction:
How would you explain what the following Python 3 function does
def autorange(self, analyte='total_counts', gwin=5, swin=3, win=30,
                  on_mult=[1., 1.], off_mult=[1., 1.5],
                  ploterrs=True, transform='log', **kwargs):
        """
        Automatically separates signal and background data regions.

        Automatically detect signal and background regions in the laser
        data, based on the behaviour of a single analyte. The analyte used
        should be abundant and homogenous in the sample.

        **Step 1: Thresholding.**
        The background signal is determined using a gaussian kernel density
        estimator (kde) of all the data. Under normal circumstances, this
        kde should find two distinct data distributions, corresponding to
        'signal' and 'background'. The minima between these two distributions
        is taken as a rough threshold to identify signal and background
        regions. Any point where the trace crosses this thrshold is identified
        as a 'transition'.

        **Step 2: Transition Removal.**
        The width of the transition regions between signal and background are
        then determined, and the transitions are excluded from analysis. The
        width of the transitions is determined by fitting a gaussian to the
        smoothed first derivative of the analyte trace, and determining its
        width at a point where the gaussian intensity is at at `conf` time the
        gaussian maximum. These gaussians are fit to subsets of the data
        centered around the transitions regions determined in Step 1, +/- `win`
        data points. The peak is further isolated by finding the minima and
        maxima of a second derivative within this window, and the gaussian is
        fit to the isolated peak.

        Parameters
        ----------
        analyte : str
            The analyte that autorange should consider. For best results,
            choose an analyte that is present homogeneously in high
            concentrations.
        gwin : int
            The smoothing window used for calculating the first derivative.
            Must be odd.
        win : int
            Determines the width (c +/- win) of the transition data subsets.
        on_mult and off_mult : tuple, len=2
            Factors to control the width of the excluded transition regions.
            A region n times the full - width - half - maximum of the transition
            gradient will be removed either side of the transition center.
            `on_mult` and `off_mult` refer to the laser - on and laser - off
            transitions, respectively. See manual for full explanation.
            Defaults to (1.5, 1) and (1, 1.5).

        Returns
        -------
        Outputs added as instance attributes. Returns None.
        bkg, sig, trn : iterable, bool
            Boolean arrays identifying background, signal and transision
            regions
        bkgrng, sigrng and trnrng : iterable
            (min, max) pairs identifying the boundaries of contiguous
            True regions in the boolean arrays.
        """
        if analyte is None:
            # sig = self.focus[self.internal_standard]
            sig = self.data['total_counts']
        elif analyte == 'total_counts':
            sig = self.data['total_counts']
        elif analyte in self.analytes:
            sig = self.focus[analyte]
        else:
            raise ValueError('Invalid analyte.')

        (self.bkg, self.sig,
         self.trn, failed) = proc.autorange(self.Time, sig, gwin=gwin, swin=swin, win=win,
                                            on_mult=on_mult, off_mult=off_mult,
                                            transform=transform)

        self.mkrngs()

        errs_to_plot = False
        if len(failed) > 0:
            errs_to_plot = True
            plotlines = []
            for f in failed:
                if f != self.Time[-1]:
                    plotlines.append(f)
            # warnings.warn(("\n\nSample {:s}: ".format(self.sample) +
            #                "Transition identification at " +
            #                "{:.1f} failed.".format(f) +
            #                "\n  **This is not necessarily a problem**"
            #                "\nBut please check the data plots and make sure " +
            #                "everything is OK.\n"))

        if ploterrs and errs_to_plot and len(plotlines) > 0:
            f, ax = self.tplot(ranges=True)
            for pl in plotlines:
                ax.axvline(pl, c='r', alpha=0.6, lw=3, ls='dashed')
            return f, plotlines
        else:
            return

Response: A basic autorange of the log - likelihood of the current sample.


Instruction:
Implement a function in Python 3 to
plot a detailed autorange report for this sample.

Response:
def autorange_plot(self, analyte='total_counts', gwin=7, swin=None, win=20,
                       on_mult=[1.5, 1.], off_mult=[1., 1.5],
                       transform='log'):
        """
        Plot a detailed autorange report for this sample.
        """
        if analyte is None:
            # sig = self.focus[self.internal_standard]
            sig = self.data['total_counts']
        elif analyte == 'total_counts':
            sig = self.data['total_counts']
        elif analyte in self.analytes:
            sig = self.focus[analyte]
        else:
            raise ValueError('Invalid analyte.')

        if transform == 'log':
            sig = np.log10(sig)

        fig, axs = plot.autorange_plot(t=self.Time, sig=sig, gwin=gwin,
                                       swin=swin, win=win, on_mult=on_mult,
                                       off_mult=off_mult)

        return fig, axs


Instruction:
Implement a function in Python 3 to
transform boolean arrays into list of limit pairs.

Response:
def mkrngs(self):
        """
        Transform boolean arrays into list of limit pairs.

        Gets Time limits of signal/background boolean arrays and stores them as
        sigrng and bkgrng arrays. These arrays can be saved by 'save_ranges' in
        the analyse object.
        """
        bbool = bool_2_indices(self.bkg)
        if bbool is not None:
            self.bkgrng = self.Time[bbool]
        else:
            self.bkgrng = [[np.nan, np.nan]]
        sbool = bool_2_indices(self.sig)
        if sbool is not None:
            self.sigrng = self.Time[sbool]
        else:
            self.sigrng = [[np.nan, np.nan]]
        tbool = bool_2_indices(self.trn)
        if tbool is not None:
            self.trnrng = self.Time[tbool]
        else:
            self.trnrng = [[np.nan, np.nan]]

        self.ns = np.zeros(self.Time.size)
        n = 1
        for i in range(len(self.sig) - 1):
            if self.sig[i]:
                self.ns[i] = n
            if self.sig[i] and ~self.sig[i + 1]:
                n += 1
        self.n = int(max(self.ns))  # record number of traces

        return


Instruction:
Given the following Python 3 function, write the documentation
def bkg_subtract(self, analyte, bkg, ind=None, focus_stage='despiked'):
        if 'bkgsub' not in self.data.keys():
            self.data['bkgsub'] = Bunch()

        self.data['bkgsub'][analyte] = self.data[focus_stage][analyte] - bkg

        if ind is not None:
            self.data['bkgsub'][analyte][ind] = np.nan

        return

Response: Subtracts provided background from signal.


Instruction:
Given the following Python 3 function, write the documentation
def correct_spectral_interference(self, target_analyte, source_analyte, f):

        if target_analyte not in self.analytes:
            raise ValueError('target_analyte: {:} not in available analytes ({:})'.format(target_analyte, ', '.join(self.analytes)))

        if source_analyte not in self.analytes:
            raise ValueError('source_analyte: {:} not in available analytes ({:})'.format(source_analyte, ', '.join(self.analytes)))

        self.data['bkgsub'][target_analyte] -= self.data['bkgsub'][source_analyte] * f

Response: Correct the spectral interference of two sets of analytes.


Instruction:
Can you create a Python 3 function that
divides all analytes by a specified internal_standard and set the focus to the ratio of the analytes.

Response:
def ratio(self, internal_standard=None):
        """
        Divide all analytes by a specified internal_standard analyte.

        Parameters
        ----------
        internal_standard : str
            The analyte used as the internal_standard.

        Returns
        -------
        None
        """
        if internal_standard is not None:
            self.internal_standard = internal_standard

        self.data['ratios'] = Bunch()
        for a in self.analytes:
            self.data['ratios'][a] = (self.data['bkgsub'][a] /
                                      self.data['bkgsub'][self.internal_standard])
        self.setfocus('ratios')
        return


Instruction:
Can you implement a function in Python 3 that
applies calibration to data.

Response:
def calibrate(self, calib_ps, analytes=None):
        """
        Apply calibration to data.

        The `calib_dict` must be calculated at the `analyse` level,
        and passed to this calibrate function.

        Parameters
        ----------
        calib_dict : dict
            A dict of calibration values to apply to each analyte.

        Returns
        -------
        None
        """
        # can have calibration function stored in self and pass *coefs?
        if analytes is None:
            analytes = self.analytes

        if 'calibrated' not in self.data.keys():
            self.data['calibrated'] = Bunch()

        for a in analytes:
            m = calib_ps[a]['m'].new(self.uTime)

            if 'c' in calib_ps[a]:
                c = calib_ps[a]['c'].new(self.uTime)
            else:
                c = 0

            self.data['calibrated'][a] = self.data['ratios'][a] * m + c

        if self.internal_standard not in analytes:
            self.data['calibrated'][self.internal_standard] = \
                np.empty(len(self.data['ratios'][self.internal_standard]))

        self.setfocus('calibrated')
        return


Instruction:
Can you create a Python 3 function that
calculates sample statistics for the specified analytes and return the samples analytes and arrays of statistics.

Response:
def sample_stats(self, analytes=None, filt=True,
                     stat_fns={},
                     eachtrace=True):
        """
        Calculate sample statistics

        Returns samples, analytes, and arrays of statistics
        of shape (samples, analytes). Statistics are calculated
        from the 'focus' data variable, so output depends on how
        the data have been processed.

        Parameters
        ----------
        analytes : array_like
            List of analytes to calculate the statistic on
        filt : bool or str
            The filter to apply to the data when calculating sample statistics.
                bool: True applies filter specified in filt.switches.
                str: logical string specifying a partucular filter
        stat_fns : dict
            Dict of {name: function} pairs. Functions that take a single
            array_like input, and return a single statistic. Function should
            be able to cope with NaN values.
        eachtrace : bool
            True: per - ablation statistics
            False: whole sample statistics

        Returns
        -------
        None
        """
        if analytes is None:
                analytes = self.analytes
        elif isinstance(analytes, str):
            analytes = [analytes]

        self.stats = Bunch()
        self.stats['analytes'] = analytes

        with warnings.catch_warnings():
            warnings.simplefilter("ignore", category=RuntimeWarning)
            for n, f in stat_fns.items():
                self.stats[n] = []
                for a in analytes:
                    ind = self.filt.grab_filt(filt, a)
                    dat = nominal_values(self.focus[a])
                    if eachtrace:
                        sts = []
                        for t in np.arange(self.n) + 1:
                            sts.append(f(dat[ind & (self.ns == t)]))
                        self.stats[n].append(sts)
                    else:
                        self.stats[n].append(f(dat[ind]))
                self.stats[n] = np.array(self.stats[n])
        return


Instruction:
Can you create a Python 3 function that
functions for calculating the ablation time for each entry in the set of all the ablation times. Returns ------- dict of times for each entry in the set of ablation times for each entry in the set.

Response:
def ablation_times(self):
        """
        Function for calculating the ablation time for each
        ablation.

        Returns
        -------
            dict of times for each ablation.
        """
        ats = {}
        for n in np.arange(self.n) + 1:
            t = self.Time[self.ns == n]
            ats[n - 1] = t.max() - t.min()
        return ats


Instruction:
Write a Python 3 function that can
apply threshold filter. Generates threshold filters for the given analytes above and below the specified threshold. Two filters are created with prefixes '_above' and '_below'. '_above' keeps all the data above the threshold. '_below' keeps all the data below the threshold. i.e. to select data below the threshold value, you should turn the '_above' filter off. Parameters ---------- analyte : TYPE Description of `analyte`. threshold : TYPE Description of `threshold`. Returns ------- None

Response:
def filter_threshold(self, analyte, threshold):
        """
        Apply threshold filter.

        Generates threshold filters for the given analytes above and below
        the specified threshold.

        Two filters are created with prefixes '_above' and '_below'.
            '_above' keeps all the data above the threshold.
            '_below' keeps all the data below the threshold.

        i.e. to select data below the threshold value, you should turn the
        '_above' filter off.

        Parameters
        ----------
        analyte : TYPE
            Description of `analyte`.
        threshold : TYPE
            Description of `threshold`.

        Returns
        -------
        None
        """
        params = locals()
        del(params['self'])

        # generate filter
        below, above = filters.threshold(self.focus[analyte], threshold)

        setn = self.filt.maxset + 1

        self.filt.add(analyte + '_thresh_below',
                        below,
                        'Keep below {:.3e} '.format(threshold) + analyte,
                        params, setn=setn)
        self.filt.add(analyte + '_thresh_above',
                        above,
                        'Keep above {:.3e} '.format(threshold) + analyte,
                        params, setn=setn)


Instruction:
Implement a Python 3 function for
applying gradient threshold filter.

Response:
def filter_gradient_threshold(self, analyte, win, threshold, recalc=True):
        """
        Apply gradient threshold filter.

        Generates threshold filters for the given analytes above and below
        the specified threshold.

        Two filters are created with prefixes '_above' and '_below'.
            '_above' keeps all the data above the threshold.
            '_below' keeps all the data below the threshold.

        i.e. to select data below the threshold value, you should turn the
        '_above' filter off.

        Parameters
        ----------
        analyte : str
            Description of `analyte`.
        threshold : float
            Description of `threshold`.
        win : int
            Window used to calculate gradients (n points)
        recalc : bool
            Whether or not to re-calculate the gradients.

        Returns
        -------
        None
        """
        params = locals()
        del(params['self'])

        # calculate absolute gradient
        if recalc or not self.grads_calced:
            self.grads = calc_grads(self.Time, self.focus,
                                    [analyte], win)
            self.grads_calced = True

        below, above = filters.threshold(abs(self.grads[analyte]), threshold)

        setn = self.filt.maxset + 1

        self.filt.add(analyte + '_gthresh_below',
                        below,
                        'Keep gradient below {:.3e} '.format(threshold) + analyte,
                        params, setn=setn)
        self.filt.add(analyte + '_gthresh_above',
                        above,
                        'Keep gradient above {:.3e} '.format(threshold) + analyte,
                        params, setn=setn)


Instruction:
How would you implement a function in Python 3 that
applies a n - dimensional clustering filter to the data.

Response:
def filter_clustering(self, analytes, filt=False, normalise=True,
                          method='meanshift', include_time=False,
                          sort=None, min_data=10, **kwargs):
        """
        Applies an n - dimensional clustering filter to the data.

        Available Clustering Algorithms

        * 'meanshift': The `sklearn.cluster.MeanShift` algorithm.
          Automatically determines number of clusters
          in data based on the `bandwidth` of expected
          variation.
        * 'kmeans': The `sklearn.cluster.KMeans` algorithm. Determines
          the characteristics of a known number of clusters
          within the data. Must provide `n_clusters` to specify
          the expected number of clusters.
        * 'DBSCAN': The `sklearn.cluster.DBSCAN` algorithm. Automatically
          determines the number and characteristics of clusters
          within the data based on the 'connectivity' of the
          data (i.e. how far apart each data point is in a
          multi - dimensional parameter space). Requires you to
          set `eps`, the minimum distance point must be from
          another point to be considered in the same cluster,
          and `min_samples`, the minimum number of points that
          must be within the minimum distance for it to be
          considered a cluster. It may also be run in automatic
          mode by specifying `n_clusters` alongside
          `min_samples`, where eps is decreased until the
          desired number of clusters is obtained.

        For more information on these algorithms, refer to the
        documentation.

        Parameters
        ----------
        analytes : str
            The analyte(s) that the filter applies to.
        filt : bool
            Whether or not to apply existing filters to the data before
            calculating this filter.
        normalise : bool
            Whether or not to normalise the data to zero mean and unit
            variance. Reccomended if clustering based on more than 1 analyte.
            Uses `sklearn.preprocessing.scale`.
        method : str
            Which clustering algorithm to use (see above).
        include_time : bool
            Whether or not to include the Time variable in the clustering
            analysis. Useful if you're looking for spatially continuous
            clusters in your data, i.e. this will identify each spot in your
            analysis as an individual cluster.
        sort : bool, str or array-like
            Whether or not to label the resulting clusters according to their
            contents. If used, the cluster with the lowest values will be
            labelled from 0, in order of increasing cluster mean value.analytes.
            The sorting rules depend on the value of 'sort', which can be the name
            of a single analyte (str), a list of several analyte names (array-like)
            or True (bool), to specify all analytes used to calcualte the cluster.
        min_data : int
            The minimum number of data points that should be considered by
            the filter. Default = 10.
        **kwargs
            Parameters passed to the clustering algorithm specified by
            `method`.

        Meanshift Parameters
        --------------------
        bandwidth : str or float
            The bandwith (float) or bandwidth method ('scott' or 'silverman')
            used to estimate the data bandwidth.
        bin_seeding : bool
            Modifies the behaviour of the meanshift algorithm. Refer to
            sklearn.cluster.meanshift documentation.

        K - Means Parameters
        ------------------
        n_clusters : int
            The number of clusters expected in the data.

        DBSCAN Parameters
        -----------------
        eps : float
            The minimum 'distance' points must be apart for them to be in the
            same cluster. Defaults to 0.3. Note: If the data are normalised
            (they should be for DBSCAN) this is in terms of total sample
            variance. Normalised data have a mean of 0 and a variance of 1.
        min_samples : int
            The minimum number of samples within distance `eps` required
            to be considered as an independent cluster.
        n_clusters : int
            The number of clusters expected. If specified, `eps` will be
            incrementally reduced until the expected number of clusters is
            found.
        maxiter : int
            The maximum number of iterations DBSCAN will run.

        Returns
        -------
        None
        """
        params = locals()
        del(params['self'])

        # convert string to list, if single analyte
        if isinstance(analytes, str):
            analytes = [analytes]

        setn = self.filt.maxset + 1

        # generate filter
        vals = np.vstack(nominal_values(list(self.focus.values())))
        if filt is not None:
            ind = (self.filt.grab_filt(filt, analytes) &
                   np.apply_along_axis(all, 0, ~np.isnan(vals)))
        else:
            ind = np.apply_along_axis(all, 0, ~np.isnan(vals))

        if sum(ind) > min_data:

            # get indices for data passed to clustering
            sampled = np.arange(self.Time.size)[ind]

            # generate data for clustering
            if include_time:
                extra = self.Time
            else:
                extra = None
            # get data as array
            ds = stack_keys(self.focus, analytes, extra)
            # apply filter, and get nominal values
            ds = nominal_values(ds[ind, :])

            if normalise | (len(analytes) > 1):
                ds = preprocessing.scale(ds)

            method_key = {'kmeans': clustering.cluster_kmeans,
                        #   'DBSCAN': clustering.cluster_DBSCAN,
                          'meanshift': clustering.cluster_meanshift}

            cfun = method_key[method]

            labels, core_samples_mask = cfun(ds, **kwargs)
            # return labels, and if DBSCAN core_sample_mask

            labels_unique = np.unique(labels)

            # label the clusters according to their contents
            if (sort is not None) & (sort is not False):

                if isinstance(sort, str):
                    sort = [sort]

                sanalytes = analytes

                # make boolean filter to select analytes
                if sort is True:
                    sortk = np.array([True] * len(sanalytes))
                else:
                    sortk = np.array([s in sort for s in sanalytes])

                # create per-point mean based on selected analytes.
                sd = np.apply_along_axis(sum, 1, ds[:, sortk])
                # calculate per-cluster means
                avs = [np.nanmean(sd[labels == lab]) for lab in labels_unique]
                # re-order the cluster labels based on their means
                order = [x[0] for x in sorted(enumerate(avs), key=lambda x:x[1])]
                sdict = dict(zip(order, labels_unique))
            else:
                sdict = dict(zip(labels_unique, labels_unique))

            filts = {}
            for ind, lab in sdict.items():
                filts[lab] = labels == ind

            # only applies to DBSCAN results.
            if not all(np.isnan(core_samples_mask)):
                filts['core'] = core_samples_mask

            resized = {}
            for k, v in filts.items():
                resized[k] = np.zeros(self.Time.size, dtype=bool)
                resized[k][sampled] = v

            namebase = '-'.join(analytes) + '_cluster-' + method
            info = '-'.join(analytes) + ' cluster filter.'

            if method == 'DBSCAN':
                for k, v in resized.items():
                    if isinstance(k, str):
                        name = namebase + '_core'
                    elif k < 0:
                        name = namebase + '_noise'
                    else:
                        name = namebase + '_{:.0f}'.format(k)
                    self.filt.add(name, v, info=info, params=params, setn=setn)
            else:
                for k, v in resized.items():
                    name = namebase + '_{:.0f}'.format(k)
                    self.filt.add(name, v, info=info, params=params, setn=setn)
        else:
            # if there are no data
            name = '-'.join(analytes) + '_cluster-' + method + '_0'
            info = '-'.join(analytes) + ' cluster filter failed.'

            self.filt.add(name, np.zeros(self.Time.size, dtype=bool),
                          info=info, params=params, setn=setn)

        return


Instruction:
How would you code a function in Python 3 to
calculate the local correlation between two analytes.

Response:
def calc_correlation(self, x_analyte, y_analyte, window=15, filt=True, recalc=True):
        """
        Calculate local correlation between two analytes.

        Parameters
        ----------
        x_analyte, y_analyte : str
            The names of the x and y analytes to correlate.
        window : int, None
            The rolling window used when calculating the correlation.
        filt : bool
            Whether or not to apply existing filters to the data before
            calculating this filter.
        recalc : bool
            If True, the correlation is re-calculated, even if it is already present.

        Returns
        -------
        None
        """
        label = '{:}_{:}_{:.0f}'.format(x_analyte, y_analyte, window)

        if label in self.correlations and not recalc:
            return

        # make window odd
        if window % 2 != 1:
            window += 1

        # get filter
        ind = self.filt.grab_filt(filt, [x_analyte, y_analyte])

        x = nominal_values(self.focus[x_analyte])
        x[~ind] = np.nan
        xr = rolling_window(x, window, pad=np.nan)

        y = nominal_values(self.focus[y_analyte])
        y[~ind] = np.nan
        yr = rolling_window(y, window, pad=np.nan)

        r, p = zip(*map(nan_pearsonr, xr, yr))

        r = np.array(r)
        p = np.array(p)

        # save correlation info

        self.correlations[label] = r, p
        return


Instruction:
Make a summary of the following Python 3 code
def filter_correlation(self, x_analyte, y_analyte, window=15,
                           r_threshold=0.9, p_threshold=0.05, filt=True, recalc=False):
        """
        Calculate correlation filter.

        Parameters
        ----------
        x_analyte, y_analyte : str
            The names of the x and y analytes to correlate.
        window : int, None
            The rolling window used when calculating the correlation.
        r_threshold : float
            The correlation index above which to exclude data.
            Note: the absolute pearson R value is considered, so
            negative correlations below -`r_threshold` will also
            be excluded.
        p_threshold : float
            The significant level below which data are excluded.
        filt : bool
            Whether or not to apply existing filters to the data before
            calculating this filter.
        recalc : bool
            If True, the correlation is re-calculated, even if it is already present.

        Returns
        -------
        None
        """
        # make window odd
        if window % 2 != 1:
            window += 1

        params = locals()
        del(params['self'])

        setn = self.filt.maxset + 1

        label = '{:}_{:}_{:.0f}'.format(x_analyte, y_analyte, window)

        self.calc_correlation(x_analyte, y_analyte, window, filt, recalc)
        r, p = self.correlations[label]

        cfilt = (abs(r) > r_threshold) & (p < p_threshold)
        cfilt = ~cfilt

        name = x_analyte + '_' + y_analyte + '_corr'

        self.filt.add(name=name,
                      filt=cfilt,
                      info=(x_analyte + ' vs. ' + y_analyte +
                            ' correlation filter.'),
                      params=params, setn=setn)
        self.filt.off(filt=name)
        self.filt.on(analyte=y_analyte, filt=name)

        return

Response: Calculate and apply a correlation filter to the set of analytes.


Instruction:
Write a Python 3 function that can
plot the local correlation between two analytes.

Response:
def correlation_plot(self, x_analyte, y_analyte, window=15, filt=True, recalc=False):
        """
        Plot the local correlation between two analytes.

        Parameters
        ----------
        x_analyte, y_analyte : str
            The names of the x and y analytes to correlate.
        window : int, None
            The rolling window used when calculating the correlation.
        filt : bool
            Whether or not to apply existing filters to the data before
            calculating this filter.
        recalc : bool
            If True, the correlation is re-calculated, even if it is already present.

        Returns
        -------
        fig, axs : figure and axes objects
        """
        label = '{:}_{:}_{:.0f}'.format(x_analyte, y_analyte, window)

        self.calc_correlation(x_analyte, y_analyte, window, filt, recalc)
        r, p = self.correlations[label]

        fig, axs = plt.subplots(3, 1, figsize=[7, 5], sharex=True)

        # plot analytes
        ax = axs[0]

        ax.plot(self.Time, nominal_values(self.focus[x_analyte]), color=self.cmap[x_analyte], label=x_analyte)
        ax.plot(self.Time, nominal_values(self.focus[y_analyte]), color=self.cmap[y_analyte], label=y_analyte)

        ax.set_yscale('log')
        ax.legend()
        ax.set_ylabel('Signals')

        # plot r
        ax = axs[1]
        ax.plot(self.Time, r)
        ax.set_ylabel('Pearson R')

        # plot p
        ax = axs[2]
        ax.plot(self.Time, p)
        ax.set_ylabel('pignificance Level (p)')

        fig.tight_layout()

        return fig, axs


Instruction:
Here you have a function in Python 3, explain what it does
def filter_new(self, name, filt_str):
        filt = self.filt.grab_filt(filt=filt_str)
        self.filt.add(name, filt, info=filt_str)
        return

Response: Make new filter from combination of other filters.


Instruction:
How would you implement a function in Python 3 that
removes points from the start and end of the filter regions.

Response:
def filter_trim(self, start=1, end=1, filt=True):
        """
        Remove points from the start and end of filter regions.

        Parameters
        ----------
        start, end : int
            The number of points to remove from the start and end of
            the specified filter.
        filt : valid filter string or bool
            Which filter to trim. If True, applies to currently active
            filters.
        """
        params = locals()
        del(params['self'])

        f = self.filt.grab_filt(filt)
        nf = filters.trim(f, start, end)

        self.filt.add('trimmed_filter',
                    nf,
                    'Trimmed Filter ({:.0f} start, {:.0f} end)'.format(start, end),
                    params, setn=self.filt.maxset + 1)


Instruction:
Can you generate the documentation for the following Python 3 function
def filter_exclude_downhole(self, threshold, filt=True):
        f = self.filt.grab_filt(filt)

        if self.n == 1:
            nfilt = filters.exclude_downhole(f, threshold)

        else:
            nfilt = []
            for i in range(self.n):
                nf = self.ns == i + 1
                nfilt.append(filters.exclude_downhole(f & nf, threshold))
            nfilt = np.apply_along_axis(any, 0, nfilt)

        self.filt.add(name='downhole_excl_{:.0f}'.format(threshold),
                      filt=nfilt,
                      info='Exclude data downhole of {:.0f} consecutive filtered points.'.format(threshold),
                      params=(threshold, filt))

Response: Exclude all data points down -hole of the specified threshold.


Instruction:
Explain what the following Python 3 code does
def signal_optimiser(self, analytes, min_points=5,
                         threshold_mode='kde_first_max',
                         threshold_mult=1., x_bias=0,
                         weights=None, filt=True, mode='minimise'):
        """
        Optimise data selection based on specified analytes.

        Identifies the longest possible contiguous data region in
        the signal where the relative standard deviation (std) and
        concentration of all analytes is minimised.

        Optimisation is performed via a grid search of all possible
        contiguous data regions. For each region, the mean std and
        mean scaled analyte concentration ('amplitude') are calculated.

        The size and position of the optimal data region are identified
        using threshold std and amplitude values. Thresholds are derived
        from all calculated stds and amplitudes using the method specified
        by `threshold_mode`. For example, using the 'kde_max' method, a
        probability density function (PDF) is calculated for std and
        amplitude values, and the threshold is set as the maximum of the
        PDF. These thresholds are then used to identify the size and position
        of the longest contiguous region where the std is below the threshold,
        and the amplitude is either below the threshold.

        All possible regions of the data that have at least
        `min_points` are considered.

        For a graphical demonstration of the action of signal_optimiser,
        use `optimisation_plot`.

        Parameters
        ----------
        d : latools.D object
            An latools data object.
        analytes : str or array-like
            Which analytes to consider.
        min_points : int
            The minimum number of contiguous points to
            consider.
        threshold_mode : str
            The method used to calculate the optimisation
            thresholds. Can be 'mean', 'median', 'kde_max'
            or 'bayes_mvs', or a custom function. If a
            function, must take a 1D array, and return a
            single, real number.
        weights : array-like of length len(analytes)
            An array of numbers specifying the importance of
            each analyte considered. Larger number makes the
            analyte have a greater effect on the optimisation.
            Default is None.
        """
        params = locals()
        del(params['self'])
        setn = self.filt.maxset + 1

        if isinstance(analytes, str):
            analytes = [analytes]

        # get filter
        if filt is not False:
            ind = (self.filt.grab_filt(filt, analytes))
        else:
            ind = np.full(self.Time.shape, True)

        errmsg = []
        ofilt = []
        self.opt = {}
        for i in range(self.n):
            nind = ind & (self.ns == i + 1)

            self.opt[i + 1], err = signal_optimiser(self, analytes=analytes,
                                                    min_points=min_points,
                                                    threshold_mode=threshold_mode,
                                                    threshold_mult=threshold_mult,
                                                    weights=weights,
                                                    ind=nind, x_bias=x_bias,
                                                    mode=mode)

            if err == '':
                ofilt.append(self.opt[i + 1].filt)
            else:
                errmsg.append(self.sample + '_{:.0f}: '.format(i + 1) + err)

        if len(ofilt) > 0:
            ofilt = np.apply_along_axis(any, 0, ofilt)

            name = 'optimise_' + '_'.join(analytes)
            self.filt.add(name=name,
                        filt=ofilt,
                        info="Optimisation filter to minimise " + ', '.join(analytes),
                        params=params, setn=setn)

        if len(errmsg) > 0:
            return '\n'.join(errmsg)
        else:
            return ''

Response: This method is used to optimize the data selection based on the specified analytes.


Instruction:
How would you implement a function in Python 3 that
plots analytes as a function of Time.

Response:
def tplot(self, analytes=None, figsize=[10, 4], scale='log', filt=None,
              ranges=False, stats=False, stat='nanmean', err='nanstd',
              focus_stage=None, err_envelope=False, ax=None):
        """
        Plot analytes as a function of Time.

        Parameters
        ----------
        analytes : array_like
            list of strings containing names of analytes to plot.
            None = all analytes.
        figsize : tuple
            size of final figure.
        scale : str or None
           'log' = plot data on log scale
        filt : bool, str or dict
            False: plot unfiltered data.
            True: plot filtered data over unfiltered data.
            str: apply filter key to all analytes
            dict: apply key to each analyte in dict. Must contain all
            analytes plotted. Can use self.filt.keydict.
        ranges : bool
            show signal/background regions.
        stats : bool
            plot average and error of each trace, as specified by `stat` and
            `err`.
        stat : str
            average statistic to plot.
        err : str
            error statistic to plot.

        Returns
        -------
        figure, axis
        """

        return plot.tplot(self=self, analytes=analytes, figsize=figsize, scale=scale, filt=filt,
                          ranges=ranges, stats=stats, stat=stat, err=err,
                          focus_stage=focus_stage, err_envelope=err_envelope, ax=ax)


Instruction:
Implement a Python 3 function for
plotting analytes gradients as a function of Time.

Response:
def gplot(self, analytes=None, win=5, figsize=[10, 4],
              ranges=False, focus_stage=None, ax=None):
        """
        Plot analytes gradients as a function of Time.

        Parameters
        ----------
        analytes : array_like
            list of strings containing names of analytes to plot.
            None = all analytes.
        win : int
            The window over which to calculate the rolling gradient.
        figsize : tuple
            size of final figure.
        ranges : bool
            show signal/background regions.

        Returns
        -------
        figure, axis
        """

        return plot.gplot(self=self, analytes=analytes, win=win, figsize=figsize,
                          ranges=ranges, focus_stage=focus_stage, ax=ax)


Instruction:
Can you tell what is the following Python 3 function doing
def crossplot(self, analytes=None, bins=25, lognorm=True, filt=True, colourful=True, figsize=(12, 12)):
        if analytes is None:
            analytes = self.analytes
        if self.focus_stage in ['ratio', 'calibrated']:
            analytes = [a for a in analytes if self.internal_standard not in a]

        if figsize[0] < 1.5 * len(analytes):
            figsize = [1.5 * len(analytes)] * 2

        numvars = len(analytes)
        fig, axes = plt.subplots(nrows=numvars, ncols=numvars,
                                 figsize=(12, 12))
        fig.subplots_adjust(hspace=0.05, wspace=0.05)

        for ax in axes.flat:
            ax.xaxis.set_visible(False)
            ax.yaxis.set_visible(False)

            if ax.is_first_col():
                ax.yaxis.set_ticks_position('left')
            if ax.is_last_col():
                ax.yaxis.set_ticks_position('right')
            if ax.is_first_row():
                ax.xaxis.set_ticks_position('top')
            if ax.is_last_row():
                ax.xaxis.set_ticks_position('bottom')

        # set up colour scales
        if colourful:
            cmlist = ['Blues', 'BuGn', 'BuPu', 'GnBu',
                      'Greens', 'Greys', 'Oranges', 'OrRd',
                      'PuBu', 'PuBuGn', 'PuRd', 'Purples',
                      'RdPu', 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']
        else:
            cmlist = ['Greys']

        while len(cmlist) < len(analytes):
            cmlist *= 2

        udict = {}
        for i, j in zip(*np.triu_indices_from(axes, k=1)):
            for x, y in [(i, j), (j, i)]:
                # set unit multipliers
                mx, ux = unitpicker(np.nanmean(self.focus[analytes[x]]),
                                    denominator=self.internal_standard,
                                    focus_stage=self.focus_stage)
                my, uy = unitpicker(np.nanmean(self.focus[analytes[y]]),
                                    denominator=self.internal_standard,
                                    focus_stage=self.focus_stage)
                udict[analytes[x]] = (x, ux)

                # get filter
                xd = nominal_values(self.focus[analytes[x]])
                yd = nominal_values(self.focus[analytes[y]])

                ind = (self.filt.grab_filt(filt, analytes[x]) &
                       self.filt.grab_filt(filt, analytes[y]) &
                       ~np.isnan(xd) &
                       ~np.isnan(yd))

                # make plot
                pi = xd[ind] * mx
                pj = yd[ind] * my

                # determine normalisation shceme
                if lognorm:
                    norm = mpl.colors.LogNorm()
                else:
                    norm = None

                # draw plots
                axes[i, j].hist2d(pj, pi, bins,
                                  norm=norm,
                                  cmap=plt.get_cmap(cmlist[i]))
                axes[j, i].hist2d(pi, pj, bins,
                                  norm=norm,
                                  cmap=plt.get_cmap(cmlist[j]))

                axes[x, y].set_ylim([pi.min(), pi.max()])
                axes[x, y].set_xlim([pj.min(), pj.max()])
        # diagonal labels
        for a, (i, u) in udict.items():
            axes[i, i].annotate(a + '\n' + u, (0.5, 0.5),
                                xycoords='axes fraction',
                                ha='center', va='center')
        # switch on alternating axes
        for i, j in zip(range(numvars), itertools.cycle((-1, 0))):
            axes[j, i].xaxis.set_visible(True)
            for label in axes[j, i].get_xticklabels():
                label.set_rotation(90)
            axes[i, j].yaxis.set_visible(True)

        axes[0, 0].set_title(self.sample, weight='bold', x=0.05, ha='left')

        return fig, axes

Response: Plot analytes against each other.


Instruction:
Can you create a Python 3 function that
plots the results of a group of filters in a crossplot.

Response:
def crossplot_filters(self, filter_string, analytes=None):
        """
        Plot the results of a group of filters in a crossplot.

        Parameters
        ----------
        filter_string : str
            A string that identifies a group of filters.
            e.g. 'test' would plot all filters with 'test' in the
            name.
        analytes : optional, array_like or str
            The analyte(s) to plot. Defaults to all analytes.

        Returns
        -------
        fig, axes objects
        """

        if analytes is None:
            analytes = [a for a in self.analytes if 'Ca' not in a]

        # isolate relevant filters
        filts = self.filt.components.keys()
        cfilts = [f for f in filts if filter_string in f]
        flab = re.compile('.*_(.*)$')  # regex to get cluster number

        # set up axes
        numvars = len(analytes)
        fig, axes = plt.subplots(nrows=numvars, ncols=numvars,
                                 figsize=(12, 12))
        fig.subplots_adjust(hspace=0.05, wspace=0.05)

        for ax in axes.flat:
            ax.xaxis.set_visible(False)
            ax.yaxis.set_visible(False)

            if ax.is_first_col():
                ax.yaxis.set_ticks_position('left')
            if ax.is_last_col():
                ax.yaxis.set_ticks_position('right')
            if ax.is_first_row():
                ax.xaxis.set_ticks_position('top')
            if ax.is_last_row():
                ax.xaxis.set_ticks_position('bottom')

        # isolate nominal_values for all analytes
        focus = {k: nominal_values(v) for k, v in self.focus.items()}
        # determine units for all analytes
        udict = {a: unitpicker(np.nanmean(focus[a]),
                               denominator=self.internal_standard,
                               focus_stage=self.focus_stage) for a in analytes}
        # determine ranges for all analytes
        rdict = {a: (np.nanmin(focus[a] * udict[a][0]),
                     np.nanmax(focus[a] * udict[a][0])) for a in analytes}

        for f in cfilts:
            ind = self.filt.grab_filt(f)
            focus = {k: nominal_values(v[ind]) for k, v in self.focus.items()}
            lab = flab.match(f).groups()[0]
            axes[0, 0].scatter([], [], s=10, label=lab)

            for i, j in zip(*np.triu_indices_from(axes, k=1)):
                # get analytes
                ai = analytes[i]
                aj = analytes[j]

                # remove nan, apply multipliers
                pi = focus[ai][~np.isnan(focus[ai])] * udict[ai][0]
                pj = focus[aj][~np.isnan(focus[aj])] * udict[aj][0]

                # make plot
                axes[i, j].scatter(pj, pi, alpha=0.4, s=10, lw=0)
                axes[j, i].scatter(pi, pj, alpha=0.4, s=10, lw=0)

                axes[i, j].set_ylim(*rdict[ai])
                axes[i, j].set_xlim(*rdict[aj])

                axes[j, i].set_ylim(*rdict[aj])
                axes[j, i].set_xlim(*rdict[ai])

        # diagonal labels
        for a, n in zip(analytes, np.arange(len(analytes))):
            axes[n, n].annotate(a + '\n' + udict[a][1], (0.5, 0.5),
                                xycoords='axes fraction',
                                ha='center', va='center')
            axes[n, n].set_xlim(*rdict[a])
            axes[n, n].set_ylim(*rdict[a])

        axes[0, 0].legend(loc='upper left', title=filter_string, fontsize=8)

        # switch on alternating axes
        for i, j in zip(range(numvars), itertools.cycle((-1, 0))):
            axes[j, i].xaxis.set_visible(True)
            for label in axes[j, i].get_xticklabels():
                label.set_rotation(90)
            axes[i, j].yaxis.set_visible(True)

        return fig, axes


Instruction:
Can you create a Python 3 function that
visualises effect of data filters.

Response:
def filter_report(self, filt=None, analytes=None, savedir=None, nbin=5):
        """
        Visualise effect of data filters.

        Parameters
        ----------
        filt : str
            Exact or partial name of filter to plot. Supports
            partial matching. i.e. if 'cluster' is specified, all
            filters with 'cluster' in the name will be plotted.
            Defaults to all filters.
        analyte : str
            Name of analyte to plot.
        save : str
            file path to save the plot

        Returns
        -------
        (fig, axes)
        """
        return plot.filter_report(self, filt, analytes, savedir, nbin)


Instruction:
Create a Python 3 function to
return a dictionary of the parameters used to process data.

Response:
def get_params(self):
        """
        Returns paramters used to process data.

        Returns
        -------
        dict
            dict of analysis parameters
        """
        outputs = ['sample',
                   'ratio_params',
                   'despike_params',
                   'autorange_params',
                   'bkgcorrect_params']

        out = {}
        for o in outputs:
            out[o] = getattr(self, o)

        out['filter_params'] = self.filt.params
        out['filter_sequence'] = self.filt.sequence
        out['filter_used'] = self.filt.make_keydict()

        return out


Instruction:
How would you implement a function in Python 3 that
plots analytes as a function of Time.

Response:
def tplot(self, analytes=None, figsize=[10, 4], scale='log', filt=None,
              ranges=False, stats=False, stat='nanmean', err='nanstd',
              focus_stage=None, err_envelope=False, ax=None):
        """
        Plot analytes as a function of Time.

        Parameters
        ----------
        analytes : array_like
            list of strings containing names of analytes to plot.
            None = all analytes.
        figsize : tuple
            size of final figure.
        scale : str or None
           'log' = plot data on log scale
        filt : bool, str or dict
            False: plot unfiltered data.
            True: plot filtered data over unfiltered data.
            str: apply filter key to all analytes
            dict: apply key to each analyte in dict. Must contain all
            analytes plotted. Can use self.filt.keydict.
        ranges : bool
            show signal/background regions.
        stats : bool
            plot average and error of each trace, as specified by `stat` and
            `err`.
        stat : str
            average statistic to plot.
        err : str
            error statistic to plot.

        Returns
        -------
        figure, axis
        """
        if type(analytes) is str:
            analytes = [analytes]
        if analytes is None:
            analytes = self.analytes

        if focus_stage is None:
            focus_stage = self.focus_stage

        # exclude internal standard from analytes
        if focus_stage in ['ratios', 'calibrated']:
            analytes = [a for a in analytes if a != self.internal_standard]

        if ax is None:
            fig = plt.figure(figsize=figsize)
            ax = fig.add_axes([.1, .12, .77, .8])
            ret = True
        else:
            fig = ax.figure
            ret = False

        for a in analytes:
            x = self.Time
            y, yerr = unpack_uncertainties(self.data[focus_stage][a])

            if scale is 'log':
                ax.set_yscale('log')
                y[y == 0] = np.nan

            if filt:
                ind = self.filt.grab_filt(filt, a)
                xf = x.copy()
                yf = y.copy()
                yerrf = yerr.copy()
                if any(~ind):
                    xf[~ind] = np.nan
                    yf[~ind] = np.nan
                    yerrf[~ind] = np.nan
                if any(~ind):
                    ax.plot(x, y, color=self.cmap[a], alpha=.2, lw=0.6)
                ax.plot(xf, yf, color=self.cmap[a], label=a)
                if err_envelope:
                    ax.fill_between(xf, yf - yerrf, yf + yerrf, color=self.cmap[a],
                                    alpha=0.2, zorder=-1)
            else:
                ax.plot(x, y, color=self.cmap[a], label=a)
                if err_envelope:
                    ax.fill_between(x, y - yerr, y + yerr, color=self.cmap[a],
                                    alpha=0.2, zorder=-1)

            # Plot averages and error envelopes
            if stats and hasattr(self, 'stats'):
                warnings.warn('\nStatistic plotting is broken.\nCheck progress here: https://github.com/oscarbranson/latools/issues/18')
                pass
                # sts = self.stats[sig][0].size
                # if sts > 1:
                #     for n in np.arange(self.n):
                #         n_ind = ind & (self.ns == n + 1)
                #         if sum(n_ind) > 2:
                #             x = [self.Time[n_ind][0], self.Time[n_ind][-1]]
                #             y = [self.stats[sig][self.stats['analytes'] == a][0][n]] * 2

                #             yp = ([self.stats[sig][self.stats['analytes'] == a][0][n] +
                #                   self.stats[err][self.stats['analytes'] == a][0][n]] * 2)
                #             yn = ([self.stats[sig][self.stats['analytes'] == a][0][n] -
                #                   self.stats[err][self.stats['analytes'] == a][0][n]] * 2)

                #             ax.plot(x, y, color=self.cmap[a], lw=2)
                #             ax.fill_between(x + x[::-1], yp + yn,
                #                             color=self.cmap[a], alpha=0.4,
                #                             linewidth=0)
                # else:
                #     x = [self.Time[0], self.Time[-1]]
                #     y = [self.stats[sig][self.stats['analytes'] == a][0]] * 2
                #     yp = ([self.stats[sig][self.stats['analytes'] == a][0] +
                #           self.stats[err][self.stats['analytes'] == a][0]] * 2)
                #     yn = ([self.stats[sig][self.stats['analytes'] == a][0] -
                #           self.stats[err][self.stats['analytes'] == a][0]] * 2)

                #     ax.plot(x, y, color=self.cmap[a], lw=2)
                #     ax.fill_between(x + x[::-1], yp + yn, color=self.cmap[a],
                #                     alpha=0.4, linewidth=0)

        if ranges:
            for lims in self.bkgrng:
                ax.axvspan(*lims, color='k', alpha=0.1, zorder=-1)
            for lims in self.sigrng:
                ax.axvspan(*lims, color='r', alpha=0.1, zorder=-1)

        ax.text(0.01, 0.99, self.sample + ' : ' + focus_stage,
                transform=ax.transAxes,
                ha='left', va='top')

        ax.set_xlabel('Time (s)')
        ax.set_xlim(np.nanmin(x), np.nanmax(x))

        # y label
        ud = {'rawdata': 'counts',
              'despiked': 'counts',
              'bkgsub': 'background corrected counts',
              'ratios': 'counts/{:s} count',
              'calibrated': 'mol/mol {:s}'}
        if focus_stage in ['ratios', 'calibrated']:
            ud[focus_stage] = ud[focus_stage].format(self.internal_standard)
        ax.set_ylabel(ud[focus_stage])

        # if interactive:
        #     ax.legend()
        #     plugins.connect(fig, plugins.MousePosition(fontsize=14))
        #     display.clear_output(wait=True)
        #     display.display(fig)
        #     input('Press [Return] when finished.')
        # else:
        ax.legend(bbox_to_anchor=(1.15, 1))

        if ret:
            return fig, ax


Instruction:
Write a Python 3 script to
plot the rolling gradient of the analytes.

Response:
def gplot(self, analytes=None, win=25, figsize=[10, 4],
              ranges=False, focus_stage=None, ax=None, recalc=True):
        """
        Plot analytes gradients as a function of Time.

        Parameters
        ----------
        analytes : array_like
            list of strings containing names of analytes to plot.
            None = all analytes.
        win : int
            The window over which to calculate the rolling gradient.
        figsize : tuple
            size of final figure.
        ranges : bool
            show signal/background regions.

        Returns
        -------
        figure, axis
        """

        if type(analytes) is str:
            analytes = [analytes]
        if analytes is None:
            analytes = self.analytes

        if focus_stage is None:
            focus_stage = self.focus_stage

        if ax is None:
            fig = plt.figure(figsize=figsize)
            ax = fig.add_axes([.1, .12, .77, .8])
            ret = True
        else:
            fig = ax.figure
            ret = False

        x = self.Time
        if recalc or not self.grads_calced:
            self.grads = calc_grads(x, self.data[focus_stage], analytes, win)
            self.grads_calce = True

        for a in analytes:
            ax.plot(x, self.grads[a], color=self.cmap[a], label=a)

        if ranges:
            for lims in self.bkgrng:
                ax.axvspan(*lims, color='k', alpha=0.1, zorder=-1)
            for lims in self.sigrng:
                ax.axvspan(*lims, color='r', alpha=0.1, zorder=-1)

        ax.text(0.01, 0.99, self.sample + ' : ' + self.focus_stage + ' : gradient',
                transform=ax.transAxes,
                ha='left', va='top')

        ax.set_xlabel('Time (s)')
        ax.set_xlim(np.nanmin(x), np.nanmax(x))

        # y label
        ud = {'rawdata': 'counts/s',
              'despiked': 'counts/s',
              'bkgsub': 'background corrected counts/s',
              'ratios': 'counts/{:s} count/s',
              'calibrated': 'mol/mol {:s}/s'}
        if focus_stage in ['ratios', 'calibrated']:
            ud[focus_stage] = ud[focus_stage].format(self.internal_standard)
        ax.set_ylabel(ud[focus_stage])
        # y tick format

        def yfmt(x, p):
            return '{:.0e}'.format(x)
        ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(yfmt))

        ax.legend(bbox_to_anchor=(1.15, 1))

        ax.axhline(0, color='k', lw=1, ls='dashed', alpha=0.5)

        if ret:
            return fig, ax


Instruction:
Write a Python 3 function for
plotting analytes against each other.

Response:
def crossplot(dat, keys=None, lognorm=True, bins=25, figsize=(12, 12),
              colourful=True, focus_stage=None, denominator=None,
              mode='hist2d', cmap=None, **kwargs):
    """
    Plot analytes against each other.

    The number of plots is n**2 - n, where n = len(keys).

    Parameters
    ----------
    dat : dict
        A dictionary of key: data pairs, where data is the same
        length in each entry.
    keys : optional, array_like or str
        The keys of dat to plot. Defaults to all keys.
    lognorm : bool
        Whether or not to log normalise the colour scale
        of the 2D histogram.
    bins : int
        The number of bins in the 2D histogram.
    figsize : tuple
    colourful : bool

    Returns
    -------
    (fig, axes)
    """
    if keys is None:
        keys = list(dat.keys())

    numvar = len(keys)
    if figsize[0] < 1.5 * numvar:
        figsize = [1.5 * numvar] * 2

    fig, axes = plt.subplots(nrows=numvar, ncols=numvar,
                             figsize=(12, 12))
    fig.subplots_adjust(hspace=0.05, wspace=0.05)

    for ax in axes.flat:
        ax.xaxis.set_visible(False)
        ax.yaxis.set_visible(False)

        if ax.is_first_col():
            ax.yaxis.set_ticks_position('left')
        if ax.is_last_col():
            ax.yaxis.set_ticks_position('right')
        if ax.is_first_row():
            ax.xaxis.set_ticks_position('top')
        if ax.is_last_row():
            ax.xaxis.set_ticks_position('bottom')

    # set up colour scales
    if colourful:
        cmlist = ['Blues', 'BuGn', 'BuPu', 'GnBu',
                  'Greens', 'Greys', 'Oranges', 'OrRd',
                  'PuBu', 'PuBuGn', 'PuRd', 'Purples',
                  'RdPu', 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']
    else:
        cmlist = ['Greys']

    if cmap is None and mode == 'scatter':
        cmap = {k: 'k' for k in dat.keys()}

    while len(cmlist) < len(keys):
        cmlist *= 2

    # isolate nominal_values for all keys
    focus = {k: nominal_values(dat[k]) for k in keys}
    # determine units for all keys
    udict = {a: unitpicker(np.nanmean(focus[a]),
                           focus_stage=focus_stage,
                           denominator=denominator) for a in keys}
    # determine ranges for all analytes
    rdict = {a: (np.nanmin(focus[a] * udict[a][0]),
                 np.nanmax(focus[a] * udict[a][0])) for a in keys}

    for i, j in tqdm(zip(*np.triu_indices_from(axes, k=1)), desc='Drawing Plots',
                     total=sum(range(len(keys)))):
        # get analytes
        ai = keys[i]
        aj = keys[j]

        # remove nan, apply multipliers
        pi = focus[ai] * udict[ai][0]
        pj = focus[aj] * udict[aj][0]

        # determine normalisation shceme
        if lognorm:
            norm = mpl.colors.LogNorm()
        else:
            norm = None

        # draw plots
        if mode == 'hist2d':
            # remove nan
            pi = pi[~np.isnan(pi)]
            pj = pj[~np.isnan(pj)]

            axes[i, j].hist2d(pj, pi, bins,
                              norm=norm,
                              cmap=plt.get_cmap(cmlist[i]))
            axes[j, i].hist2d(pi, pj, bins,
                              norm=norm,
                              cmap=plt.get_cmap(cmlist[j]))
        elif mode == 'scatter':
            axes[i, j].scatter(pj, pi, s=10,
                               color=cmap[ai], lw=0.5, edgecolor='k',
                               alpha=0.4)
            axes[j, i].scatter(pi, pj, s=10,
                               color=cmap[aj], lw=0.5, edgecolor='k',
                               alpha=0.4)
        else:
            raise ValueError("invalid mode. Must be 'hist2d' or 'scatter'.")

        axes[i, j].set_ylim(*rdict[ai])
        axes[i, j].set_xlim(*rdict[aj])

        axes[j, i].set_ylim(*rdict[aj])
        axes[j, i].set_xlim(*rdict[ai])

    # diagonal labels
    for a, n in zip(keys, np.arange(len(keys))):
        axes[n, n].annotate(a + '\n' + udict[a][1], (0.5, 0.5),
                            xycoords='axes fraction',
                            ha='center', va='center', fontsize=8)
        axes[n, n].set_xlim(*rdict[a])
        axes[n, n].set_ylim(*rdict[a])
    # switch on alternating axes
    for i, j in zip(range(numvar), itertools.cycle((-1, 0))):
        axes[j, i].xaxis.set_visible(True)
        for label in axes[j, i].get_xticklabels():
            label.set_rotation(90)
        axes[i, j].yaxis.set_visible(True)

    return fig, axes