<jupyter_start><jupyter_text>Practice functionsReview [`Intro_to_functions`](Intro_to_functions.ipynb) before coming in here.Our goal for this notebook is to get some practice writing functions. You can place these functions in `utils.py` once you've confirmed that you've got them working properly in the notebook.In doing so, we will implement a function to compute reflection coefficients from sequences of Vp and density values. ExerciseWe want a function to compute acoustic velocity from slowness (aka travel time, or the reciprocal of velocity). For example, we might compute Vp from the DT log.Rearrange the following lines to create this function:<jupyter_code>return vel
vel = 1e6 / slow
def vel_from_slow(slow):
"""Velocity from slowness, where slowness is in microseconds per unit distance."""
# YOUR CODE HERE<jupyter_output><empty_output><jupyter_text>Use this to call the function:<jupyter_code>vel_from_slow(500)<jupyter_output><empty_output><jupyter_text>You should get     2000.0 Optional exercisesStretch goal: can you write the function so it accepts lists instead? Try it with `[450, 475, 550, 425]`. (Don't worry if you don't have time for this, we'll get to it later.)Stretch goal: what happens if you pass a NumPy array to your function? Generate an array with:    import numpy as np    arr = np.array([450, 475, 550, 425]) ExerciseWe would like to write a function that takes malformed depth strings from LAS files and returns something we can cast to a float. For example, if we get strings `'2370 M'` or `'12,300ft'` as input then we would like our function to return `'2370'` or `'12300'`.Rearrange the following lines to perform this task:<jupyter_code># Replace any numeral separating commas.
return value
stripped = string.lower().strip(' \n\t.mft')
def clean_depth(string):
# Remove units and other stuff.
"""Clean the units from a number."""
value = stripped.replace(',', '')
#YOUR CODE HERE



clean_depth('2370 M'), clean_depth('12,300 ft')<jupyter_output><empty_output><jupyter_text>Multiple returnsFunctions can only return one thing, but we can pack multiple things into a tuple:<jupyter_code># The builtin function pow(a, b) raises a to the power
# of b. Imagine we *also* want b to the power of a...

def powpow(a, b):
    return a**b, b**a

powpow(2, 3)<jupyter_output><empty_output><jupyter_text>Assign variables to these two outputs by simultaneous assignment:<jupyter_code>a_b, b_a = powpow(2, 3)
a_b<jupyter_output><empty_output><jupyter_text>ExerciseModify the function in the previous exercise to return floats, not strings.Use the code from the `if` section of `Intro_to_Python` to also return the units as a string. If there's nothing we recognize, we'll return `None` for the units.After both of these modifications your function should work like this: if we get `'2370 M'` or `'12,300ft'` as input then we would like our function to return `(2370.0, 'm')` or `(12300.0, 'ft')`. Finally, an input of `'3322'` should return `(3322.0, None)`.<jupyter_code>float('234')
# YOUR CODE HERE<jupyter_output><empty_output><jupyter_text>When you get it working, add this function to the `utils.py` module. ExerciseThis exercise is about Gardner's equation:$$ \rho = 310\ V_\mathrm{P}^{\,0.25}\ \ \mathrm{kg}/\mathrm{m}^3 $$- Implement Gardner's equation as a Python function.- Make the factor $\alpha$ of 310 and the exponent $\beta$ of 0.25 into arguments the user can change if they want to. I.e. give them default values.- Add this function to your `utils.py` file in the `/notebooks` folder.- How can you make this function accept a list of velocities?<jupyter_code>def gardner(vp):
    
    # Your code here.<jupyter_output><empty_output><jupyter_text>The following should yield a number close to `2224.1965`.<jupyter_code>gardner(2650)<jupyter_output><empty_output><jupyter_text>----  More complicated functionsLet's make some dummy data:<jupyter_code>rho = [2.45, 2.35, 2.45, 2.55, 2.80, 2.75]
vp = [2300, 2400, 2500, 2300, 2600, 2700]
%matplotlib inline
import matplotlib.pyplot as plt

plt.plot(vp)
from utils import impedance

impedance(rho, vp)<jupyter_output><empty_output><jupyter_text>It turns out there's a way to step over the values of `rho` and `vp` together. We will use the function `zip()`. [Read about zip() here](https://docs.python.org/3.3/library/functions.htmlzip). Try the code below to see what it does.<jupyter_code>a = [1, 2, 3]
b = [10, 11, 12]

for pair in zip(a, b):
    print(pair)<jupyter_output><empty_output><jupyter_text>Remember the trick we used before to assign two variables to a tuple?    x, y = (1.61, 3.14)    After doing this, `x` points to `1.61` and `y` to `3.14`. We can use the same trick in the `for` loop initialization, so that we have **two** integer variables inside the loop instead of one tuple:<jupyter_code>for an, bn in zip(a, b):
    print('an is', an)
    print('bn is', bn)
    print()<jupyter_output><empty_output><jupyter_text>ExerciseUpdate your `impedance` function to handle this data. It should contain a `for` loop to step over the values of `rho` and `vp`. Remember, `rho` and `vp` are lists.<jupyter_code>def impedance(rho, vp):

    # Your code here.
    
    return  # You must return something.
# Once you're done, this should work...
impedance(rho, vp)<jupyter_output><empty_output><jupyter_text>This should give you:    [5635.0, 5640.0, 6125.0, 5865.0, 7280.0, 7425.0] Now that we've got our function working for lists, let's try it when we pass in two scalars:<jupyter_code>impedance(2300, 2400)<jupyter_output><empty_output><jupyter_text>Handling errorsObviously, there are plenty of ways in which we might encounter errors in our programs. There are a number of ways that our programs can break. In fact, every time our code breaks, we get a specific error message that we can use as a hint as to why the program isn't working. This can be a `TypeError`, `SyntaxError`, `FileNotFoundError`, a `NameError`, and `IndexError`, and so on. A full list of [Python's built-in exceptions](https://docs.python.org/3/library/exceptions.htmlbltin-exceptions) can be found here. In situations where we want to want to handle errors like the `TypeError` above, instead of forcing the program to stop, we can use `try-except` statement.  ```    try:         to do this code    except TypeError:         do this code instead```        The principle at work is:> It's better to ask for forgiveness than permission. ExerciseUpdate your `impedance` function to handle both cases. The first case you need to deal with is when `vp` and `rho` are scalars (single values, `floats` or `ints`), the second case is when `vp` and `rho` are lists (whose elements are either `floats` or `ints`)<jupyter_code>impedance([2500, 2400], [2600, 2600])
impedance(2300, 2400)<jupyter_output><empty_output><jupyter_text>It works! ExerciseIf you have already met NumPy at this point, can you implement the same functionality — accepting scalars and vectors — but using NumPy arrays?  Docstrings and doctestsLet's add a docstring and doctests to our function.<jupyter_code>import doctest
doctest.testmod()<jupyter_output><empty_output><jupyter_text>ExerciseAdd docstrings and doctests to the Gardner function, then add it and your definition of `impedance` to your utils.py  Compute reflection coefficients ExerciseCan you implement the following equation?$$ \mathrm{rc} = \frac{Z_\mathrm{lower} - Z_\mathrm{upper}}{Z_\mathrm{lower} + Z_\mathrm{upper}} $$You will need to use slicing to implement the concept of upper and lower layers.<jupyter_code>def rc_series(z):

    # YOUR CODE HERE!
# When you're done, this should work...
z = impedance(rho, vp)
rc_series(z)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09940072954663888, 'fraction_numerical': 0.032569046378322045, 'mean_word_length': 5.049645390070922, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 66, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '487162', 'license': 'Apache-2.0', 'n_tokens_mistral': 2587, 'n_tokens_neox': 2344, 'n_words': 986, 'path': 'notebooks/Practice_functions.ipynb', 'repo_name': 'agile-geoscience/geocomp-public', 'size': '7676'}