<jupyter_start><jupyter_text>Python Day 0----________**Goals for Today**- Learn the basics of Python coding    - Variables and Types    - Simple Input/Output    - Comments    - If/Else    - Lists    - Loops    - Functions    - Imports    - Style Guides **Preface**:  It is not this notebook's goal to teach you all of Python. Instead, the goal is to teach you just enough Python so that you can apply the basics of it to geophysical data analysis.  We're going to skip over some *very* important coding concepts, such as classes and objects, dictionaries, and exceptions.  These are certainly good things to know, and in the future you will need to learn them if you work in a geoscience job with a heavy emphasis on Python.  However, right now, said topics are not our priority.This notebook is meant as a quick and dirty introduction to some fundamental Python concepts.  There is not much space for dedicated practice of the introduced concepts, but you're encouraged to mess around in the code blocks and experiment.  Think of this notebook as something to keep on hand as a reference on core Python syntax.  There's also some links to where you can find more information.If you have some coding experience, the majority of this notebook will be purely review. This notebook was adapted from a notebook used to teach beginner Python to geophysics students at the Colorado School of Mines. Thank you to <NAME> for helping review this notebook.Now let's get into the wonderful world of programming in Python!  Variables and Types----____The basic building block of any coding script is a variable.  Just like variables in algebra, coding variables can take on any value.<jupyter_code>a = 12
print(a)<jupyter_output>12<jupyter_text>In coding, `=` means 'assign'.  We'll discuss input/output in the next section, but it should be self-explanatory that `print()` lets me print out whatever I put in the paranthesis.*type* refers to how we classify our variables.  An *int* is an integer, a *float* is a decimal point number, etc.  If you do not explictly give your variable a type, Python will give a type for you.Numeric types:*   *int* = integer number*   *long* = long integer number (integer with lots of digits so Python gives it more space)*   *float* = decimal number*   *complex* = complex number (in python, we use *j* rather than *i* to indicate the imaginary component of a complex number)<jupyter_code>b = int(8)
c = float(12.1)
d = int(12.1)
e = complex(4, 5)
print(b)
print(c)
print(d)
print(e)<jupyter_output>8
12.1
12
(4+5j)<jupyter_text>Notice how Python truncated the decimal on that second to last variable because I explictly declared it to be an integer value.  Small errors like that can creep up on you as you code, so always be careful of how you or Python is assigning data types.  We can check Python types easily using `type(variable_we_want_to_type_check)`.<jupyter_code>print('The type of variable b is:')
print(type(b))
print('The type of variable c is:')
print(type(c))
print('The type of variable e is:')
print(type(e))<jupyter_output>The type of variable b is:
<class 'int'>
The type of variable c is:
<class 'float'>
The type of variable e is:
<class 'complex'><jupyter_text>We can combine variables with scalar values or other variables using our standard set of mathematical operators.<jupyter_code>a = 2
b = 3
c = a + b # addition
print(c)
d = a - b # subtraction
print(d)
e = a * b # multiplication
print(e)
f = a / b # division
print(f)
g = a ** b # exponentiation
print(g)
h = a % b # modulus, or remainder after division
print(h)<jupyter_output>5
-1
6
0.6666666666666666
8
2<jupyter_text>Let's quickly elaborate on the modulus, as it's not used in your standard math courses, and it probably seems really silly to calculate a remainder. However, this has more uses than you may think in programming. For example, we can use it to calculate if a number is even or odd.  If a number is odd and we divide it by 2, the remainder should be 1, right? (Don't worry about the if/else syntax, we'll cover that soon!)  Try assigning any number to the variable 'k'.<jupyter_code>print('Let us check if a number is odd:')
k = 10
print(k)
if(k % 2 == 1): # read as: 'if k divided by 2 has a remainder of 1...'
    print('The number is odd')
else:
    print('The number is even')<jupyter_output>Let us check if a number is odd:
10
The number is even<jupyter_text>Variables don't have to be numbers.  They can be characters or strings of text.Non-numeric types:*   *str* = string of text, represented in the code by surrounding the string with single or double quotes.*   *bool* = boolean, a variable type that can be either `True` or `False`.<jupyter_code>my_string = 'Hello World!'
print(my_string)<jupyter_output>Hello World!<jupyter_text>Simple Input/Output----____Generally speaking, our programs will take in some amount of input information and output some result.  Most geoscientists will obtain inputs from data files they feed into their programs, but input can be simple as asking the users for some input from their keyboard.  We can define a variable from user input using `input()`.  As you already know, we can display information using `print()`.<jupyter_code>user_number = float(input('Enter a number. \n'))
new_number = user_number + 3
print('Your new number is:')
print(new_number)<jupyter_output>Enter a number. 
1
Your new number is:
4.0<jupyter_text>A few things to note:*   Notice the explicit declaration of a float variable in the input line.  Input is by default a string, so we must cast the input into a number type such as *float* or an *int* so we can do math with it.*   Because we made `user_number` a *float*, Python also makes `new_number` a float to preserve precision.*   There are neat ways to embed variables in strings without using two separate print statements.  If such practices interest you, I would suggest you look into the topic of string formatting and the `.format()` string method: https://docs.python.org/3/library/string.htmlstring-formattingFor the most part, we will only need to use file input, rather than keyboard input.  We'll cover file input/output in a another notebook as that's a much meatier topic.  Comments----____It's no fun to read Python code that isn't well explained.  We use *comments* to explain what we're doing.  Commented lines begin with a ``, followed by whatever we want to say about the line of code.  The commented line isn't 'seen' when we execute the code, so we don't get any errors from the comments.Comments come in a few flavors, but starting out it's best to use descriptive block comments.  Block comments are one or more lines of comments that appear before a block of code.  They explain what that chunk of code does. Of course, if you're using a Notebook format like Jupyter or Colab, the practice of block comments is eclipsed by using Markdown cells instead. Regardless, it can good practice to include block comments anyways, in case you wind up moving notebook code into a dedicated script.<jupyter_code># Applying a scalar transform to the user's input.
# This accounts for the uncertainty in the heat
# resistance of the thermodenuder.

user_number = float(input('Enter a number. \n'))
new_number = user_number + 3
print('Your new number is:')
print(new_number)<jupyter_output>Enter a number. 
1
Your new number is:
4.0<jupyter_text>Note that I'm explaining what I'm doing as well as why I'm doing it.  You don't always have to explain your 'why', but sometimes it helps put code in context.  Also note how I didn't explain every single line of code.  I don't need to explain what `print()` does; people should know that.  I also avoid stating the obvious, like "I'm adding 3 to the input".  Everyone can see that I'm adding 3; they may be asking themselves what purpose that's accomplishing.  Maybe it accounts for an edge effect in a graphic, or it corrects an equation in a model, etc.  Think about your audience when writing your comments.  Be descriptive enough that they know what you're doing, but don't be overly verbose to the point where the comments are useless.  Keep it short and impactful.There's a couple other flavors of comments/textual description.  At the start of some IDEs, such as Spyder, you may see what look like a comment that begins with three single quotes, spans multiple lines, and ends with three single quotes.  This is called a docstring, which is an important concept that's outside the scope of this class.  It's kind of like a mega-comment that explains what an entire program or function does.  Certain programs can find docstrings and combine them together to create documentation for a set of scripts.<jupyter_code>'''Introductory notebook to teach students basic Python syntax.
   Runs natively in Jupyter Notebooks or Google Colab.
'''

# The red text above is a docstring.  You may see them if looking
# at python scripts other people have written.  Don't worry about putting
# them in your code for this class unless you really want to!<jupyter_output><empty_output><jupyter_text>If/Else----____We can use *conditional statements* to control the flow of our code, creating a river with many branching paths rather than a single stream. `if` will only allow the code below it to execute if its conditions are met (`a > 10`).  Otherwise, our `else` statement will execute. Python accepts the most common comparison/inequality operators, such as `>`, `=`, `<=`. Equality is represented by `==`, while "not equals to" is represented by `!=`.<jupyter_code>a = 11
if a > 10:
    b = 5
else:
    b = 0
print(b)

a = 9
if a > 10:
    b = 5
else:
    b = 0
print(b)<jupyter_output>5
0<jupyter_text>We can stack multiple if statements to an else statement using `elif` (else if) statements after the first `if` statement.  If the first `if` statement fails, we move to the first `elif` statement.  If that statement fails, we move down to the next `elif` statement, and so on. If no `if` or `elif` statements pass then the `else` statement will run. **For a given set of if/elif/else statements, only one will execute!**<jupyter_code>a = 10
if a > 10:
    b = 5
elif a == 10:
    b = 2
elif a == 20:
    b = 20
else:
    b = 0
print(b)<jupyter_output>2<jupyter_text>Notice how I check equality using `==`.  We have to do this because `=` already means assign, so it can't be both 'assign' and 'is equal to'!  This is a common error when coding so be sure to check and make sure you mean `=` (assign right side information to left side variable) or `==` (is left side equal to right side?).`if` statements can also be coupled with booleans rather than explicit conditional statements.  If the boolean is true, the indented block executes.<jupyter_code>a = True
if(a):
    b = 5
else:
    b = 0
print(b)<jupyter_output>5<jupyter_text>`if(a)` can be thought of as a simple way of writing `if(a==True)`.We can stack multiple conditions together with the keywords `and` and `or`.  As you may guess, `and` ensures a conditional statement is true only if both statements are true.  `or` can execute as long as one of the statements is true.<jupyter_code>a = 15

if a > 10 and a < 20:
    b = 5
else:
    b = 0
print(b)

if a > 10 or a < 20:
    b = 5
else:
    b = 0
print(b)

if a > 20 and a < 30:
    b = 5
else:
    b = 0
print(b)

if a > 20 or a < 30:
    b = 5
else:
    b = 0
print(b)<jupyter_output>5
5
0
5<jupyter_text>Try to track the logic for each of the above four `if` statements.  Lists----____Sometimes, we have multiple variables that are related to each other.  For instance, you may be a teaching assistant at a state school in Colorado.  You have a ton of grading to do and not nearly enough time for skiing and playing video games.  If you wanted to use Python to speed up your grading, you could reason that you'll have different variables for each student's grade.  You could write them out one at a time.<jupyter_code>grade_0 = 80
grade_1 = 90
grade_2 = 81
grade_3 = 97<jupyter_output><empty_output><jupyter_text>But this would be really clunky, and the whole point of coding is to make our lives easier.  A Python *list* is an array of items that we can place together, iterate through, and manipulate as a single unit.  We declare lists just as we would variables, and we place items in brackets, separating them with commas.<jupyter_code>assignment_grades = [80, 90, 81, 97]
print(assignment_grades[0])<jupyter_output>80<jupyter_text>We can access any value in the array using bracket notation (remember that array counting starts at 0, not 1). Lists have their own set of functions/actions that can be accessed by typing our list name, then a '.', then the function name.  For example, we can add on another value to our list using `name_of_our_list.append(value)`.  We'll make our own functions in a few sections, but for now, know that Python has plenty of built in functions for lists and other constructs.<jupyter_code>assignment_grades.append(85)
print(assignment_grades)<jupyter_output>[80, 90, 81, 97, 85]<jupyter_text>In later notebooks, we'll discuss other ways of creating arrays in Python, which will open new doors for how we manipulate data.  Loops----____Wouldn't it be great if there was a way to quickly go through the array and perform some basic math or check a conditional statement?  After all, it would be really annoying to have to call each value separately.<jupyter_code>assignment_grades = [90, 97, 89, 91, 92, 94, 95, 78, 82, 83, 90]
assignment_letters = []
if assignment_grades[0] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
if assignment_grades[1] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
if assignment_grades[2] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
if assignment_grades[3] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
if assignment_grades[4] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
if assignment_grades[5] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
if assignment_grades[6] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
if assignment_grades[7] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
if assignment_grades[8] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
if assignment_grades[9] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
if assignment_grades[10] > 90:
    assignment_letters.append('A')
else:
    assignment_letters.append('F--')
print(assignment_letters)<jupyter_output>['F--', 'A', 'F--', 'A', 'A', 'A', 'A', 'F--', 'F--', 'F--', 'F--']<jupyter_text>Seriously, nobody would ever want to code if we had to do it like that.  In Python, we can repeat an action multiple times using a loop.  A loop is a section of code that repeats over and over until a certain condition is met.In data science, `for` loops are the most common.  Let's see one in action and then break down the syntax.<jupyter_code>assignment_letters = []
for i in range(11):
    if(assignment_grades[i] > 90):
        assignment_letters.append('A')
    else:
        assignment_letters.append('F--')
print(assignment_letters)<jupyter_output>['F--', 'A', 'F--', 'A', 'A', 'A', 'A', 'F--', 'F--', 'F--', 'F--']<jupyter_text>Well look at that, we got the same result with a fraction of the work.  Everything except for the second line of code is old news, so let's look at the 'for' statement.We start every for loop statement with `for`, followed by our iterating element.  Our iterating element is a variable that is going to exist only in our for loop and helps us keep track of where we are in the loop.  Here we call our iterating element 'i', which is fine for simple code like this.  If we do more complex tasks, we may want to give our variables more meaningful names.Next, we have the keyword `in`, followed by whatever sequence, list, etc. that we want to cycle through.   For our loop, we generate our sequence using `range()`. `range()` is a generator that creates integers leading up to (but not including) the number in parantheses.<jupyter_code>for i in range(11):
    print(i)<jupyter_output>0
1
2
3
4
5
6
7
8
9
10<jupyter_text>Again, recall that we start counting at 0. We can feed whatever value we want into range.  For instance, rather than hardcoding the length of our assignment grades array, we can just feed in the length of the array to the range function using `len(assignment_grades)`<jupyter_code>print(len(assignment_grades))

assignment_letters = []
for i in range(len(assignment_grades)):
    if(assignment_grades[i] > 90):
        assignment_letters.append('A')
    else:
        assignment_letters.append('F--')
print(assignment_letters)<jupyter_output>11
['F--', 'A', 'F--', 'A', 'A', 'A', 'A', 'F--', 'F--', 'F--', 'F--']<jupyter_text>Functions----____Variables and variable manipulation allow us to walk, while functions allow us to run and fly.  A function is a block of code that can be called repeatedly throughout a script.  We can supply some information to the function (these pieces of information are called arguments), and the function will return an output.<jupyter_code>def assign_grade(assignment_grade):
    '''Return a grade letter for a given assignment grade number.
    Inputs:
    assignment_grade - grade to assign letter to
    '''
    if(assignment_grades[i] > 90):
        return 'A'
    elif(assignment_grades[i] > 80):
        return 'B'
    elif(assignment_grades[i] > 70):
        return 'C'
    elif(assignment_grades[i] > 60):
        return 'D'
    elif(assignment_grades[i] > 50):
        return 'F'
    else:
        return 'F--'<jupyter_output><empty_output><jupyter_text>We can use our function anywhere our code.  Let's toss it in a loop.<jupyter_code>assignment_grades = [90, 97, 89, 91, 92, 94, 95, 78, 82, 83, 90]                 
assignment_letters = []
for i in range(len(assignment_grades)):
    assignment_letter = assign_grade(assignment_grades[i])
    assignment_letters.append(assignment_letter)
print(assignment_letters)<jupyter_output>['B', 'A', 'B', 'A', 'A', 'A', 'A', 'C', 'B', 'B', 'B']<jupyter_text>With just a couple basic programming concepts, we've greatly reduced our grading code from where we started.    Imports----____So far, we've dealt with native Python.  To be honest, native Python kind of sucks in some aspects.  The Python interpreter is slow as heck, and there's a lot of features that other languages have that basic Python lacks.  But that's intentional.  The real magic of Python is the ability to pull in other people's work by importing modules.  A module is a set of Python code containing pre-built functions that anyone can reuse.  Why reinvent the wheel?  Why spend hours upon hours coming up with an effective linear algebra system when the makers of NumPy already did?  Just import it!We'll cover imports again in later notebooks, but just so you have the syntax for everything in one nice spot, here are a few ways to import modules.<jupyter_code>import numpy # we import the entire module
array = numpy.ones((5,5))
print(array)
import numpy as np # we import the entire module but give it an alias
array = np.ones((5,5))
print(array)
 # we import only what functions we need.  This is best practice, but
 # we get lazy usually and just import everything for the heck of it.

from numpy import ones 
array = ones((5,5))
print(array)
# this is literally the worst thing you could do ever.  Good luck
# managing your namespace when you wildcard import everything.
# Don't be surprised if you accidently rename a function and break
# the internet.  No seriously, don't do this ever.

from numpy import * # a * indicates a wildcard import
array = ones((5,5))
print(array)

# To elaborate on why the last code block is so bad, imagine you want to 
# name a variable "zeros".  You do this not realizing that numpy also has
# a function called zeros.  Later, you actually want to use numpy's zeros
# function, but you've accidentally overwritten that name, leading to this
# error.

from numpy import * # a * indicates a wildcard import
array = ones((5,5))
print(array)

zeros = 0
print(zeros)

# hundreds of lines of code later...

array_2 = zeros((5,5))
print(array_2)<jupyter_output>[[1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1.]]
0<jupyter_text>Ooops, that wildcard import wasn't very smart of us.  We inadvertently created a minefield in our code where we could accidentally overwrite a number of functions.  Modules are powerful, but remember that with great power comes great responsibility.  Luckily, if you're viewing this in Google Colab, Google's coders know what's up and give us a nice button to search stack overflow for our error message.  Stack Overflow is a nice website for receiving helpful yet also sometimes quite condescending coding advice.For the time being, we can fix our error either by naming our variable `zeros` something different, or we can change the wildcard import to something sensible.  Style Guides----____There are nearly infinite ways to structure your code, name your variables, etc.  Which way is best?If you're just coding for yourself, the answer is whatever way is best for you.  If you're coding alongside others, then it makes more sense to have a standard set of rules that everyone follows.  These rules are put together in what's called a style guide.  The style guide contains a set of best practices to follow when coding: what naming conventions to use, where to put your functions, how to document your code, etc.In Python, the standard for almost every company or institution is PEP8, which can be found at this link: https://www.python.org/dev/peps/pep-0008/PEP8 contains a ton of information on topics we haven't covered yet, but I encourage you to peruse it all the same when you have some down time in class.  Even if you don't understand everything, good habits are easiest to establish in the beginning of your career.  Consider incorporating the following practices from PEP8 when you code your starting assignments:*   Keep all lines of code under 80 characters.  If you code in Google Colab, this is represented by a dashed line that appears when you begin to reach 80 characters.  This isn't as important as it was back in the day when screens were smaller, but nobody enjoys reading lines of code that never seem to end.*   Be consistent with quotes.  Single or double quotes both work in Python, but pick one and stick to it.*   Avoid unnecessary whitespae.*   Use appropriate block comments before a block of code to properly explain in proper English.  Avoid short inline comments that state the obvious *   *(e.g. x = x + 1  increment x by 1)**   Use a consistent naming convention for your variables.  PEP8 prefers lowercase_separated_by_underscores, but some people swear by mixedCaseWhereEveryWordAfterTheFirstIsCapitalized.*   If you code any functions you use later in your code, put them at the beginning of your script or notebook.  Conclusion----____Wow, look at you!  You're ready to start kicking butt at Python for geophysical data analysis.  Throughout the rest of the module, we'll focus on more specific key topics important to our work, but they all build off these fundamental concepts.  Going forward, it's always good to practice.  I encourage you to look at some online tutorials to help round out your knowledge of basic Python as well as just get some experience writing basic code.Some important resources:*   Python 3's documentation: https://docs.python.org/3/*   Anaconda's website: for downloading a standalone version of Python (Google Colab is awesome, but not without its faults.  Managing your own private workspace will be essential later on in your career.): https://www.anaconda.com/download/*   r/ProgrammerHumor, for dank memes: https://www.reddit.com/r/ProgrammerHumor/Happy coding!*The GPGN268 Teaching Team*<jupyter_code><jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.07844602166604409, 'fraction_numerical': 0.01398746523886606, 'mean_word_length': 4.210640138408304, 'pattern_counts': {'":': 0, '<': 86, '<?xml version=': 0, '>': 108, 'https://': 5, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 9, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '24', 'dir': 'jupyter-structured-clean-dedup', 'id': '612852', 'license': 'MIT', 'n_tokens_mistral': 7117, 'n_tokens_neox': 6668, 'n_words': 3486, 'path': 'Python_Basics.ipynb', 'repo_name': 'envgp/sample_jupyter_notebooks', 'size': '24098'}