<jupyter_start><jupyter_text>Data Analysis with Jupyter Notebooks. Tutorial 3<NAME>, University of Bath.  Contents- [Data Types](data_types)    - [Integers and Floats](numbers)        - [Scientific Notation](scientific_notation)    - [Strings](strings)    - [Lists](lists)- [Numpy and arrays](numpy)  Data typesSo far we have talked about &ldquo;data&rdquo; and &ldquo;results&rdquo;, but what are the pieces of information that we want to manipulate? Typically numbers (or groups of numbers) or text (or lists of text). Different kinds of data can be used for different things: numbers can be combined in mathematical expressions, text can be printed, searched, or reorganised; numbers can be arranged by magnitude, names can be arranged by alphabetical order. In Python, these differences are represented by different **data types**. Numbers: *int* and *float*We will discuss two kinds of numeric types: integers and floating point numbers. Python has other built in numeric data types, including complex numbers, which are useful in specialised cases.Whole numbers, without decimal points are integers, e.g. 1, 6, 2331.  Numbers with decimal points are floating point numbers or &ldquo;floats&rdquo;, e.g. 1.0, 232.141, 1.3e5.  That last example, 1.3e5, uses scientific notation and is shorthand for 130000.0.  Scientific NotationVery large and very small numbers can be written using **scientific notation**. For example, instead of 0.0000241, we would normally write 2.41&times;10-5. In Python this would be written `2.41e-5` or `2.41e-05`.>```python2.41e-5 == 0.0000241```  StringsStrings are any sequence of text. We indicate that a sequence of text is a string, and not a Python command, by enclosing it in single or double quotes. Being able to use either quote type allows strings that themselves contain quotes.>```python'this is a string using single quotes'``` >```python"this is a string using double quotes"``` >```python'this string has "nested quotes"'```  ListsPython also contains built-in data types for collections of things. For data analysis we often deal with sets of numbers. These can be collected in **lists**.A list is denoted by a series separated by commas, and enclosed in square brackets:>```pythonmy_list = [ 1, 2, 3, 4 ]mylist``` although lists can contain any set of Python objects, even other lists:>```pythonmy_other_list = [ 4, 1.5, 'peach' ]my_other_list``` >```pythonboth_lists = [ my_list, my_other_list ]both_lists``` To refer to one element in a list, use the **index** of that element. Index numbering counts the number of jumps along the sequence, so starts at zero.>```python 1st element (zero jumps along the sequence)print( my_other_list[0] ) 2nd element (one jump along the sequence)print( my_other_list[1] )  3rd element (two jumps along the sequence)print( my_other_list[2] ) ``` Using an index outside the range of elements in the list will produce an error. For example, `my_other_list` has three elements, but `my_other_list[3]` tries to return the *4th* element (which does not exist)>```pythonprint( my_other_list[3] )``` You can also refer to a sequence of elements by giving a *range* as the index:<jupyter_code># run this cell to create the list `alphabet`
alphabet = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 
             'h', 'i', 'j', 'k', 'l', 'm', 'n', 
             'o', 'p', 'q', 'r', 's', 't', 'u',
             'v', 'w', 'x', 'y', 'z' ]<jupyter_output><empty_output><jupyter_text>>```pythonalphabet[3:8]```→ start from three jumps, finish at eight jumps, i.e. elements 4 to 9. Negative numbers count backwards from the end of the sequence.>```pythonalphabet[-8:-3]```→ 9th from the end up to 4th from the end. And leaving out one of the numbers in the range will include all elements up to the start or end of the sequence.>```pythonalphabet[14:]``` >```pythonalphabet[:14]```  numpy and arraysAlthough lists can be very useful for handling ordered collections of things, for data manipulation we usually deal with ordered lists of only numbers. The flexibility of lists means using them is (relatively) computationally slow. This is not an issue for small data sets, but can be prohibitive for large data sets, with perhaps millions or more entries.An alternative data type, specifically designed for manipulating (large) numerical data sets is the **numpy array**. `numpy` is a module for numerical scientific computing with Python, and is conventionally imported via```pythonimport numpy as np```This is similar to the import math we saw [above](functions_and_modules), but uses the as keyword to make `numpy` more convenient to work with.>```pythonimport mathmath.sqrt(4)```<jupyter_code>import math<jupyter_output><empty_output><jupyter_text>Having imported `numpy` (as `np`) we can store lists of numbers as `numpy` arrays.>```pythonimport numpy as npa = np.array( [ 1, 2, 3, 4 ] )a``` You can think of a 1-dimensional `numpy` array as a vector, and we can use very compact code to perform *vector* mathematical operations on the entire array.>```pythona + 1``` >```pythona**2```Remember that `**` is the $power$ operator. This code calculates $a^2$ for every number stored in `a`. In both these cases, the mathematical operation (add one; square) is applied to every element in the array, and a new array with *all* the results is returned.If the mathematical expression contains two (or more) arrays, then an **element-by-element** operation is performed:e.g. vector addition:>```pythonb = np.array( [ 5, 6, 7, 8 ] )a + b``` >```pythona * b``` Let us try to calculate the square root of all the numbers in `a`:>```pythonfrom math import sqrtsqrt(a)```<jupyter_code>import numpy as np
a = np.array( [ 1, 2, 3, 4 ] )
a
np.sqrt(a)<jupyter_output><empty_output><jupyter_text>This gives an error.  Because `numpy` is not part of the standard Python library, the `sqrt` function provided by the `math` module does not know how to treat a `numpy` array of numbers. To do what we want we can use the `sqrt` function in `numpy` instead. >```pythonnp.sqrt(a)``` Edit the previous code cell to use the numpy version of sqrt instead of the standard function from the math module.<jupyter_code># This cell tests your answers from the three previous code cells.
# You do not need to edit it
assert _[0] == math.sqrt(1)
assert _[1] == math.sqrt(2)
assert _[2] == math.sqrt(3)
assert _[3] == math.sqrt(4)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10625978090766823, 'fraction_numerical': 0.01815336463223787, 'mean_word_length': 4.923076923076923, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '402792', 'license': 'CC-BY-4.0', 'n_tokens_mistral': 1939, 'n_tokens_neox': 1771, 'n_words': 888, 'path': 'Tutorial_3.ipynb', 'repo_name': 'pythoninchemistry/chem_data_analysis_jupyter', 'size': '6402'}