Instruction:
Implement a function in Python 3 to
return a list of dicts that are lists of lists of file types and headers.

Response:
def get_dicts(data):
    """
    data must be a list of lists, from a tab delimited file.
    in each list:
    the first list item will be the type of data.
    the second list item will be a tab delimited list of headers.
    the remaining items  will be a tab delimited list following the list of headers.
    """
    data_dictionaries = []
    for chunk in data[:-1]:
        if not chunk:
            continue
        data1 = data[0]
        file_type = chunk[0].split('\t')[1].strip('\n').strip('\r')
        keys = chunk[1].split('\t')
        clean_keys = []

        # remove new-line characters, and any empty string keys
        for key in keys:
            clean_key = key.strip('\n').strip('\r')
            if clean_key:
                clean_keys.append(clean_key)
        for line in chunk[2:]:
            data_dict = {}
            for key in clean_keys:
                data_dict[key] = ""
            line = line.split('\t')
            for n, key in enumerate(clean_keys):
                data_dict[key] = line[n].strip('\n').strip('\r')
            data_dict['file_type'] = file_type
            data_dictionaries.append(data_dict)
    return data_dictionaries


Instruction:
Create a Python 3 function for
determining Declination, Inclination and Intensity from the IGRF model. (http://www.ngdc.noaa.gov/IAGA/vmod/igrf.html) Parameters ---------- input_list : list with format [Date, Altitude, Latitude, Longitude] date must be in decimal year format XXXX.XXXX (Common Era) mod : desired model "" : Use the IGRF custom : use values supplied in ghfile or choose from this list ['arch3k','cals3k','pfm9k','hfm10k','cals10k.2','cals10k.1b'] where: arch3k (Korte et al., 2009) cals3k (Korte and Constable, 2011) cals10k.1b (Korte et al., 2011) pfm9k (Nilsson et al., 2014) hfm10k is the hfm.OL1.A1 of Constable et al. (2016) cals10k.2 (Constable et al., 2016) the first four of these models, are constrained to agree with gufm1 (Jackson et al., 2000) for the past four centuries gh : path to file with l m g h data Returns ------- igrf_array : array of IGRF values (0: dec; 1: inc; 2: intensity (in nT)) Examples -------- >>> local_field = ipmag.igrf([2013.6544, .052, 37.87, -122.27]) >>> local_field array([ 1.39489916e+01, 6.13532008e+01, 4.87452644e+04]) >>> ipmag.igrf_print(local_field) Declination: 13.949 Inclination: 61.353 Intensity: 48745.264 nT

Response:
def igrf(input_list, mod='', ghfile=""):
    """
    Determine Declination, Inclination and Intensity from the IGRF model.
    (http://www.ngdc.noaa.gov/IAGA/vmod/igrf.html)

    Parameters
    ----------
    input_list : list with format [Date, Altitude, Latitude, Longitude]
        date must be in decimal year format XXXX.XXXX (Common Era)
    mod :  desired model
        "" : Use the IGRF
        custom : use values supplied in ghfile
        or choose from this list
        ['arch3k','cals3k','pfm9k','hfm10k','cals10k.2','cals10k.1b']
        where:
            arch3k (Korte et al., 2009)
            cals3k (Korte and Constable, 2011)
            cals10k.1b (Korte et al., 2011)
            pfm9k  (Nilsson et al., 2014)
            hfm10k is the hfm.OL1.A1 of Constable et al. (2016)
            cals10k.2 (Constable et al., 2016)
            the first four of these models, are constrained to agree
            with gufm1 (Jackson et al., 2000) for the past four centuries

    gh : path to file with l m g h data

    Returns
    -------
    igrf_array : array of IGRF values (0: dec; 1: inc; 2: intensity (in nT))

    Examples
    --------
    >>> local_field = ipmag.igrf([2013.6544, .052, 37.87, -122.27])
    >>> local_field
    array([  1.39489916e+01,   6.13532008e+01,   4.87452644e+04])
    >>> ipmag.igrf_print(local_field)
    Declination: 13.949
    Inclination: 61.353
    Intensity: 48745.264 nT
    """
    if ghfile != "":
        lmgh = np.loadtxt(ghfile)
        gh = []
        lmgh = np.loadtxt(ghfile).transpose()
        gh.append(lmgh[2][0])
        for i in range(1, lmgh.shape[1]):
            gh.append(lmgh[2][i])
            gh.append(lmgh[3][i])
        if len(gh) == 0:
            print('no valid gh file')
            return
        mod = 'custom'
    if mod == "":
        x, y, z, f = pmag.doigrf(
            input_list[3] % 360., input_list[2], input_list[1], input_list[0])
    elif mod != 'custom':
        x, y, z, f = pmag.doigrf(
            input_list[3] % 360., input_list[2], input_list[1], input_list[0], mod=mod)
    else:
        x, y, z, f = pmag.docustom(
            input_list[3] % 360., input_list[2], input_list[1], gh)

    igrf_array = pmag.cart2dir((x, y, z))
    return igrf_array


Instruction:
Make a summary of the following Python 3 code
def dms2dd(degrees, minutes, seconds):
    dd = float(degrees) + old_div(float(minutes), 60) + \
        old_div(float(seconds), (60 * 60))
    return dd

Response: Convert latitude and longitude of a location that is in degrees minutes seconds to decimal degrees


Instruction:
Here you have a function in Python 3, explain what it does
def fisher_mean(dec=None, inc=None, di_block=None):
    if di_block is None:
        di_block = make_di_block(dec, inc)
        return pmag.fisher_mean(di_block)
    else:
        return pmag.fisher_mean(di_block)

Response: Calculates the Fisher mean and associated parameters from either a list of declination values and a separate list of inclination values or a single di_block.


Instruction:
Create a Python 3 function for
calculating the critical angle of interest from the true mean within which a chosen percentage of directions lie can be calculated from the Fisher distribution.

Response:
def fisher_angular_deviation(dec=None, inc=None, di_block=None, confidence=95):
    '''
    The angle from the true mean within which a chosen percentage of directions
    lie can be calculated from the Fisher distribution. This function uses the
    calculated Fisher concentration parameter to estimate this angle from
    directional data. The 63 percent confidence interval is often called the
    angular standard deviation.

    Parameters
    ----------
    dec : list of declinations or longitudes
    inc : list of inclinations or latitudes
    di_block : a nested list of [dec,inc,1.0]
        A di_block can be provided instead of dec, inc lists in which case it
        will be used. Either dec, inc lists or a di_block need to be provided.
    confidence : 50 percent, 63 percent or 95 percent

    Returns
    -------
    theta : critical angle of interest from the mean which contains the
        percentage of directions specified by the confidence parameter
    '''
    if di_block is None:
        di_block = make_di_block(dec, inc)
        mean = pmag.fisher_mean(di_block)
    else:
        mean = pmag.fisher_mean(di_block)
    if confidence == 50:
        theta = old_div(67.5, np.sqrt(mean['k']))
    if confidence == 63:
        theta = old_div(81, np.sqrt(mean['k']))
    if confidence == 95:
        theta = old_div(140, np.sqrt(mean['k']))
    return theta


Instruction:
Implement a Python 3 function for
calculating the Bingham mean and associated statistical parameters from a list of declination values and inclination values.

Response:
def bingham_mean(dec=None, inc=None, di_block=None):
    """
    Calculates the Bingham mean and associated statistical parameters from
    either a list of declination values and a separate list of inclination
    values or from a di_block (a nested list a nested list of [dec,inc,1.0]).
    Returns a dictionary with the Bingham mean and statistical parameters.

    Parameters
    ----------
    dec: list of declinations
    inc: list of inclinations

    or

    di_block: a nested list of [dec,inc,1.0]

    A di_block can be provided instead of dec, inc lists in which case it will
    be used. Either dec, inc lists or a di_block need to passed to the function.

    Returns
    ---------
    bpars : dictionary containing the Bingham mean and associated statistics.

    Examples
    --------
    Use lists of declination and inclination to calculate a Bingham mean:

    >>> ipmag.bingham_mean(dec=[140,127,142,136],inc=[21,23,19,22])
    {'Edec': 220.84075754194598,
     'Einc': -13.745780972597291,
     'Eta': 9.9111522306938742,
     'Zdec': 280.38894136954474,
     'Zeta': 9.8653370276451113,
     'Zinc': 64.23509410796224,
     'dec': 136.32637167111312,
     'inc': 21.34518678073179,
     'n': 4}

    Use a di_block to calculate a Bingham mean (will give the same output as the
    example with the lists):

    >>> ipmag.bingham_mean(di_block=[[140,21],[127,23],[142,19],[136,22]])
    """
    if di_block is None:
        di_block = make_di_block(dec, inc)
        return pmag.dobingham(di_block)
    else:
        return pmag.dobingham(di_block)


Instruction:
Implement a function in Python 3 to
calculate the Kent mean and associated statistical parameters from either a list of declination values and a separate list of inclination values or a single di_block.

Response:
def kent_mean(dec=None, inc=None, di_block=None):
    """
    Calculates the Kent mean and associated statistical parameters from either a list of
    declination values and a separate list of inclination values or from a
    di_block (a nested list a nested list of [dec,inc,1.0]). Returns a
    dictionary with the Kent mean and statistical parameters.

    Parameters
    ----------
    dec: list of declinations
    inc: list of inclinations

    or

    di_block: a nested list of [dec,inc,1.0]

    A di_block can be provided instead of dec, inc lists in which case it will
    be used. Either dec, inc lists or a di_block need to passed to the function.

    Returns
    ----------
    kpars : dictionary containing Kent mean and associated statistics.

    Examples
    --------
    Use lists of declination and inclination to calculate a Kent mean:

    >>> ipmag.kent_mean(dec=[140,127,142,136],inc=[21,23,19,22])
    {'Edec': 280.38683553668795,
     'Einc': 64.236598921744289,
     'Eta': 0.72982112760919715,
     'Zdec': 40.824690028412761,
     'Zeta': 6.7896823241008795,
     'Zinc': 13.739412321974067,
     'dec': 136.30838974272072,
     'inc': 21.347784026899987,
     'n': 4}

    Use a di_block to calculate a Kent mean (will give the same output as the
    example with the lists):

    >>> ipmag.kent_mean(di_block=[[140,21],[127,23],[142,19],[136,22]])
    """
    if di_block is None:
        di_block = make_di_block(dec, inc)
        return pmag.dokent(di_block, len(di_block))
    else:
        return pmag.dokent(di_block, len(di_block))


Instruction:
Can you generate a brief explanation for the following Python 3 code
def print_direction_mean(mean_dictionary):
    print('Dec: ' + str(round(mean_dictionary['dec'], 1)) +
          '  Inc: ' + str(round(mean_dictionary['inc'], 1)))
    print('Number of directions in mean (n): ' + str(mean_dictionary['n']))
    print('Angular radius of 95% confidence (a_95): ' +
          str(round(mean_dictionary['alpha95'], 1)))
    print('Precision parameter (k) estimate: ' +
          str(round(mean_dictionary['k'], 1)))

Response: This function prints a Fisher mean and associated statistics for the directional data.


Instruction:
Write a Python 3 function for
doing a pretty job printing a Fisher mean and associated statistics for mean paleomagnetic poles. Parameters ---------- mean_dictionary: output dictionary of pmag.fisher_mean Examples -------- Generate a Fisher mean using ``ipmag.fisher_mean`` and then print it nicely using ``ipmag.print_pole_mean`` >>> my_mean = ipmag.fisher_mean(di_block=[[140,21],[127,23],[142,19],[136,22]]) >>> ipmag.print_pole_mean(my_mean) Plon: 136.3 Plat: 21.3 Number of directions in mean (n): 4 Angular radius of 95% confidence (A_95): 7.3 Precision parameter (k) estimate: 159.7

Response:
def print_pole_mean(mean_dictionary):
    """
    Does a pretty job printing a Fisher mean and associated statistics for
    mean paleomagnetic poles.

    Parameters
    ----------
    mean_dictionary: output dictionary of pmag.fisher_mean

    Examples
    --------
    Generate a Fisher mean using ``ipmag.fisher_mean`` and then print it nicely
    using ``ipmag.print_pole_mean``

    >>> my_mean = ipmag.fisher_mean(di_block=[[140,21],[127,23],[142,19],[136,22]])
    >>> ipmag.print_pole_mean(my_mean)
    Plon: 136.3  Plat: 21.3
    Number of directions in mean (n): 4
    Angular radius of 95% confidence (A_95): 7.3
    Precision parameter (k) estimate: 159.7
    """
    print('Plon: ' + str(round(mean_dictionary['dec'], 1)) +
          '  Plat: ' + str(round(mean_dictionary['inc'], 1)))
    print('Number of directions in mean (n): ' + str(mean_dictionary['n']))
    print('Angular radius of 95% confidence (A_95): ' +
          str(round(mean_dictionary['alpha95'], 1)))
    print('Precision parameter (k) estimate: ' +
          str(round(mean_dictionary['k'], 1)))


Instruction:
Can you tell what is the following Python 3 function doing
def fishrot(k=20, n=100, dec=0, inc=90, di_block=True):
    directions = []
    declinations = []
    inclinations = []
    if di_block == True:
        for data in range(n):
            d, i = pmag.fshdev(k)
            drot, irot = pmag.dodirot(d, i, dec, inc)
            directions.append([drot, irot, 1.])
        return directions
    else:
        for data in range(n):
            d, i = pmag.fshdev(k)
            drot, irot = pmag.dodirot(d, i, dec, inc)
            declinations.append(drot)
            inclinations.append(irot)
        return declinations, inclinations

Response:
This function generates Fisher distributed unit vectors from a specified distribution
   .


Instruction:
Explain what the following Python 3 code does
def tk03(n=100, dec=0, lat=0, rev='no', G2=0, G3=0):
    tk_03_output = []
    for k in range(n):
        gh = pmag.mktk03(8, k, G2, G3)  # terms and random seed
        # get a random longitude, between 0 and 359
        lon = random.randint(0, 360)
        vec = pmag.getvec(gh, lat, lon)  # send field model and lat to getvec
        vec[0] += dec
        if vec[0] >= 360.:
            vec[0] -= 360.
        if k % 2 == 0 and rev == 'yes':
            vec[0] += 180.
            vec[1] = -vec[1]
        tk_03_output.append([vec[0], vec[1], vec[2]])
    return tk_03_output

Response: Returns a nested list of declination inclination and intensity of a secular variation.


Instruction:
Make a summary of the following Python 3 code
def squish(incs, f):
    try:
        length = len(incs)
        incs_squished = []
        for n in range(0, length):
            inc_rad = incs[n] * np.pi / 180.  # convert to radians
            inc_new_rad = f * np.tan(inc_rad)
            inc_new = np.arctan(inc_new_rad) * 180. / \
                np.pi  # convert back to degrees
            incs_squished.append(inc_new)
        return incs_squished
    except:
        inc_rad = incs * np.pi / 180.  # convert to radians
        inc_new_rad = f * np.tan(inc_rad)
        inc_new = np.arctan(inc_new_rad) * 180. / \
            np.pi  # convert back to degrees
        return inc_new

Response: This function applies an flattening factor to inclination data and returns a list of squished values.


Instruction:
Can you tell what is the following Python 3 function doing
def do_flip(dec=None, inc=None, di_block=None):
    if di_block is None:
        dec_flip = []
        inc_flip = []
        for n in range(0, len(dec)):
            dec_flip.append((dec[n] - 180.) % 360.0)
            inc_flip.append(-inc[n])
        return dec_flip, inc_flip
    else:
        dflip = []
        for rec in di_block:
            d, i = (rec[0] - 180.) % 360., -rec[1]
            dflip.append([d, i, 1.0])
        return dflip

Response: This function flips the declination and inclination of a single antipode into a single antipode.


Instruction:
Create a Python 3 function to
conduct a bootstrap test (Tauxe, 2010) for a common mean on two declination, inclination data sets. Plots are generated of the cumulative distributions of the Cartesian coordinates of the means of the pseudo-samples (one for x, one for y and one for z). If the 95 percent confidence bounds for each component overlap, the two directions are not significantly different. Parameters ---------- Data1 : a nested list of directional data [dec,inc] (a di_block) Data2 : a nested list of directional data [dec,inc] (a di_block) if Data2 is length of 1, treat as single direction NumSims : number of bootstrap samples (default is 1000) save : optional save of plots (default is False) save_folder : path to directory where plots should be saved fmt : format of figures to be saved (default is 'svg') figsize : optionally adjust figure size (default is (7, 2.3)) x_tick_bins : because they occasionally overlap depending on the data, this argument allows you adjust number of tick marks on the x axis of graphs (default is 4) Returns ------- three plots : cumulative distributions of the X, Y, Z of bootstrapped means Examples -------- Develop two populations of directions using ``ipmag.fishrot``. Use the function to determine if they share a common mean (through visual inspection of resulting plots). >>> directions_A = ipmag.fishrot(k=20, n=30, dec=40, inc=60) >>> directions_B = ipmag.fishrot(k=35, n=25, dec=42, inc=57) >>> ipmag.common_mean_bootstrap(directions_A, directions_B)

Response:
def common_mean_bootstrap(Data1, Data2, NumSims=1000, save=False, save_folder='.', fmt='svg', figsize=(7, 2.3), x_tick_bins=4):
    """
    Conduct a bootstrap test (Tauxe, 2010) for a common mean on two declination,
    inclination data sets. Plots are generated of the cumulative distributions
    of the Cartesian coordinates of the means of the pseudo-samples (one for x,
    one for y and one for z). If the 95 percent confidence bounds for each
    component overlap, the two directions are not significantly different.

    Parameters
    ----------
    Data1 : a nested list of directional data [dec,inc] (a di_block)
    Data2 : a nested list of directional data [dec,inc] (a di_block)
            if Data2 is length of 1, treat as single direction
    NumSims : number of bootstrap samples (default is 1000)
    save : optional save of plots (default is False)
    save_folder : path to directory where plots should be saved
    fmt : format of figures to be saved (default is 'svg')
    figsize : optionally adjust figure size (default is (7, 2.3))
    x_tick_bins : because they occasionally overlap depending on the data, this
        argument allows you adjust number of tick marks on the x axis of graphs
        (default is 4)

    Returns
    -------
    three plots : cumulative distributions of the X, Y, Z of bootstrapped means

    Examples
    --------
    Develop two populations of directions using ``ipmag.fishrot``. Use the
    function to determine if they share a common mean (through visual inspection
    of resulting plots).

    >>> directions_A = ipmag.fishrot(k=20, n=30, dec=40, inc=60)
    >>> directions_B = ipmag.fishrot(k=35, n=25, dec=42, inc=57)
    >>> ipmag.common_mean_bootstrap(directions_A, directions_B)
    """
    counter = 0
    BDI1 = pmag.di_boot(Data1)
    cart1 = pmag.dir2cart(BDI1).transpose()
    X1, Y1, Z1 = cart1[0], cart1[1], cart1[2]
    if np.array(Data2).shape[0] > 2:
        BDI2 = pmag.di_boot(Data2)
        cart2 = pmag.dir2cart(BDI2).transpose()
        X2, Y2, Z2 = cart2[0], cart2[1], cart2[2]
    else:
        cart = pmag.dir2cart(Data2).transpose()

    fignum = 1
    fig = plt.figure(figsize=figsize)
    fig = plt.subplot(1, 3, 1)

    minimum = int(0.025 * len(X1))
    maximum = int(0.975 * len(X1))

    X1, y = pmagplotlib.plot_cdf(fignum, X1, "X component", 'r', "")
    bounds1 = [X1[minimum], X1[maximum]]
    pmagplotlib.plot_vs(fignum, bounds1, 'r', '-')
    if np.array(Data2).shape[0] > 2:
        X2, y = pmagplotlib.plot_cdf(fignum, X2, "X component", 'b', "")
        bounds2 = [X2[minimum], X2[maximum]]
        pmagplotlib.plot_vs(fignum, bounds2, 'b', '--')
    else:
        pmagplotlib.plot_vs(fignum, [cart[0]], 'k', '--')
    plt.ylim(0, 1)
    plt.locator_params(nbins=x_tick_bins)

    plt.subplot(1, 3, 2)

    Y1, y = pmagplotlib.plot_cdf(fignum, Y1, "Y component", 'r', "")
    bounds1 = [Y1[minimum], Y1[maximum]]
    pmagplotlib.plot_vs(fignum, bounds1, 'r', '-')
    if np.array(Data2).shape[0] > 2:
        Y2, y = pmagplotlib.plot_cdf(fignum, Y2, "Y component", 'b', "")
        bounds2 = [Y2[minimum], Y2[maximum]]
        pmagplotlib.plot_vs(fignum, bounds2, 'b', '--')
    else:
        pmagplotlib.plot_vs(fignum, [cart[1]], 'k', '--')
    plt.ylim(0, 1)

    plt.subplot(1, 3, 3)

    Z1, y = pmagplotlib.plot_cdf(fignum, Z1, "Z component", 'r', "")
    bounds1 = [Z1[minimum], Z1[maximum]]
    pmagplotlib.plot_vs(fignum, bounds1, 'r', '-')

    if np.array(Data2).shape[0] > 2:
        Z2, y = pmagplotlib.plot_cdf(fignum, Z2, "Z component", 'b', "")
        bounds2 = [Z2[minimum], Z2[maximum]]
        pmagplotlib.plot_vs(fignum, bounds2, 'b', '--')
    else:
        pmagplotlib.plot_vs(fignum, [cart[2]], 'k', '--')
    plt.ylim(0, 1)
    plt.locator_params(nbins=x_tick_bins)

    plt.tight_layout()
    if save == True:
        plt.savefig(os.path.join(
            save_folder, 'common_mean_bootstrap') + '.' + fmt)
    plt.show()


Instruction:
How would you code a function in Python 3 to
conduct a Watson V test for a common mean on two directional data sets. This function calculates Watson's V statistic from input files through Monte Carlo simulation in order to test whether two populations of directional data could have been drawn from a common mean. The critical angle between the two sample mean directions and the corresponding McFadden and McElhinny (1990) classification is printed. Parameters ---------- Data1 : a nested list of directional data [dec,inc] (a di_block) Data2 : a nested list of directional data [dec,inc] (a di_block) NumSims : number of Monte Carlo simulations (default is 5000) print_result : default is to print the test result (True) plot : the default is no plot ('no'). Putting 'yes' will the plot the CDF from the Monte Carlo simulations. save : optional save of plots (default is False) save_folder : path to where plots will be saved (default is current) fmt : format of figures to be saved (default is 'svg') Returns ------- printed text : text describing the test result is printed result : a boolean where 0 is fail and 1 is pass angle : angle between the Fisher means of the two data sets critical_angle : critical angle for the test to pass Examples -------- Develop two populations of directions using ``ipmag.fishrot``. Use the function to determine if they share a common mean. >>> directions_A = ipmag.fishrot(k=20, n=30, dec=40, inc=60) >>> directions_B = ipmag.fishrot(k=35, n=25, dec=42, inc=57) >>> ipmag.common_mean_watson(directions_A, directions_B)

Response:
def common_mean_watson(Data1, Data2, NumSims=5000, print_result=True, plot='no', save=False, save_folder='.', fmt='svg'):
    """
    Conduct a Watson V test for a common mean on two directional data sets.

    This function calculates Watson's V statistic from input files through
    Monte Carlo simulation in order to test whether two populations of
    directional data could have been drawn from a common mean. The critical
    angle between the two sample mean directions and the corresponding
    McFadden and McElhinny (1990) classification is printed.

    Parameters
    ----------
    Data1 : a nested list of directional data [dec,inc] (a di_block)
    Data2 : a nested list of directional data [dec,inc] (a di_block)
    NumSims : number of Monte Carlo simulations (default is 5000)
    print_result : default is to print the test result (True)
    plot : the default is no plot ('no'). Putting 'yes' will the plot the CDF
        from the Monte Carlo simulations.
    save : optional save of plots (default is False)
    save_folder : path to where plots will be saved (default is current)
    fmt : format of figures to be saved (default is 'svg')

    Returns
    -------
    printed text : text describing the test result is printed
    result : a boolean where 0 is fail and 1 is pass
    angle : angle between the Fisher means of the two data sets
    critical_angle : critical angle for the test to pass

    Examples
    --------
    Develop two populations of directions using ``ipmag.fishrot``. Use the
    function to determine if they share a common mean.

    >>> directions_A = ipmag.fishrot(k=20, n=30, dec=40, inc=60)
    >>> directions_B = ipmag.fishrot(k=35, n=25, dec=42, inc=57)
    >>> ipmag.common_mean_watson(directions_A, directions_B)
    """
    pars_1 = pmag.fisher_mean(Data1)
    pars_2 = pmag.fisher_mean(Data2)
    cart_1 = pmag.dir2cart([pars_1["dec"], pars_1["inc"], pars_1["r"]])
    cart_2 = pmag.dir2cart([pars_2['dec'], pars_2['inc'], pars_2["r"]])
    Sw = pars_1['k'] * pars_1['r'] + pars_2['k'] * pars_2['r']  # k1*r1+k2*r2
    xhat_1 = pars_1['k'] * cart_1[0] + pars_2['k'] * cart_2[0]  # k1*x1+k2*x2
    xhat_2 = pars_1['k'] * cart_1[1] + pars_2['k'] * cart_2[1]  # k1*y1+k2*y2
    xhat_3 = pars_1['k'] * cart_1[2] + pars_2['k'] * cart_2[2]  # k1*z1+k2*z2
    Rw = np.sqrt(xhat_1**2 + xhat_2**2 + xhat_3**2)
    V = 2 * (Sw - Rw)
    # keep weighted sum for later when determining the "critical angle"
    # let's save it as Sr (notation of McFadden and McElhinny, 1990)
    Sr = Sw

    # do monte carlo simulation of datasets with same kappas as data,
    # but a common mean
    counter = 0
    Vp = []  # set of Vs from simulations
    for k in range(NumSims):

        # get a set of N1 fisher distributed vectors with k1,
        # calculate fisher stats
        Dirp = []
        for i in range(pars_1["n"]):
            Dirp.append(pmag.fshdev(pars_1["k"]))
        pars_p1 = pmag.fisher_mean(Dirp)
    # get a set of N2 fisher distributed vectors with k2,
    # calculate fisher stats
        Dirp = []
        for i in range(pars_2["n"]):
            Dirp.append(pmag.fshdev(pars_2["k"]))
        pars_p2 = pmag.fisher_mean(Dirp)
    # get the V for these
        Vk = pmag.vfunc(pars_p1, pars_p2)
        Vp.append(Vk)

    # sort the Vs, get Vcrit (95th percentile one)

    Vp.sort()
    k = int(.95 * NumSims)
    Vcrit = Vp[k]

    # equation 18 of McFadden and McElhinny, 1990 calculates the critical
    # value of R (Rwc)

    Rwc = Sr - (old_div(Vcrit, 2))

    # following equation 19 of McFadden and McElhinny (1990) the critical
    # angle is calculated. If the observed angle (also calculated below)
    # between the data set means exceeds the critical angle the hypothesis
    # of a common mean direction may be rejected at the 95% confidence
    # level. The critical angle is simply a different way to present
    # Watson's V parameter so it makes sense to use the Watson V parameter
    # in comparison with the critical value of V for considering the test
    # results. What calculating the critical angle allows for is the
    # classification of McFadden and McElhinny (1990) to be made
    # for data sets that are consistent with sharing a common mean.

    k1 = pars_1['k']
    k2 = pars_2['k']
    R1 = pars_1['r']
    R2 = pars_2['r']
    critical_angle = np.degrees(np.arccos(old_div(((Rwc**2) - ((k1 * R1)**2)
                                                   - ((k2 * R2)**2)),
                                                  (2 * k1 * R1 * k2 * R2))))
    D1 = (pars_1['dec'], pars_1['inc'])
    D2 = (pars_2['dec'], pars_2['inc'])
    angle = pmag.angle(D1, D2)

    if print_result == True:
        print("Results of Watson V test: ")
        print("")
        print("Watson's V:           " '%.1f' % (V))
        print("Critical value of V:  " '%.1f' % (Vcrit))

    if V < Vcrit:
        if print_result == True:
            print('"Pass": Since V is less than Vcrit, the null hypothesis')
            print('that the two populations are drawn from distributions')
            print('that share a common mean direction can not be rejected.')
        result = 1
    elif V > Vcrit:
        if print_result == True:
            print('"Fail": Since V is greater than Vcrit, the two means can')
            print('be distinguished at the 95% confidence level.')
        result = 0

    if print_result == True:
        print("")
        print("M&M1990 classification:")
        print("")
        print("Angle between data set means: " '%.1f' % (angle))
        print("Critical angle for M&M1990:   " '%.1f' % (critical_angle))

    if print_result == True:
        if V > Vcrit:
            print("")
        elif V < Vcrit:
            if critical_angle < 5:
                print("The McFadden and McElhinny (1990) classification for")
                print("this test is: 'A'")
            elif critical_angle < 10:
                print("The McFadden and McElhinny (1990) classification for")
                print("this test is: 'B'")
            elif critical_angle < 20:
                print("The McFadden and McElhinny (1990) classification for")
                print("this test is: 'C'")
            else:
                print("The McFadden and McElhinny (1990) classification for")
                print("this test is: 'INDETERMINATE;")

    if plot == 'yes':
        CDF = {'cdf': 1}
        # pmagplotlib.plot_init(CDF['cdf'],5,5)
        plt.figure(figsize=(3.5, 2.5))
        p1 = pmagplotlib.plot_cdf(CDF['cdf'], Vp, "Watson's V", 'r', "")
        p2 = pmagplotlib.plot_vs(CDF['cdf'], [V], 'g', '-')
        p3 = pmagplotlib.plot_vs(CDF['cdf'], [Vp[k]], 'b', '--')
        # pmagplotlib.draw_figs(CDF)
        if save == True:
            plt.savefig(os.path.join(
                save_folder, 'common_mean_watson') + '.' + fmt)
        pmagplotlib.show_fig(CDF['cdf'])

    return result, angle[0], critical_angle


Instruction:
Can you tell what is the following Python 3 function doing
def fishqq(lon=None, lat=None, di_block=None):
    if di_block is None:
        all_dirs = make_di_block(lon, lat)
    else:
        all_dirs = di_block

    ppars = pmag.doprinc(all_dirs)  # get principal directions

    rDIs = []
    nDIs = []
    QQ_dict1 = {}
    QQ_dict2 = {}

    for rec in all_dirs:
        angle = pmag.angle([rec[0], rec[1]], [ppars['dec'], ppars['inc']])
        if angle > 90.:
            rDIs.append(rec)
        else:
            nDIs.append(rec)

    if len(rDIs) >= 10 or len(nDIs) >= 10:
        D1, I1 = [], []
        QQ = {'unf': 1, 'exp': 2}
        if len(nDIs) < 10:
            ppars = pmag.doprinc(rDIs)  # get principal directions
            Drbar, Irbar = ppars['dec'] - 180., -ppars['inc']
            Nr = len(rDIs)
            for di in rDIs:
                d, irot = pmag.dotilt(
                    di[0], di[1], Drbar - 180., 90. - Irbar)  # rotate to mean
                drot = d - 180.
                if drot < 0:
                    drot = drot + 360.
                D1.append(drot)
                I1.append(irot)
                Dtit = 'Mode 2 Declinations'
                Itit = 'Mode 2 Inclinations'
        else:
            ppars = pmag.doprinc(nDIs)  # get principal directions
            Dnbar, Inbar = ppars['dec'], ppars['inc']
            Nn = len(nDIs)
            for di in nDIs:
                d, irot = pmag.dotilt(
                    di[0], di[1], Dnbar - 180., 90. - Inbar)  # rotate to mean
                drot = d - 180.
                if drot < 0:
                    drot = drot + 360.
                D1.append(drot)
                I1.append(irot)
                Dtit = 'Mode 1 Declinations'
                Itit = 'Mode 1 Inclinations'
        plt.figure(figsize=(6, 3))
        Mu_n, Mu_ncr = pmagplotlib.plot_qq_unf(
            QQ['unf'], D1, Dtit, subplot=True)  # make plot
        Me_n, Me_ncr = pmagplotlib.plot_qq_exp(
            QQ['exp'], I1, Itit, subplot=True)  # make plot
        plt.tight_layout()
        if Mu_n <= Mu_ncr and Me_n <= Me_ncr:
            F_n = 'consistent with Fisherian model'
        else:
            F_n = 'Fisherian model rejected'
        QQ_dict1['Mode'] = 'Mode 1'
        QQ_dict1['Dec'] = Dnbar
        QQ_dict1['Inc'] = Inbar
        QQ_dict1['N'] = Nn
        QQ_dict1['Mu'] = Mu_n
        QQ_dict1['Mu_critical'] = Mu_ncr
        QQ_dict1['Me'] = Me_n
        QQ_dict1['Me_critical'] = Me_ncr
        QQ_dict1['Test_result'] = F_n

    if len(rDIs) > 10 and len(nDIs) > 10:
        D2, I2 = [], []
        ppars = pmag.doprinc(rDIs)  # get principal directions
        Drbar, Irbar = ppars['dec'] - 180., -ppars['inc']
        Nr = len(rDIs)
        for di in rDIs:
            d, irot = pmag.dotilt(
                di[0], di[1], Drbar - 180., 90. - Irbar)  # rotate to mean
            drot = d - 180.
            if drot < 0:
                drot = drot + 360.
            D2.append(drot)
            I2.append(irot)
            Dtit = 'Mode 2 Declinations'
            Itit = 'Mode 2 Inclinations'
        plt.figure(figsize=(6, 3))
        Mu_r, Mu_rcr = pmagplotlib.plot_qq_unf(
            QQ['unf'], D2, Dtit, subplot=True)  # make plot
        Me_r, Me_rcr = pmagplotlib.plot_qq_exp(
            QQ['exp'], I2, Itit, subplot=True)  # make plot
        plt.tight_layout()

        if Mu_r <= Mu_rcr and Me_r <= Me_rcr:
            F_r = 'consistent with Fisherian model'
        else:
            F_r = 'Fisherian model rejected'
        QQ_dict2['Mode'] = 'Mode 2'
        QQ_dict2['Dec'] = Drbar
        QQ_dict2['Inc'] = Irbar
        QQ_dict2['N'] = Nr
        QQ_dict2['Mu'] = Mu_r
        QQ_dict2['Mu_critical'] = Mu_rcr
        QQ_dict2['Me'] = Me_r
        QQ_dict2['Me_critical'] = Me_rcr
        QQ_dict2['Test_result'] = F_r

    if QQ_dict2:
        return QQ_dict1, QQ_dict2
    elif QQ_dict1:
        return QQ_dict1
    else:
        print('you need N> 10 for at least one mode')

Response: Test whether a Fisher distribution is Fisherian and make a corresponding Q - Q plot.


Instruction:
Create a Python 3 function to
calculate the paleolatitude from inclination using the dipole equation

Response:
def lat_from_inc(inc, a95=None):
    """
    Calculate paleolatitude from inclination using the dipole equation

    Required Parameter
    ----------
    inc: (paleo)magnetic inclination in degrees

    Optional Parameter
    ----------
    a95: 95% confidence interval from Fisher mean

    Returns
    ----------
    if a95 is provided paleo_lat, paleo_lat_max, paleo_lat_min are returned
    otherwise, it just returns paleo_lat
    """
    rad = old_div(np.pi, 180.)
    paleo_lat = old_div(np.arctan(0.5 * np.tan(inc * rad)), rad)
    if a95 is not None:
        paleo_lat_max = old_div(
            np.arctan(0.5 * np.tan((inc + a95) * rad)), rad)
        paleo_lat_min = old_div(
            np.arctan(0.5 * np.tan((inc - a95) * rad)), rad)
        return paleo_lat, paleo_lat_max, paleo_lat_min
    else:
        return paleo_lat


Instruction:
Create a Python 3 function to
calculate the paleolatitude for a reference location based on a paleomagnetic pole

Response:
def lat_from_pole(ref_loc_lon, ref_loc_lat, pole_plon, pole_plat):
    """
    Calculate paleolatitude for a reference location based on a paleomagnetic pole

    Required Parameters
    ----------
    ref_loc_lon: longitude of reference location in degrees
    ref_loc_lat: latitude of reference location
    pole_plon: paleopole longitude in degrees
    pole_plat: paleopole latitude in degrees
    """

    ref_loc = (ref_loc_lon, ref_loc_lat)
    pole = (pole_plon, pole_plat)
    paleo_lat = 90 - pmag.angle(pole, ref_loc)
    return float(paleo_lat)


Instruction:
Given the following Python 3 function, write the documentation
def inc_from_lat(lat):
    rad = old_div(np.pi, 180.)
    inc = old_div(np.arctan(2 * np.tan(lat * rad)), rad)
    return inc

Response: Calculate inclination predicted from latitude using the dipole equation


Instruction:
How would you explain what the following Python 3 function does
def plot_net(fignum):

# make the perimeter
    plt.figure(num=fignum,)
    plt.clf()
    plt.axis("off")
    Dcirc = np.arange(0, 361.)
    Icirc = np.zeros(361, 'f')
    Xcirc, Ycirc = [], []
    for k in range(361):
        XY = pmag.dimap(Dcirc[k], Icirc[k])
        Xcirc.append(XY[0])
        Ycirc.append(XY[1])
    plt.plot(Xcirc, Ycirc, 'k')

# put on the tick marks
    Xsym, Ysym = [], []
    for I in range(10, 100, 10):
        XY = pmag.dimap(0., I)
        Xsym.append(XY[0])
        Ysym.append(XY[1])
    plt.plot(Xsym, Ysym, 'k+')
    Xsym, Ysym = [], []
    for I in range(10, 90, 10):
        XY = pmag.dimap(90., I)
        Xsym.append(XY[0])
        Ysym.append(XY[1])
    plt.plot(Xsym, Ysym, 'k+')
    Xsym, Ysym = [], []
    for I in range(10, 90, 10):
        XY = pmag.dimap(180., I)
        Xsym.append(XY[0])
        Ysym.append(XY[1])
    plt.plot(Xsym, Ysym, 'k+')
    Xsym, Ysym = [], []
    for I in range(10, 90, 10):
        XY = pmag.dimap(270., I)
        Xsym.append(XY[0])
        Ysym.append(XY[1])
    plt.plot(Xsym, Ysym, 'k+')
    for D in range(0, 360, 10):
        Xtick, Ytick = [], []
        for I in range(4):
            XY = pmag.dimap(D, I)
            Xtick.append(XY[0])
            Ytick.append(XY[1])
        plt.plot(Xtick, Ytick, 'k')
    plt.axis("equal")
    plt.axis((-1.05, 1.05, -1.05, 1.05))

Response: Plots the perimeter and tick marks for equal area projection.


Instruction:
Here you have a function in Python 3, explain what it does
def plot_di(dec=None, inc=None, di_block=None, color='k', marker='o', markersize=20, legend='no', label='', title='', edge='',alpha=1):
    X_down = []
    X_up = []
    Y_down = []
    Y_up = []
    color_down = []
    color_up = []

    if di_block is not None:
        di_lists = unpack_di_block(di_block)
        if len(di_lists) == 3:
            dec, inc, intensity = di_lists
        if len(di_lists) == 2:
            dec, inc = di_lists
    try:
        length = len(dec)
        for n in range(len(dec)):
            XY = pmag.dimap(dec[n], inc[n])
            if inc[n] >= 0:
                X_down.append(XY[0])
                Y_down.append(XY[1])
                if type(color) == list:
                    color_down.append(color[n])
                else:
                    color_down.append(color)
            else:
                X_up.append(XY[0])
                Y_up.append(XY[1])
                if type(color) == list:
                    color_up.append(color[n])
                else:
                    color_up.append(color)
    except:
        XY = pmag.dimap(dec, inc)
        if inc >= 0:
            X_down.append(XY[0])
            Y_down.append(XY[1])
            color_down.append(color)
        else:
            X_up.append(XY[0])
            Y_up.append(XY[1])
            color_up.append(color)

    if len(X_up) > 0:
        plt.scatter(X_up, Y_up, facecolors='none', edgecolors=color_up,
                    s=markersize, marker=marker, label=label,alpha=alpha)

    if len(X_down) > 0:
        plt.scatter(X_down, Y_down, facecolors=color_down, edgecolors=edge,
                    s=markersize, marker=marker, label=label,alpha=alpha)
    if legend == 'yes':
        plt.legend(loc=2)
    plt.tight_layout()
    if title != "":
        plt.title(title)

Response: This function plots declination inclination data on an equal area plot.


Instruction:
Given the following Python 3 function, write the documentation
def plot_di_mean(dec, inc, a95, color='k', marker='o', markersize=20, label='', legend='no'):
    DI_dimap = pmag.dimap(dec, inc)
    if inc < 0:
        plt.scatter(DI_dimap[0], DI_dimap[1],
                    edgecolors=color, facecolors='white',
                    marker=marker, s=markersize, label=label)
    if inc >= 0:
        plt.scatter(DI_dimap[0], DI_dimap[1],
                    edgecolors=color, facecolors=color,
                    marker=marker, s=markersize, label=label)
    Xcirc, Ycirc = [], []
    Da95, Ia95 = pmag.circ(dec, inc, a95)
    if legend == 'yes':
        plt.legend(loc=2)
    for k in range(len(Da95)):
        XY = pmag.dimap(Da95[k], Ia95[k])
        Xcirc.append(XY[0])
        Ycirc.append(XY[1])
    plt.plot(Xcirc, Ycirc, c=color)
    plt.tight_layout()

Response: Plots a mean direction of declination inclination and alpha_95 ellipse on the code tree.


Instruction:
Here you have a function in Python 3, explain what it does
def plot_di_mean_bingham(bingham_dictionary, fignum=1, color='k', marker='o', markersize=20, label='', legend='no'):
    plot_di_mean_ellipse(bingham_dictionary, fignum=fignum, color=color,
                         marker=marker, markersize=markersize, label=label, legend=legend)

Response: plot di mean ellipse of a bingham dictionary


Instruction:
Make a summary of the following Python 3 code
def plot_di_mean_ellipse(dictionary, fignum=1, color='k', marker='o', markersize=20, label='', legend='no'):
    pars = []
    pars.append(dictionary['dec'])
    pars.append(dictionary['inc'])
    pars.append(dictionary['Zeta'])
    pars.append(dictionary['Zdec'])
    pars.append(dictionary['Zinc'])
    pars.append(dictionary['Eta'])
    pars.append(dictionary['Edec'])
    pars.append(dictionary['Einc'])

    DI_dimap = pmag.dimap(dictionary['dec'], dictionary['inc'])
    if dictionary['inc'] < 0:
        plt.scatter(DI_dimap[0], DI_dimap[1],
                    edgecolors=color, facecolors='white',
                    marker=marker, s=markersize, label=label)
    if dictionary['inc'] >= 0:
        plt.scatter(DI_dimap[0], DI_dimap[1],
                    edgecolors=color, facecolors=color,
                    marker=marker, s=markersize, label=label)
    pmagplotlib.plot_ell(fignum, pars, color, 0, 1)

Response: Plots a confidence ellipse of the di_mean_ellipse of the given dictionary.


Instruction:
Write a Python 3 function for
functioning creates and returns an orthographic map projection using cartopy

Response:
def make_orthographic_map(central_longitude=0, central_latitude=0, figsize=(8, 8),
                          add_land=True, land_color='tan', add_ocean=False, ocean_color='lightblue', grid_lines=True,
                          lat_grid=[-80.,  -60.,  -30.,
                                    0.,   30.,   60., 80.],
                          lon_grid=[-180., -150., -120.,  -90.,  -60.,  -30.,    0.,   30.,   60., 90.,  120.,  150.,  180.]):
    '''
    Function creates and returns an orthographic map projection using cartopy

    Example
    -------
    >>> map_axis = make_orthographic_map(central_longitude=200,central_latitude=30)

    Optional Parameters
    -----------
    central_longitude : central longitude of projection (default is 0)
    central_latitude : central latitude of projection (default is 0)
    figsize : size of the figure (default is 8x8)
    add_land : chose whether land is plotted on map (default is true)
    land_color : specify land color (default is 'tan')
    add_ocean : chose whether land is plotted on map (default is False, change to True to plot)
    ocean_color : specify ocean color (default is 'lightblue')
    grid_lines : chose whether gird lines are plotted on map (default is true)
    lat_grid : specify the latitude grid (default is 30 degree spacing)
    lon_grid : specify the longitude grid (default is 30 degree spacing)
    '''
    if not has_cartopy:
        print('-W- cartopy must be installed to run ipmag.make_orthographic_map')
        return
    fig = plt.figure(figsize=figsize)
    map_projection = ccrs.Orthographic(
        central_longitude=central_longitude, central_latitude=central_latitude)
    ax = plt.axes(projection=map_projection)
    ax.set_global()
    if add_ocean == True:
        ax.add_feature(cartopy.feature.OCEAN, zorder=0, facecolor=ocean_color)
    if add_land == True:
        ax.add_feature(cartopy.feature.LAND, zorder=0,
                       facecolor=land_color, edgecolor='black')
    if grid_lines == True:
        ax.gridlines(xlocs=lon_grid, ylocs=lat_grid, linewidth=1,
                     color='black', linestyle='dotted')
    return ax


Instruction:
Explain what the following Python 3 code does
def plot_pole_basemap(mapname, plon, plat, A95, label='', color='k', edgecolor='k', marker='o', markersize=20, legend='no'):
    centerlon, centerlat = mapname(plon, plat)
    A95_km = A95 * 111.32
    mapname.scatter(centerlon, centerlat, marker=marker,
                    color=color, edgecolors=edgecolor, s=markersize, label=label, zorder=101)
    equi_basemap(mapname, plon, plat, A95_km, color)
    if legend == 'yes':
        plt.legend(loc=2)

Response: This function plots a paleomagnetic pole and A95 error ellipse on the current map.


Instruction:
Can you tell what is the following Python 3 function doing
def plot_pole_dp_dm(map_axis, plon, plat, slon, slat, dp, dm, pole_label='pole', site_label='site',
                    pole_color='k', pole_edgecolor='k', pole_marker='o',
                    site_color='r', site_edgecolor='r', site_marker='s',
                    markersize=20, legend=True):
    """
    This function plots a paleomagnetic pole and a dp/dm confidence ellipse on a cartopy map axis.

    Before this function is called, a plot needs to be initialized with code
    such as that in the make_orthographic_map function.

    Example
    -------
    >>> dec = 280
    >>> inc = 45
    >>> a95 = 5
    >>> site_lat = 45
    >>> site_lon = -100
    >>> pole = pmag.dia_vgp(dec, inc, a95, site_lat, site_lon)
    >>> pole_lon = pole[0]
    >>> pole_lat = pole[1]
    >>> dp = pole[2]
    >>> dm = pole[3]

    >>> map_axis = ipmag.make_orthographic_map(central_longitude=200,central_latitude=30)
    >>> ipmag.plot_pole_dp_dm(map_axis,pole_lon,pole_lat,site_lon,site_lat,dp,dm)

    Required Parameters
    -----------
    map_axis : the name of the current map axis that has been developed using cartopy
    plon : the longitude of the paleomagnetic pole being plotted (in degrees E)
    plat : the latitude of the paleomagnetic pole being plotted (in degrees)
    slon : the longitude of the site (in degrees E)
    slat : the latitude of the site (in degrees)
    dp : the semi-minor axis of the confidence ellipse (in degrees)
    dm : the semi-major axis of the confidence ellipse (in degrees)

    Optional Parameters (defaults are used if not specified)
    -----------
    pole_color : the default color is black. Other colors can be chosen (e.g. 'g')
    site_color : the default color is red. Other colors can be chosen (e.g. 'g')
    pole_marker : the default is a circle. Other symbols can be chosen (e.g. 's')
    site_marker : the default is a square. Other symbols can be chosen (e.g. '^')
    markersize : the default is 20. Other size can be chosen
    pole_label : string that labels the pole.
    site_label : string that labels the site
    legend : the default is a legend (True). Putting False will suppress legend plotting.
    """
    if not has_cartopy:
        print('-W- cartopy must be installed to run ipmag.plot_pole_dp_dm')
        return
    dp_km = dp*111.32
    dm_km = dm*111.32
    map_axis.scatter(plon, plat, marker=pole_marker,
                     color=pole_color, edgecolors=pole_edgecolor, s=markersize,
                     label=pole_label, zorder=101, transform=ccrs.Geodetic())
    map_axis.scatter(slon, slat, marker=site_marker,
                     color=site_color, edgecolors=site_edgecolor, s=markersize,
                     label=site_label, zorder=101, transform=ccrs.Geodetic())
    # the orientation of the ellipse needs to be determined using the
    # two laws of cosines for spherical triangles where the triangle is
    # A: site, B: north pole, C: paleomagnetic pole (see Fig. A.2 of Butler)

    site_lon_rad = np.deg2rad(slon)
    site_lat_rad = np.deg2rad(slat)
    c_rad = np.deg2rad(90-slat)

    pole_lon_rad = np.deg2rad(plon)
    pole_lat_rad = np.deg2rad(plat)
    a_rad = np.deg2rad(90-plat)

    B_rad = np.abs(pole_lon_rad-site_lon_rad)

    cos_b = np.cos(c_rad)*np.cos(a_rad) + np.sin(c_rad) * \
        np.sin(a_rad)*np.cos(B_rad)
    b_rad = np.arccos(cos_b)

    sin_C = (np.sin(B_rad)/np.sin(b_rad))*np.sin(c_rad)
    C_rad = np.arcsin(sin_C)

    # need to make the rotation of the ellipse go the right way
    if slon-plon > 180:
        if plon >= slon and plat >= slat:
            C_deg = -np.abs(np.rad2deg(C_rad))
        elif plon <= slon and plat >= slat:
            C_deg = np.abs(np.rad2deg(C_rad))
        elif plon >= slon and plat <= slat:
            C_deg = np.abs(np.rad2deg(C_rad))
        elif plon <= slon and plat <= slat:
            C_deg = -np.abs(np.rad2deg(C_rad))
    elif slon-plon <= 180:
        if plon >= slon and plat >= slat:
            C_deg = np.abs(np.rad2deg(C_rad))
        elif plon <= slon and plat >= slat:
            C_deg = -np.abs(np.rad2deg(C_rad))
        elif plon >= slon and plat <= slat:
            C_deg = -np.abs(np.rad2deg(C_rad))
        elif plon <= slon and plat <= slat:
            C_deg = np.abs(np.rad2deg(C_rad))

    print(C_deg)
    ellipse(map_axis, plon, plat, dp_km, dm_km, C_deg)

    if legend == True:
        plt.legend(loc=2)

Response: This function plots a paleomagnetic pole and a semi - major axis of the confidence ellipse on a cartopy map axis.


Instruction:
Can you generate the documentation for the following Python 3 function
def plot_vgp_basemap(mapname, vgp_lon=None, vgp_lat=None, di_block=None, label='', color='k', marker='o', markersize=20, legend='no'):
    if di_block != None:
        di_lists = unpack_di_block(di_block)
        if len(di_lists) == 3:
            vgp_lon, vgp_lat, intensity = di_lists
        if len(di_lists) == 2:
            vgp_lon, vgp_lat = di_lists
    centerlon, centerlat = mapname(vgp_lon, vgp_lat)
    mapname.scatter(centerlon, centerlat, marker=marker,
                    s=markersize, color=color, label=label, zorder=100)
    if legend == 'yes':
        plt.legend(loc=2)

Response: This function plots a paleomagnetic pole on the current map.


Instruction:
Can you generate the documentation for the following Python 3 function
def vgp_calc(dataframe, tilt_correction='yes', site_lon='site_lon', site_lat='site_lat', dec_is='dec_is', inc_is='inc_is', dec_tc='dec_tc', inc_tc='inc_tc'):
    dataframe.is_copy = False
    if tilt_correction == 'yes':
        # calculate the paleolatitude/colatitude
        dataframe['paleolatitude'] = np.degrees(
            np.arctan(0.5 * np.tan(np.radians(dataframe[inc_tc]))))
        dataframe['colatitude'] = 90 - dataframe['paleolatitude']
        # calculate the latitude of the pole
        dataframe['vgp_lat'] = np.degrees(np.arcsin(np.sin(np.radians(dataframe[site_lat])) *
                                                    np.cos(np.radians(dataframe['colatitude'])) +
                                                    np.cos(np.radians(dataframe[site_lat])) *
                                                    np.sin(np.radians(dataframe['colatitude'])) *
                                                    np.cos(np.radians(dataframe[dec_tc]))))
        # calculate the longitudinal difference between the pole and the site
        # (beta)
        dataframe['beta'] = np.degrees(np.arcsin(old_div((np.sin(np.radians(dataframe['colatitude'])) *
                                                          np.sin(np.radians(dataframe[dec_tc]))),
                                                         (np.cos(np.radians(dataframe['vgp_lat']))))))
        # generate a boolean array (mask) to use to distinguish between the two possibilities for pole longitude
        # and then calculate pole longitude using the site location and
        # calculated beta
        mask = np.cos(np.radians(dataframe['colatitude'])) > np.sin(
            np.radians(dataframe[site_lat])) * np.sin(np.radians(dataframe['vgp_lat']))
        dataframe['vgp_lon'] = np.where(mask, (dataframe[site_lon] + dataframe['beta']) %
                                        360., (dataframe[site_lon] + 180 - dataframe['beta']) % 360.)
        # calculate the antipode of the poles
        dataframe['vgp_lat_rev'] = -dataframe['vgp_lat']
        dataframe['vgp_lon_rev'] = (dataframe['vgp_lon'] - 180.) % 360.
        # the 'colatitude' and 'beta' columns were created for the purposes of the pole calculations
        # but aren't of further use and are deleted
        del dataframe['colatitude']
        del dataframe['beta']
    if tilt_correction == 'no':
        # calculate the paleolatitude/colatitude
        dataframe['paleolatitude'] = np.degrees(
            np.arctan(0.5 * np.tan(np.radians(dataframe[inc_is]))))
        dataframe['colatitude'] = 90 - dataframe['paleolatitude']
        # calculate the latitude of the pole
        dataframe['vgp_lat'] = np.degrees(np.arcsin(np.sin(np.radians(dataframe[site_lat])) *
                                                    np.cos(np.radians(dataframe['colatitude'])) +
                                                    np.cos(np.radians(dataframe[site_lat])) *
                                                    np.sin(np.radians(dataframe['colatitude'])) *
                                                    np.cos(np.radians(dataframe[dec_is]))))
        # calculate the longitudinal difference between the pole and the site
        # (beta)
        dataframe['beta'] = np.degrees(np.arcsin(old_div((np.sin(np.radians(dataframe['colatitude'])) *
                                                          np.sin(np.radians(dataframe[dec_is]))),
                                                         (np.cos(np.radians(dataframe['vgp_lat']))))))
        # generate a boolean array (mask) to use to distinguish between the two possibilities for pole longitude
        # and then calculate pole longitude using the site location and
        # calculated beta
        mask = np.cos(np.radians(dataframe['colatitude'])) > np.sin(
            np.radians(dataframe[site_lat])) * np.sin(np.radians(dataframe['vgp_lat']))
        dataframe['vgp_lon'] = np.where(mask, (dataframe[site_lon] + dataframe['beta']) %
                                        360., (dataframe[site_lon] + 180 - dataframe['beta']) % 360.)
        # calculate the antipode of the poles
        dataframe['vgp_lat_rev'] = -dataframe['vgp_lat']
        dataframe['vgp_lon_rev'] = (dataframe['vgp_lon'] - 180.) % 360.
        # the 'colatitude' and 'beta' columns were created for the purposes of the pole calculations
        # but aren't of further use and are deleted
        del dataframe['colatitude']
        del dataframe['beta']
    return(dataframe)

Response: Calculates paleomagnetic poles using directional data and site location data within a pandas. DataFrame.


Instruction:
Explain what the following Python 3 code does
def make_diddd_array(dec, inc, dip_direction, dip):
    diddd_block = []
    for n in range(0, len(dec)):
        diddd_block.append([dec[n], inc[n], dip_direction[n], dip[n]])
    diddd_array = np.array(diddd_block)
    return diddd_array

Response: This function creates a numpy array of dec inc dd dip.


Instruction:
Can you tell what is the following Python 3 function doing
def ellipse(map_axis, centerlon, centerlat, major_axis, minor_axis, angle, n=360, filled=False, **kwargs):
    if not has_cartopy:
        print('-W- cartopy must be installed to run ipmag.ellipse')
        return False
    angle = angle*(np.pi/180)
    glon1 = centerlon
    glat1 = centerlat
    X = []
    Y = []
    for azimuth in np.linspace(0, 360, n):
        az_rad = azimuth*(np.pi/180)
        radius = ((major_axis*minor_axis)/(((minor_axis*np.cos(az_rad-angle))
                                            ** 2 + (major_axis*np.sin(az_rad-angle))**2)**.5))
        glon2, glat2, baz = shoot(glon1, glat1, azimuth, radius)
        X.append((360+glon2) % 360)
        Y.append(glat2)
    X.append(X[0])
    Y.append(Y[0])

    if filled:
        ellip = np.array((X, Y)).T
        ellip = map_axis.projection.transform_points(
            ccrs.PlateCarree(), ellip[:, 0], ellip[:, 1])
        poly = Polygon(ellip[:, :2], **kwargs)
        map_axis.add_patch(poly)
    else:
        try:
            map_axis.plot(X, Y, transform=ccrs.Geodetic(), **kwargs)
            return True
        except ValueError:
            return False

Response: This function creates a key word ellipses that can be drawn on the map object.


Instruction:
Can you implement a function in Python 3 that
combines a list of MagIC formatted files into one.

Response:
def combine_magic(filenames, outfile='measurements.txt', data_model=3, magic_table='measurements',
                  dir_path=".", input_dir_path=""):
    """
    Takes a list of magic-formatted files, concatenates them, and creates a
    single file. Returns output filename if the operation was successful.

    Parameters
    -----------
    filenames : list of MagIC formatted files
    outfile : name of output file [e.g., measurements.txt]
    data_model : data model number (2.5 or 3), default 3
    magic_table : name of magic table, default 'measurements'
    dir_path : str
        output directory, default "."
    input_dir_path : str
        input file directory (if different from dir_path), default ""

    Returns
    ----------
    outfile name if success, False if failure
    """
    input_dir_path, output_dir_path = pmag.fix_directories(input_dir_path, dir_path)
    if float(data_model) == 3.0:
        outfile = pmag.resolve_file_name(outfile, output_dir_path)
        output_dir_path, file_name = os.path.split(outfile)
        con = cb.Contribution(output_dir_path, read_tables=[])
        # make sure files actually exist
        filenames = [pmag.resolve_file_name(f, input_dir_path) for f in filenames]
        #filenames = [os.path.realpath(f) for f in filenames]
        filenames = [f for f in filenames if os.path.exists(f)]
        if not filenames:
            print("You have provided no valid file paths, so nothing will be combined".format(
                magic_table))
            return False
        # figure out file type from first of files to join
        with open(filenames[0]) as f:
            file_type = f.readline().split()[1]
        if file_type in ['er_specimens', 'er_samples', 'er_sites',
                         'er_locations', 'er_ages', 'pmag_specimens',
                         'pmag_samples', 'pmag_sites', 'pmag_results',
                         'magic_measurements', 'rmag_anisotropy',
                         'rmag_results', 'rmag_specimens']:
            print(
                '-W- You are working in MagIC 3 but have provided a MagIC 2.5 file: {}'.format(file_type))
            return False
        if file_type not in con.table_names:
            file_type = magic_table
        infiles = [pd.read_csv(infile, sep='\t', header=1)
                   for infile in filenames]
        df = pd.concat(infiles, ignore_index=True, sort=True)
        # drop any fully duplicated rows
        df.drop_duplicates(inplace=True)
        con.add_magic_table(dtype=file_type, df=df)
        # drop any mostly empty rows IF they have duplicate index
        parent, child = con.get_parent_and_child(file_type)
        ignore_cols = [col[:-1] for col in [file_type, parent] if col]
        ignore_cols.extend(['software_packages', 'citations'])
        con.tables[file_type].drop_duplicate_rows(ignore_cols)
        # correctly handle measurements.sequence column
        if 'sequence' in con.tables[file_type].df:
            con.tables[file_type].df['sequence'] = range(1, len(con.tables[file_type].df) + 1)
        # write table to file, use custom name
        res = con.write_table_to_file(file_type, custom_name=file_name)
        return res
    else:
        datasets = []
        if not filenames:
            print("You must provide at least one file")
            return False
        for infile in filenames:
            if not os.path.isfile(infile):
                print("{} is not a valid file name".format(infile))
                return False
            try:
                dataset, file_type = pmag.magic_read(infile)
            except IndexError:
                print('-W- Could not get records from {}'.format(infile))
                print('    Skipping...')
                continue
            print("File ", infile, " read in with ", len(dataset), " records")
            for rec in dataset:
                datasets.append(rec)

        Recs, keys = pmag.fillkeys(datasets)
        if Recs:
            pmag.magic_write(outfile, Recs, file_type)
            print("All records stored in ", outfile)
            return outfile
        print("No file could be created")
        return False


Instruction:
How would you code a function in Python 3 to
return matplotlib figure with anisotropy data plotted against depth available depth scales: 'sample_composite_depth', 'sample_core_depth', or 'age' (you must provide an age file to use this option)

Response:
def ani_depthplot2(ani_file='rmag_anisotropy.txt', meas_file='magic_measurements.txt', samp_file='er_samples.txt', age_file=None, sum_file=None, fmt='svg', dmin=-1, dmax=-1, depth_scale='sample_core_depth', dir_path='.'):
    """
    returns matplotlib figure with anisotropy data plotted against depth
    available depth scales: 'sample_composite_depth', 'sample_core_depth', or 'age' (you must provide an age file to use this option)

    """
    pcol = 4
    tint = 9
    plots = 0

    # format files to use full path
    # os.path.join(dir_path, ani_file)
    ani_file = pmag.resolve_file_name(ani_file, dir_path)
    if not os.path.isfile(ani_file):
        print("Could not find rmag_anisotropy type file: {}.\nPlease provide a valid file path and try again".format(ani_file))
        return False, "Could not find rmag_anisotropy type file: {}.\nPlease provide a valid file path and try again".format(ani_file)

    # os.path.join(dir_path, meas_file)
    meas_file = pmag.resolve_file_name(meas_file, dir_path)

    if age_file:
        if not os.path.isfile(age_file):
            print(
                'Warning: you have provided an invalid age file.  Attempting to use sample file instead')
            age_file = None
            depth_scale = 'sample_core_depth'
            # os.path.join(dir_path, samp_file)
            samp_file = pmag.resolve_file_name(samp_file, dir_path)
        else:
            # os.path.join(dir_path, age_file)
            samp_file = pmag.resolve_file_name(samp_file, dir_path)
            depth_scale = 'age'
            print(
                'Warning: you have provided an er_ages format file, which will take precedence over er_samples')
    else:
        samp_file = pmag.resolve_file_name(samp_file, dir_path)

    label = 1

    if sum_file:
        sum_file = os.path.join(dir_path, sum_file)

    dmin, dmax = float(dmin), float(dmax)

    # get data read in
    isbulk = 0  # tests if there are bulk susceptibility measurements
    AniData, file_type = pmag.magic_read(ani_file)  # read in tensor elements
    if not age_file:
        # read in sample depth info from er_sample.txt format file
        Samps, file_type = pmag.magic_read(samp_file)
    else:
        # read in sample age info from er_ages.txt format file
        Samps, file_type = pmag.magic_read(samp_file)
        age_unit = Samps[0]['age_unit']
    for s in Samps:
        # change to upper case for every sample name
        s['er_sample_name'] = s['er_sample_name'].upper()
    Meas, file_type = pmag.magic_read(meas_file)
    # print 'meas_file', meas_file
    # print 'file_type', file_type
    if file_type == 'magic_measurements':
        isbulk = 1
    Data = []
    Bulks = []
    BulkDepths = []
    for rec in AniData:
        # look for depth record for this sample
        samprecs = pmag.get_dictitem(Samps, 'er_sample_name',
                                     rec['er_sample_name'].upper(), 'T')
        # see if there are non-blank depth data
        sampdepths = pmag.get_dictitem(samprecs, depth_scale, '', 'F')
        if dmax != -1:
            # fishes out records within depth bounds
            sampdepths = pmag.get_dictitem(
                sampdepths, depth_scale, dmax, 'max')
            sampdepths = pmag.get_dictitem(
                sampdepths, depth_scale, dmin, 'min')
        if len(sampdepths) > 0:  # if there are any....
            # set the core depth of this record
            rec['core_depth'] = sampdepths[0][depth_scale]
            Data.append(rec)  # fish out data with core_depth
            if isbulk:  # if there are bulk data
                chis = pmag.get_dictitem(
                    Meas, 'er_specimen_name', rec['er_specimen_name'], 'T')
                # get the non-zero values for this specimen
                chis = pmag.get_dictitem(
                    chis, 'measurement_chi_volume', '', 'F')
                if len(chis) > 0:  # if there are any....
                    # put in microSI
                    Bulks.append(
                        1e6 * float(chis[0]['measurement_chi_volume']))
                    BulkDepths.append(float(sampdepths[0][depth_scale]))
    if len(Bulks) > 0:  # set min and max bulk values
        bmin = min(Bulks)
        bmax = max(Bulks)
    xlab = "Depth (m)"
    if len(Data) > 0:
        location = Data[0]['er_location_name']
    else:
        return False, 'no data to plot'
    # collect the data for plotting tau  V3_inc and V1_dec
    Depths, Tau1, Tau2, Tau3, V3Incs, P, V1Decs = [], [], [], [], [], [], []
    F23s = []
    Axs = []  # collect the plot ids
# START HERE
    if len(Bulks) > 0:
        pcol += 1
    # get all the s1 values from Data as floats
    s1 = pmag.get_dictkey(Data, 'anisotropy_s1', 'f')
    s2 = pmag.get_dictkey(Data, 'anisotropy_s2', 'f')
    s3 = pmag.get_dictkey(Data, 'anisotropy_s3', 'f')
    s4 = pmag.get_dictkey(Data, 'anisotropy_s4', 'f')
    s5 = pmag.get_dictkey(Data, 'anisotropy_s5', 'f')
    s6 = pmag.get_dictkey(Data, 'anisotropy_s6', 'f')
    nmeas = pmag.get_dictkey(Data, 'anisotropy_n', 'int')
    sigma = pmag.get_dictkey(Data, 'anisotropy_sigma', 'f')
    Depths = pmag.get_dictkey(Data, 'core_depth', 'f')
    # Ss=np.array([s1,s4,s5,s4,s2,s6,s5,s6,s3]).transpose() # make an array
    Ss = np.array([s1, s2, s3, s4, s5, s6]).transpose()  # make an array
    # Ts=np.reshape(Ss,(len(Ss),3,-1)) # and re-shape to be n-length array of
    # 3x3 sub-arrays
    for k in range(len(Depths)):
        # tau,Evecs= pmag.tauV(Ts[k]) # get the sorted eigenvalues and eigenvectors
        # v3=pmag.cart2dir(Evecs[2])[1] # convert to inclination of the minimum
        # eigenvector
        fpars = pmag.dohext(nmeas[k] - 6, sigma[k], Ss[k])
        V3Incs.append(fpars['v3_inc'])
        V1Decs.append(fpars['v1_dec'])
        Tau1.append(fpars['t1'])
        Tau2.append(fpars['t2'])
        Tau3.append(fpars['t3'])
        P.append(old_div(Tau1[-1], Tau3[-1]))
        F23s.append(fpars['F23'])
    if len(Depths) > 0:
        if dmax == -1:
            dmax = max(Depths)
            dmin = min(Depths)
        tau_min = 1
        for t in Tau3:
            if t > 0 and t < tau_min:
                tau_min = t
        tau_max = max(Tau1)
        # tau_min=min(Tau3)
        P_max = max(P)
        P_min = min(P)
        # dmax=dmax+.05*dmax
        # dmin=dmin-.05*dmax

        main_plot = plt.figure(1, figsize=(10, 8))  # make the figure

        version_num = pmag.get_version()
        plt.figtext(.02, .01, version_num)  # attach the pmagpy version number
        ax = plt.subplot(1, pcol, 1)  # make the first column
        Axs.append(ax)
        ax.plot(Tau1, Depths, 'rs')
        ax.plot(Tau2, Depths, 'b^')
        ax.plot(Tau3, Depths, 'ko')
        if sum_file:
            core_depth_key, core_label_key, Cores = read_core_csv_file(
                sum_file)
            for core in Cores:
                depth = float(core[core_depth_key])
                if depth > dmin and depth < dmax:
                    plt.plot([0, 90], [depth, depth], 'b--')
        ax.axis([tau_min, tau_max, dmax, dmin])
        ax.set_xlabel('Eigenvalues')
        if depth_scale == 'sample_core_depth':
            ax.set_ylabel('Depth (mbsf)')
        elif depth_scale == 'age':
            ax.set_ylabel('Age (' + age_unit + ')')
        else:
            ax.set_ylabel('Depth (mcd)')
        ax2 = plt.subplot(1, pcol, 2)  # make the second column
        ax2.plot(P, Depths, 'rs')
        ax2.axis([P_min, P_max, dmax, dmin])
        ax2.set_xlabel('P')
        ax2.set_title(location)
        if sum_file:
            for core in Cores:
                depth = float(core[core_depth_key])
                if depth > dmin and depth < dmax:
                    plt.plot([0, 90], [depth, depth], 'b--')
        Axs.append(ax2)
        ax3 = plt.subplot(1, pcol, 3)
        Axs.append(ax3)
        ax3.plot(V3Incs, Depths, 'ko')
        ax3.axis([0, 90, dmax, dmin])
        ax3.set_xlabel('V3 Inclination')
        if sum_file:
            for core in Cores:
                depth = float(core[core_depth_key])
                if depth > dmin and depth < dmax:
                    plt.plot([0, 90], [depth, depth], 'b--')
        ax4 = plt.subplot(1, np.abs(pcol), 4)
        Axs.append(ax4)
        ax4.plot(V1Decs, Depths, 'rs')
        ax4.axis([0, 360, dmax, dmin])
        ax4.set_xlabel('V1 Declination')
        if sum_file:
            for core in Cores:
                depth = float(core[core_depth_key])
                if depth >= dmin and depth <= dmax:
                    plt.plot([0, 360], [depth, depth], 'b--')
                    if pcol == 4 and label == 1:
                        plt.text(360, depth + tint, core[core_label_key])
        # ax5=plt.subplot(1,np.abs(pcol),5)
        # Axs.append(ax5)
        # ax5.plot(F23s,Depths,'rs')
        # bounds=ax5.axis()
        # ax5.axis([bounds[0],bounds[1],dmax,dmin])
        # ax5.set_xlabel('F_23')
        # ax5.semilogx()
        # if sum_file:
        #    for core in Cores:
        #         depth=float(core[core_depth_key])
        #         if depth>=dmin and depth<=dmax:
        #            plt.plot([bounds[0],bounds[1]],[depth,depth],'b--')
        #            if pcol==5 and label==1:plt.text(bounds[1],depth+tint,core[core_label_key])
        # if pcol==6:
        if pcol == 5:
            # ax6=plt.subplot(1,pcol,6)
            ax6 = plt.subplot(1, pcol, 5)
            Axs.append(ax6)
            ax6.plot(Bulks, BulkDepths, 'bo')
            ax6.axis([bmin - 1, 1.1 * bmax, dmax, dmin])
            ax6.set_xlabel('Bulk Susc. (uSI)')
            if sum_file:
                for core in Cores:
                    depth = float(core[core_depth_key])
                    if depth >= dmin and depth <= dmax:
                        plt.plot([0, bmax], [depth, depth], 'b--')
                        if label == 1:
                            plt.text(1.1 * bmax, depth + tint,
                                     core[core_label_key])
        for x in Axs:
            # this makes the x-tick labels more reasonable - they were
            # overcrowded using the defaults
            pmagplotlib.delticks(x)
        fig_name = location + '_ani_depthplot.' + fmt
        return main_plot, fig_name
    else:
        return False, "No data to plot"


Instruction:
Can you tell what is the following Python 3 function doing
def ani_depthplot(spec_file='specimens.txt', samp_file='samples.txt',
                  meas_file='measurements.txt', site_file='sites.txt',
                  age_file="", sum_file="", fmt='svg', dmin=-1, dmax=-1,
                  depth_scale='core_depth', dir_path='.', contribution=None):
    """
    returns matplotlib figure with anisotropy data plotted against depth
    available depth scales: 'composite_depth', 'core_depth' or 'age' (you must provide an age file to use this option).
    You must provide valid specimens and sites files, and either a samples or an ages file.
    You may additionally provide measurements and a summary file (csv).

    Parameters
    ----------
    spec_file : str, default "specimens.txt"
    samp_file : str, default "samples.txt"
    meas_file : str, default "measurements.txt"
    site_file : str, default "sites.txt"
    age_file : str, default ""
    sum_file : str, default ""
    fmt : str, default "svg"
        format for figures, ["svg", "jpg", "pdf", "png"]
    dmin : number, default -1
        minimum depth to plot (if -1, default to plotting all)
    dmax : number, default -1
        maximum depth to plot (if -1, default to plotting all)
    depth_scale : str, default "core_depth"
        scale to plot, ['composite_depth', 'core_depth', 'age'].
        if 'age' is selected, you must provide an ages file.
    dir_path : str, default "."
        directory for input files
    contribution : cb.Contribution, default None
        if provided, use Contribution object instead of reading in
        data from files

    Returns
    ---------
    plot : matplotlib plot, or False if no plot could be created
    name : figure name, or error message if no plot could be created
    """
    if depth_scale == 'sample_core_depth':
        depth_scale = 'core_depth'
    if depth_scale == 'sample_composite_depth':
        depth_scale = 'composite_depth'

    pcol = 4
    tint = 9
    plots = 0

    dmin, dmax = float(dmin), float(dmax)

    # if contribution object is not provided, read in data from files
    if isinstance(contribution, cb.Contribution):
        con = contribution
    else:
        # format files to use full path
        meas_file = pmag.resolve_file_name(meas_file, dir_path)
        spec_file = pmag.resolve_file_name(spec_file, dir_path)
        samp_file = pmag.resolve_file_name(samp_file, dir_path)
        site_file = pmag.resolve_file_name(site_file, dir_path)

        if age_file:
            age_file = pmag.resolve_file_name(age_file, dir_path)
            if not os.path.isfile(age_file):
                print(
                    'Warning: you have provided an invalid age file.  Attempting to use sample file instead')
                age_file = None
                depth_scale = 'core_depth'
            else:
                samp_file = age_file
                depth_scale = 'age'
                print(
                    'Warning: you have provided an ages format file, which will take precedence over samples')

        samp_file = pmag.resolve_file_name(samp_file, dir_path)

        label = 1

        if sum_file:
            sum_file = pmag.resolve_file_name(sum_file, dir_path)
            core_df=pd.read_csv(sum_file)
            depths=core_df['Top depth cored CSF (m)'].values

        # contribution

        dir_path = os.path.split(spec_file)[0]
        tables = ['measurements', 'specimens', 'samples', 'sites']
        con = cb.Contribution(dir_path, read_tables=tables,
                              custom_filenames={'measurements': meas_file, 'specimens': spec_file,
                                                'samples': samp_file, 'sites': site_file})

    for ftype in ['specimens', 'samples', 'sites']:
        if not con.tables.get(ftype):
            if ftype == 'samples':
                if con.tables.get('ages'):
                    depth_scale = 'age'
                    continue
            print("-W- This function requires a {} file to run.".format(ftype))
            print("    Make sure you include one in your working directory")
            return False, "missing required file type: {}".format(ftype)

    # propagate needed values
    con.propagate_cols(['core_depth'], 'samples', 'sites')
    con.propagate_location_to_specimens()

    # get data read in
    isbulk = 0  # tests if there are bulk susceptibility measurements
    ani_file = spec_file

    SampData = con.tables['samples'].df
    AniData = con.tables['specimens'].df
    # add sample into specimens (AniData)
    AniData = pd.merge(
        AniData, SampData[['sample', depth_scale]], how='inner', on='sample')
    # trim down AniData
    cond = AniData[depth_scale].astype(bool)
    AniData = AniData[cond]
    if dmin != -1:
        AniData = AniData[AniData[depth_scale] < dmax]
    if dmax != -1:
        AniData = AniData[AniData[depth_scale] > dmin]
    AniData['core_depth'] = AniData[depth_scale]

    if not age_file:
        Samps = con.tables['samples'].convert_to_pmag_data_list()
    else:
        con.add_magic_table(dtype='ages', fname=age_file)
        Samps = con.tables['ages'].convert_to_pmag_data_list()
        # get age unit
        age_unit = con.tables['ages'].df['age_unit'][0]
        # propagate ages down to sample level

    for s in Samps:
        # change to upper case for every sample name
        s['sample'] = s['sample'].upper()

    if 'measurements' in con.tables:
        isbulk = 1
        Meas = con.tables['measurements'].df  # convert_to_pmag_data_list()

    if isbulk:
        Meas = Meas[Meas['specimen'].astype('bool')]
        Meas = Meas[Meas['susc_chi_volume'].astype(bool)]
        # add core_depth into Measurements dataframe
        Meas = pd.merge(Meas[['susc_chi_volume', 'specimen']], AniData[[
                        'specimen', 'core_depth']], how='inner', on='specimen')
        Bulks = list(Meas['susc_chi_volume'] * 1e6)
        BulkDepths = list(Meas['core_depth'])
    else:
        Bulks, BulkDepths = [], []

    # now turn Data from pandas dataframe to a list of dicts
    Data = list(AniData.T.apply(dict))

    if len(Bulks) > 0:  # set min and max bulk values
        bmin = min(Bulks)
        bmax = max(Bulks)
    xlab = "Depth (m)"

    #
    if len(Data) > 0:
        location = Data[0].get('location', 'unknown')
        if cb.is_null(location):
            location = 'unknown'
            try:
                location = con.tables['sites'].df['location'][0]
            except KeyError:
                pass
    else:
        return False, 'no data to plot'

    # collect the data for plotting tau  V3_inc and V1_dec
    Depths, Tau1, Tau2, Tau3, V3Incs, P, V1Decs = [], [], [], [], [], [], []
    F23s = []
    Axs = []  # collect the plot ids
    if len(Bulks) > 0:
        pcol += 1

    Data = pmag.get_dictitem(Data, 'aniso_s', '', 'not_null')
    # get all the s1 values from Data as floats
    aniso_s = pmag.get_dictkey(Data, 'aniso_s', '')
    aniso_s = [a.split(':') for a in aniso_s if a is not None]
    #print('aniso_s', aniso_s)
    s1 = [float(a[0]) for a in aniso_s]
    s2 = [float(a[1]) for a in aniso_s]
    s3 = [float(a[2]) for a in aniso_s]
    s4 = [float(a[3]) for a in aniso_s]
    s5 = [float(a[4]) for a in aniso_s]
    s6 = [float(a[5]) for a in aniso_s]
    # we are good with s1 - s2
    nmeas = pmag.get_dictkey(Data, 'aniso_s_n_measurements', 'int')
    sigma = pmag.get_dictkey(Data, 'aniso_s_sigma', 'f')
    Depths = pmag.get_dictkey(Data, 'core_depth', 'f')
    # Ss=np.array([s1,s4,s5,s4,s2,s6,s5,s6,s3]).transpose() # make an array
    Ss = np.array([s1, s2, s3, s4, s5, s6]).transpose()  # make an array
    # Ts=np.reshape(Ss,(len(Ss),3,-1)) # and re-shape to be n-length array of
    # 3x3 sub-arrays
    for k in range(len(Depths)):
        # tau,Evecs= pmag.tauV(Ts[k]) # get the sorted eigenvalues and eigenvectors
        # v3=pmag.cart2dir(Evecs[2])[1] # convert to inclination of the minimum
        # eigenvector
        fpars = pmag.dohext(nmeas[k] - 6, sigma[k], Ss[k])
        V3Incs.append(fpars['v3_inc'])
        V1Decs.append(fpars['v1_dec'])
        Tau1.append(fpars['t1'])
        Tau2.append(fpars['t2'])
        Tau3.append(fpars['t3'])
        P.append(old_div(Tau1[-1], Tau3[-1]))
        F23s.append(fpars['F23'])
    if len(Depths) > 0:
        if dmax == -1:
            dmax = max(Depths)
            dmin = min(Depths)
        tau_min = 1
        for t in Tau3:
            if t > 0 and t < tau_min:
                tau_min = t
        tau_max = max(Tau1)
        # tau_min=min(Tau3)
        P_max = max(P)
        P_min = min(P)
        # dmax=dmax+.05*dmax
        # dmin=dmin-.05*dmax

        main_plot = plt.figure(1, figsize=(11, 7))  # make the figure
        # main_plot = plt.figure(1, figsize=(10, 8))  # make the figure

        version_num = pmag.get_version()
        plt.figtext(.02, .01, version_num)  # attach the pmagpy version number
        ax = plt.subplot(1, pcol, 1)  # make the first column
        Axs.append(ax)
        ax.plot(Tau1, Depths, 'rs')
        ax.plot(Tau2, Depths, 'b^')
        ax.plot(Tau3, Depths, 'ko')
        if sum_file:
            for depth in depths:
                if depth >= dmin and depth < dmax:
                    plt.axhline(depth,color='blue',linestyle='dotted')
        if tau_min>.3: tau_min=.3
        if tau_max<.36: tau_max=.36
        ax.axis([tau_min, tau_max, dmax, dmin])
        ax.set_xlabel('Eigenvalues')
        if depth_scale == 'core_depth':
            ax.set_ylabel('Depth (mbsf)')
        elif depth_scale == 'age':
            ax.set_ylabel('Age (' + age_unit + ')')
        else:
            ax.set_ylabel('Depth (mcd)')
        ax2 = plt.subplot(1, pcol, 2)  # make the second column
        ax2.yaxis.set_major_locator(plt.NullLocator())
        ax2.plot(P, Depths, 'rs')
        ax2.axis([P_min, P_max, dmax, dmin])
        ax2.set_xlabel('P')
        ax2.set_title(location)
        if sum_file:
            for depth in depths:
                if depth >= dmin and depth < dmax:
                    plt.axhline(depth,color='blue',linestyle='dotted')
        Axs.append(ax2)
        ax3 = plt.subplot(1, pcol, 3)
        Axs.append(ax3)
        ax3.plot(V3Incs, Depths, 'ko')
        ax3.axis([0, 90, dmax, dmin])
        ax3.set_xlabel('V3 Inclination')
        ax3.yaxis.set_major_locator(plt.NullLocator())
        if sum_file:
            for depth in depths:
                if depth >= dmin and depth < dmax:
                    plt.axhline(depth,color='blue',linestyle='dotted')
        ax4 = plt.subplot(1, np.abs(pcol), 4)
        Axs.append(ax4)
        ax4.plot(V1Decs, Depths, 'rs')
        ax4.axis([0, 360, dmax, dmin])
        ax4.set_xlabel('V1 Declination')
        ax4.yaxis.set_major_locator(plt.NullLocator())
        if sum_file:
            for depth in depths:
                if depth >= dmin and depth < dmax:
                    plt.axhline(depth,color='blue',linestyle='dotted')
        # ax5=plt.subplot(1,np.abs(pcol),5)
        # Axs.append(ax5)
        # ax5.plot(F23s,Depths,'rs')
        # bounds=ax5.axis()
        # ax5.axis([bounds[0],bounds[1],dmax,dmin])
        # ax5.set_xlabel('F_23')
        # ax5.semilogx()
        # if sum_file:
        #    for core in Cores:
        #         depth=float(core[core_depth_key])
        #         if depth>=dmin and depth<=dmax:
        #            plt.plot([bounds[0],bounds[1]],[depth,depth],'b--')
        #            if pcol==5 and label==1:plt.text(bounds[1],depth+tint,core[core_label_key])
        # if pcol==6:
        if pcol == 5:
            # ax6=plt.subplot(1,pcol,6)
            ax6 = plt.subplot(1, pcol, 5)
            Axs.append(ax6)
            ax6.plot(Bulks, BulkDepths, 'bo')
            ax6.axis([bmin - 1, 1.1 * bmax, dmax, dmin])
            ax6.set_xlabel('Bulk Susc. (uSI)')
            ax6.yaxis.set_major_locator(plt.NullLocator())
            if sum_file:
                for depth in depths:
                    if depth >= dmin and depth < dmax:
                        plt.axhline(depth,color='blue',linestyle='dotted')
        for x in Axs:
            # this makes the x-tick labels more reasonable - they were
            # overcrowded using the defaults
            pmagplotlib.delticks(x)
        fig_name = location + '_ani_depthplot.' + fmt
        return main_plot, [fig_name]
    else:
        return False, "No data to plot"

Response: Function to plot an anisotropy data against a single site or specimen.