<jupyter_start><jupyter_text>Introduction to Python and Jupyter notebooks Python is a programming language where you don't need to compile. You can just run it line by line (which is how we can use it in a notebook). So if you are quite new to programming, Python is a great place to start. The current version is Python 3, which is what we'll be using here.One way to code in Python is to use a Jupyter notebook. This is probably the best way to combine programming, text and images. In a notebook, everything is laid out in cells. Text cells and code cells are the most common. If you are viewing this section as a Jupyter notebook, the text you are now reading is in a text cell. A code cell can be found just below.To run the contents of a code cell, you can click on it and press Shift + Enter. Or if there is a little arrow thing on the left, you can click on that.<jupyter_code>1 + 1<jupyter_output><empty_output><jupyter_text>If you are viewing this section as a Jupyter notebook, execute each of the code cells as you read through.<jupyter_code>a = 1
b = 0.5
a + b<jupyter_output><empty_output><jupyter_text>Above we created two variables, which we called `a` and `b`, and gave them values. Then we added them. Simple arithmetic like this is pretty straightforward in Python. Variables in Python come in many forms. Below are some examples.<jupyter_code>an_integer = 42 # Just an integer
a_float = 0.1 # A non-integer number, up to a fixed precision
a_boolean = True # A value that can be True or False
a_string = '''just enclose text between two 's, or two "s, or do what we did for this string''' # Text
none_of_the_above = None # The absence of any actual value or variable type<jupyter_output><empty_output><jupyter_text>As well as numbers, another data structure we can use is the *list*.<jupyter_code>a_list = [0,1,2,3]<jupyter_output><empty_output><jupyter_text>Lists in Python can contain any mixture of variable types.<jupyter_code>a_list = [ 42, 0.5, True, [0,1], None, 'Banana' ]<jupyter_output><empty_output><jupyter_text>Lists are indexed from `0` in Python (unlike languages such as Fortran). So here's how you access the `42` at the beginning of the above list.<jupyter_code>a_list[0]<jupyter_output><empty_output><jupyter_text>A similar data structure is the *tuple*.<jupyter_code>a_tuple = ( 42, 0.5, True, [0,1], None, 'Banana' )
a_tuple[0]<jupyter_output><empty_output><jupyter_text>A major difference between the list and the tuple is that list elements can be changed<jupyter_code>a_list[5] = 'apple'

print(a_list)<jupyter_output>[42, 0.5, True, [0, 1], None, 'apple']<jupyter_text>whereas tuple elements cannot<jupyter_code>a_tuple[5] = 'apple'<jupyter_output><empty_output><jupyter_text>Also, we can add an element to the end of a list, which we cannot do with tuples.<jupyter_code>a_list.append( 3.14 )

print(a_list)<jupyter_output>[42, 0.5, True, [0, 1], None, 'apple', 3.14]<jupyter_text>Another useful data structure is the *dictionary*. This stores a set of *values*, each labeled by a unique *key*.Values can be any data type. Keys can be anything sufficiently simple (integer, float, Boolean, string). It cannot be a list, but it _can_ be a tuple.<jupyter_code>a_dict = { 1:'This is the value, for the key 1', 'This is the key for a value 1':1, False:':)', (0,1):256 }<jupyter_output><empty_output><jupyter_text>The values are accessed using the keys<jupyter_code>a_dict['This is the key for a value 1']<jupyter_output><empty_output><jupyter_text>New key/value pairs can be added by just supplying the new value for the new key<jupyter_code>a_dict['new key'] = 'new_value'<jupyter_output><empty_output><jupyter_text>To loop over a range of numbers, the syntax is<jupyter_code>for j in range(5):
    print(j)<jupyter_output>0
1
2
3
4<jupyter_text>Note that it starts at 0 (by default), and ends at n-1 for `range(n)`. You can also loop over any 'iterable' object, such as lists<jupyter_code>for j in a_list:
    print(j)<jupyter_output>42
0.5
True
[0, 1]
None
apple
3.14<jupyter_text>or dictionaries<jupyter_code>for key in a_dict:
    value = a_dict[key]
    print('key =',key)
    print('value =',value)
    print()<jupyter_output>key = 1
value = This is the value, for the key 1

key = This is the key for a value 1
value = 1

key = False
value = :)

key = (0, 1)
value = 256

key = new key
value = new_value<jupyter_text>Conditional statements are done with `if`, `elif` and `else` with the following syntax.<jupyter_code>if 'strawberry' in a_list:
    print('We have a strawberry!')
elif a_list[5]=='apple':
    print('We have an apple!')
else:
    print('Not much fruit here!')<jupyter_output>We have an apple!<jupyter_text>Importing packages is done with a line such as<jupyter_code>import numpy<jupyter_output><empty_output><jupyter_text>The `numpy` package is important for doing maths<jupyter_code>numpy.sin( numpy.pi/2 )<jupyter_output><empty_output><jupyter_text>We have to write `numpy.` in front of every numpy command so that it knows how to find that command defined in `numpy`. To save writing, it is common to use<jupyter_code>import numpy as np

np.sin( np.pi/2 )<jupyter_output><empty_output><jupyter_text>Then you only need the shortened name. Most people use `np`, but you can choose what you like.You can also pull everything straight out of `numpy` with<jupyter_code>from numpy import *<jupyter_output><empty_output><jupyter_text>Then you can use the commands directly. But this can cause packages to mess with each other, so use with caution.<jupyter_code>sin( pi/2 )<jupyter_output><empty_output><jupyter_text>If you want to do trigonometry, linear algebra, etc, you can use `numpy`. For plotting, use `matplotlib`. For graph theory, use `networkx`. For quantum computing, use `qiskit`. For whatever you want, there will probably be a package to help you do it. A good thing to know about in any language is how to make a function.Here's a function, whose name was chosen to be `do_some_maths`, whose inputs are named `Input1` and `Input2` and whose output is named `the_answer`.<jupyter_code>def do_some_maths ( Input1, Input2 ):
    the_answer = Input1 + Input2
    return the_answer<jupyter_output><empty_output><jupyter_text>It's used as follows<jupyter_code>x = do_some_maths(1,72)
print(x)<jupyter_output>73<jupyter_text>If you give a function an object, and the function calls a method of that object to alter its state, the effect will persist. So if that's all you want to do, you don't need to `return` anything. For example, let's do it with the `append` method of a list.<jupyter_code>def add_sausages ( input_list ):
    if 'sausages' not in input_list:
        input_list.append('sausages')
print('List before the function')
print(a_list)

add_sausages(a_list) # function called without an output

print('\nList after the function')
print(a_list)<jupyter_output>List before the function
[42, 0.5, True, [0, 1], None, 'apple', 3.14]

List after the function
[42, 0.5, True, [0, 1], None, 'apple', 3.14, 'sausages']<jupyter_text>Randomness can be generated using the `random` package.<jupyter_code>import random
for j in range(5):
    print('* Results from sample',j+1)
    print('\n    Random number from 0 to 1:', random.random() )
    print("\n    Random choice from our list:", random.choice( a_list ) )
    print('\n')<jupyter_output>* Results from sample 1

    Random number from 0 to 1: 0.9565207808448243

    Random choice from our list: 3.14


* Results from sample 2

    Random number from 0 to 1: 0.2965136670559021

    Random choice from our list: sausages


* Results from sample 3

    Random number from 0 to 1: 0.5742595097667611

    Random choice from our list: 3.14


* Results from sample 4

    Random number from 0 to 1: 0.8530438802121619

    Random choice from our list: True


* Results from sample 5

    Random number from 0 to 1: 0.1329354419675386

    Random choice from our list: None
{'fraction_non_alphanumeric': 0.0912889339073183, 'fraction_numerical': 0.029121296530767282, 'mean_word_length': 4.970521541950113, 'pattern_counts': {'":': 0, '<': 96, '<?xml version=': 0, '>': 96, 'https://': 0, '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': '26', 'dir': 'jupyter-structured-clean-dedup', 'id': '46642', 'license': 'Apache-2.0', 'n_tokens_mistral': 2755, 'n_tokens_neox': 2578, 'n_words': 1038, 'path': 'notebooks/ch-prerequisites/python-and-jupyter-notebooks.ipynb', 'repo_name': 'soumya-s3/platypus', 'size': '7898'}