#!/usr/bin/env python # coding: utf-8 # # Table of Contents #
# # The basics of the Python language # # # # Python is a popular general purpose, free language. As opposed to other systems that are focused towards a particular task (e.g. R for statistics), Python has a strong following on the web, on systems operations and in data analysis. For scientific computing, a large number of useful add-ons ("libraries") are available to help you analyse and process data. This is an invaluable resource. # # In addition to being free, Python is also very portable, and easy to pick up. # # ## Launching Python # # We will be using the Jupyter (or IPython) notebook for you to interact with Python through the browser. There are ways, but the notebook has proved to be quite good for data analysis and mixing code and the logic behind it. To launch the notebook, go into a shell and type # # jupyter notebook # # This will open a new tab in the browser (or launch a browser if you haven't got one opened) with the Jupyter file selector. Through this, you can navigate through the file system, and select an existing notebook or create a new one. For this # #
# # **Note:** We recommend that you open the course notebooks and save a copy of them under a different name (`File->Make a copy`) and that you edit and modify that copy rather than the original. # #
# # The jupyter notebook allows you to either enter Python code (dropdown says `Code`), or text using the [markdown standard](http://datascience.ibm.com/blog/markdown-for-jupyter-notebooks-cheatsheet/) (dropdown says `Markdown`) # # ## Computer programs # # We can see a computer as a machine that stores some form of "data" and does operations on these data using a set of actions (e.g. a [Turing machine](https://en.wikipedia.org/wiki/Turing_machine), if you can stomach lots of irrelevant detail!). This set of actions is called *a program* (or *script*). A program basically lists a set of transformation on data. These can include arithmetic operations, working with text, interacting with the network, etc. But a critical point of all this is *data*, and we will see how to Python deals with data first. # # ## Data types # # Different pieces of information are stored differently. A *data type* describes how data are described. In Python, there is no need to define the data type, as Python will inspect the data and decide on the best data type to use. Perhaps the simplest data types are those related to storing numbers, let's see them... # # ### Integers and Floating point numbers # # Integers are whole numbers, such as 1, 2, 3, 456778, -478576, ... They're different from floating point numbers such as 1.8, -2.35, 1.78456e34, 1.78456e-34. Computers store integers in base 2 (binary numbers), so that a number like 10235 can be written as # $$ # \begin{split} # 10235 &= 1\cdot 2^{12}+0\cdot 2^{12}+0\cdot 2^{11}+1\cdot 2^{10}+1\cdot 2^{9}+ \\ # &\,1\cdot 2^{8}+1\cdot 2^{7}+1\cdot 2^{6}+1\cdot 2^{5}+1\cdot 2^{4}+1\cdot 2^{3}+0\cdot 2^{2}+1\cdot 2^{1}+1\cdot 2^{0}.\\ # \end{split} # $$ # # This means that to fit 10235, we need 13 "boxes" that can be set to either 0 or 1. These boxes are usually called *bits*, and 8 bits are assumed to be one byte. We note that it is often the case that one bit position might be used to indicate the sign of the number. In Python 2.7, most integers are either 4 or 8 bytes (the second option is the most popular one), depending on your Python build. If we use an 8 byte storage, that means we are using 64 bits, and thus the maximum integer would be $2^{63}-1$. # # Floating point numbers are real numbers. Computers store them as [floating points](http://floating-point-gui.de/). In this convention, the computer basically stores a number like 2.35 as $235\times 10^{-3}$, or as a base and an exponent. The base can be stored as for integers, and the exponent as an additional integer. # # We can use numbers and operate with them as you'd expect with the symbols `+, -, *, /, %` and `//`: # In[1]: print 2+(3*5) # Integer operations, returns **integer** print 2.+(3*5.) # Mixed integer+float operations, returns **float** print 10/2 # Integer divided by integer -> return **integer** print 10./2. # Float divided by float -> returns **float** print 10/2 # Float divided by integer -> returns **Integer** print 37 % 5 # Remainder of dividing 37/5 -> returns **Integer** print 10//3 # Integer division -> returns **Integer** # We can also compare numbers: # In[8]: print 5 > 4 print 5 == 5 print 5 < 4 print 3 <= 4 # ### Text # # In Python, collections of characters (`a`, `b`, `1`, ...) are called strings. Either way, strings and characters are input by surrounding the relevant text in either double (`"`) or single (`'`) quotes. There are a number of special characters that can be encoded in a string provided they're "escaped". For example, some of these are: # # * `\n`: the carriage return # * `\t`: a tabulator # # In[2]: print "I'm a happy string" print 'I\'m a happy string' # the apostrophe has been escaped as not to be confused by end of string print "\tI'm a happy string" print "I'm\na\nhappy\nstring" # We can do a number of things with strings, which are very useful. These so-called *string methods* are defined on all strings by Python by default, and can be used with every string. # # For one, we can concatenate strings using the `+` symbol. # # #### `len` # # Gives the length of the string as number of characters: # In[47]: print "Hello " + "there" + " everyone" print len("I'm a very happy string") # #### `replace` # # We can replace all occurrences of a string within a string by some other string. We can also replace a string by an empty string, thus in effect removing it: # In[30]: print "I'm a very happy string".replace("happy", "unhappy") print "I'm a very happy string".replace("very", "") # #### `find` # # Quite often, we might want to find a string inside another string, and potentially give the location (as in characters from the start of the string) where this string occurs. We can use the `find` method, which will return either a -1 if the string isn't found, or an integer indicating where the string starts for the first time. # In[4]: print "I'm a very happy string".find("happy") print "I'm a very happy, happy string".find("happy") print "I'm a very happy string".find("breakfast") # #### `title`, `upper`, `lower` # # We can change the case of our string using these methods: # # # In[5]: print "hello there".upper() # #### Testing strings with `startswith`, `endswith`, `isalnum`, `islower` `isupper`, `isalpha`, `isspace`, `isdigit`, `istitle` # # You can test properties and contents of a string using the above methods. These methods return a **Boolean variable**, which in Python is either `True` or `False`: # In[6]: print "MCD15A2.A2005097.h17v03.005.2008007175837.hdf".startswith("MCD15") print "MCD15A2.A2005097.h17v03.005.2008007175837.hdf".endswith("hdf") print "MCD15A2.A2005097.h17v03.005.2008007175837.hdf".isalpha() print "MCD15A2".isalnum() print "MCD15A2.A2005097.h17v03.005.2008007175837.hdf".isalnum() print "2008007175837".isdigit() # #### Counting # # We can count the number of times a character or substring appears in a given string using `count`: # In[22]: print "Something or other".count("z") print "Something or Other".count("o") print "Something or Other".lower().count("o") # ### Converting types # # Often, we want to convert from one type to another. We can use `int` to convert from a string with an integer representation to an integer, `float` to convert a string with a real number into a floating point number, and so on: # In[6]: print int("5") print float("-273.15") # ## Collections of objects # # Often, we want to store collections of data. Python has a number of default data types for this: **lists** and **dictionaries**. Lists store a sequence of data objects (floats, integers, strings, ...) and these can be **retrieved by position**. Dictionaries, on the other hand, have no concept of order: they are pairings of a *key* and a *value*, and so we need to look for a particular key to retrieve its value. # # We also introduce the concept of ** *variables* **: we can store data in named containers and then operate with them: # In[ ]: a = 5 b = 6 print a * b print a - b string1 = "something" prin # ### Lists # # A list is a collection of other objects. Each object is accessed by position, with `[0]` being the first element, `[1]` the second and so on. If the *position is negative*, it means start from the end, so `[-1]` is the last element, `[-2]` is the one before the last and so on. # # We can also define *slices*: for example, we can select the third, fourth and fifth element `[2:5]` (note that the first element is selected and the last one isn't part of the selection). If we want to select all elements except the first one, we have `[1:]`. Similarly, to select all elements except the last one, we have `[:-1]`. We can also select to choose every element every $N$: `[::2]` selects the odd elements (first, third, etc), and `[1::2]` selects the even elements (second, fourth, ...). Finally, we can *reverse* a list by using `[::-1]` # In[17]: list_of_numbers = [1, 3, 5, 67, -45., 33] print list_of_numbers[5] print list_of_numbers[-1] print list_of_numbers[-2:] print list_of_numbers[2:5] print list_of_numbers[::2] print list_of_numbers[::-1] # It is also possible to have a "list of lists": a list in which one or all elements are also lists. In a way, *strings* are also "lists": you can also apply slicing to strings. Let's see how that works. We can use this to manipulate a filename, and for example, remove its extension (in this case, the extension is `.hdf`): # In[18]: a_string = "MCD15A2.A2005097.h17v03.005.2008007175837.hdf" location = a_string.find(".hdf") print a_string[:location] # ### List methods # # Lists have a [plethora of methods](https://www.programiz.com/python-programming/methods/list) that we'll be encountering. We'll just explore a couple of them to start with. We can find the location of an item in a list using `index`. We can also `append` (add an item to the end of the list), `prepend` (add an item at the beginning of the list), or `remove` or `insert` items: # In[21]: a_list = ["Hello", "the answer", "is", 42] print a_list.index(42) print a_list[a_list.index(42)] print a_list.append(".") print a_list print a_list.pop(0) print a_list # #### String and list methods # # One is always processing text. There are many list methods that allow us to interpret strings as lists. These are very powerful, so we'll look at some of them here: `split` and `join`. # # `split` allows one to split a string based on a character, and returns a list of the substrings it finds. `join` joins a list by using the same string as a separator. For example, let's suppose that we want to take our filename, split it by the `.` symbol, and then change this symbol by `!`: # # In[20]: a_string = "MCD15A2.A2005097.h17v03.005.2008007175837.hdf" print a_string.split(".") print "!".join(a_string.split(".")) # ### Dictionaries # # Sometimes it is useful to store information on different things indexed by a *key*. For example, you might want to store the monthly average daily temperature data for different weather stations. You could store them in a list, and have another list to look up what position they have. # In[30]: Hull = [7.3, 7.9, 10.5, 12.9, 16.1, 19.1, 21.6, 21.5, 18.6, 14.5, 10.3, 7.6] Sheffield = [6.8, 7.1, 9.8, 12.5, 16.1, 18.8, 21.1, 20.6, 17.7, 13.5, 9.5, 6.9] Hampstead = [7.1, 7.4, 10.5, 13.3, 16.8, 19.9, 22.4, 22.0, 18.8, 14.6, 10.3, 7.4] station_lookup = [ "Hull", "Sheffield", "Hampstead"] meteorology = [ Hull, Sheffield, Hampstead] # To find the mean average temperature in May for the three sites, we would do: # In[31]: ihull = station_lookup.index("Hull") print "Hull, June", meteorology[ihull][4] isheffield = station_lookup.index("Sheffield") print "Sheffield, June", meteorology[isheffield][4] ihampstead = station_lookup.index("Hampstead") print "Hampstead, June", meteorology[ihampstead][4] # This is quite clumsy, as what we really want to do is to access stations by name. This is where dictionaries come in. Let's create a dictionary, and store for each station, the monthly climatology. We define dictionaries using curly braces `{}` and refer to key-value pairs using the key (in this case they're strings), and separate from the value (in this case, lists, but it could be anything) by a `:`. We refer to the elements in the dictionary by using the `[key]` notation. The previous example now becomes: # In[32]: climatology = {'Hull': Hull, 'Sheffield': Sheffield, 'Hampstead': Hampstead} print climatology['Hull'][4] print climatology['Sheffield'][4] print climatology['Hampstead'][4] # ## Problems # # Inevitably, there will be problems (no doubt, I will have encountered them already throgoughout this session!). Let's see how Python reports on problems, and how we can learn to interpret the errors. One situation where this arises is when you want do do something that doesn't make sense. For example: adding one integer and one floating point number has been figured out for you. However, what happens if you try to add one floating point number and a string? # In[48]: print "A string" + 5.0 # Python informs us that we have found a `TypeError`, and tells us that we cannot combine a string and a float like that. It also tells us the line where it all went wrong. With that information, we can go back and try to solve the problem. A second example, assume we have a list with 10 elements, and we try to retrieve element 30... # In[50]: a_list = range(10) print len(a_list) print a_list[30] # So, we now have a different type of error (`IndexError`), telling us that we've gone past the end of the string. Sometimes, errors are bit more subtle and need some working. Let's see an example: # In[54]: a = 3/5 b = 10 print b/a #
# What did just happen here? Can you find what caused this *bug* and fix it? #
# # ## Control structures: loops and conditionals # # So far, we have seen how to store data, and how to do things with it. However, we do not have the ability to control what happens. To do this, we would need to be able to repeat some actions varying some parameters, and we would need to be able to control the flow of things by checking some conditions. The former kind of structures usually go by the name of *loops*, whereas the second are usually called *conditionals*. # # ### `for` loops # # The most common loop is the `for` loop. The `for` loop iterates over a sequence (e.g. a list), giving the value of each element in turn. Once it reaches the end of the sequence, the program flow continues in the next line. In Python, the contents of the loop (i.e. the things that get executed for each iteration of the loop) are indented by a tab stop. Let's see how this works. Let's print the average monthly temperatures for our previous stations in March: # In[33]: print ">>> At the start of the loop" for station in ["Hull", "Sheffield", "Hampstead"]: print climatology[station][2], "\t", print print ">>> At the end of the loop" # We see that the loop variable (`station`) sequentially takes the values in the list. The other interestig bit is that the print statement within the loop has a `,` at the end to prevent a new line character every time it is executed. Note the indentation of the print statement as well. # # We can also *nest* loops within loops. For example, we might want print a table with the climatology for each month in every row, and the different stations in different columns. Further, we might want to add a first column with the name of the month. To this end, we'll store the month names in a list in order. We will use the `enumerate` method applied to the loop iterator to get the right position for the month: # In[34]: month_name = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] for imonth, month in enumerate(month_name): print month, for station in ["Hull", "Sheffield", "Hampstead"]: print "\t", climatology[station][imonth], print # The following snippet has the climatology for the different stations above as multi-line strings (data comes from the [Met Office](https://www.metoffice.gov.uk/public/weather/climate)). The different columns are: # # 1. Month # 2. Max. temp(°C) # 3. Min. temp (°C) # 4. Days of air frost (days) # 5. Sunshine (hours) # 6. Rainfall (mm) # 7. Days of rainfall >= 1 mm (days) # 8. Monthly mean wind speed at 10m (knots) # # Try the following things: # #
# For one station, store the monthly temperatures (or other variable as a list) #
# **HINT** You can loop over the lines by splitting each string by `\n`, and can then further split each line by `\t` tab stops. Remember that you want to have temperatures as numbers, so you'll have to convert the string of interest! #
#
# #
# For one station, calculate the total number of sunshine hours in a year #
# #
# For one station, calculate the average annual maximum and minimum temperatures #
# In[29]: hull_txt = """Jan 7.3 1.9 7.8 54.8 55.2 11.8 n/a Feb 7.9 1.8 8.0 76.3 44.1 9.3 n/a Mar 10.5 3.3 3.8 110.6 49.0 11.1 n/a Apr 12.9 4.8 1.6 151.2 50.9 10.0 n/a May 16.1 7.5 0.1 195.4 49.8 9.3 n/a Jun 19.1 10.4 0.0 177.1 66.5 9.5 n/a Jul 21.6 12.7 0.0 193.8 56.1 9.0 n/a Aug 21.5 12.5 0.0 181.1 60.7 9.1 n/a Sep 18.6 10.5 0.0 145.1 61.0 9.1 n/a Oct 14.5 7.7 0.5 111.7 61.2 10.8 n/a Nov 10.3 4.5 3.0 65.4 62.9 12.3 n/a Dec 7.6 2.2 7.7 50.5 62.5 11.7 n/a""" sheffield_txt = """Jan 6.8 1.9 8.0 45.2 83.4 13.4 n/a Feb 7.1 1.7 9.0 68.3 60.4 10.5 n/a Mar 9.8 3.3 3.9 111.9 63.4 12.3 n/a Apr 12.5 4.8 1.4 144.0 65.5 10.3 n/a May 16.1 7.5 0.0 190.9 53.8 9.6 n/a Jun 18.8 10.5 0.0 179.5 75.6 9.1 n/a Jul 21.1 12.7 0.0 199.5 56.0 9.2 n/a Aug 20.6 12.4 0.0 185.0 65.3 9.9 n/a Sep 17.7 10.3 0.0 136.2 63.8 8.9 n/a Oct 13.5 7.5 0.5 90.7 81.2 12.7 n/a Nov 9.5 4.5 3.1 53.7 79.4 12.6 n/a Dec 6.9 2.3 7.9 40.0 86.7 13.0 n/a""" hampstead_txt = """Jan 7.1 2.0 8.6 57.5 64.7 12.0 n/a Feb 7.4 1.7 9.5 76.4 46.6 9.7 n/a Mar 10.5 3.5 4.0 107.1 48.9 10.2 n/a Apr 13.3 5.0 1.5 151.6 51.5 9.9 n/a May 16.8 8.0 0.1 192.2 58.0 9.5 n/a Jun 19.9 10.9 0.0 191.0 54.2 9.0 n/a Jul 22.4 13.2 0.0 199.9 50.4 8.5 n/a Aug 22.0 13.1 0.0 193.0 64.4 8.9 n/a Sep 18.8 11.0 0.0 140.8 56.9 8.8 n/a Oct 14.6 8.1 0.3 109.9 77.7 11.0 n/a Nov 10.3 4.8 2.9 69.4 68.3 11.4 n/a Dec 7.4 2.5 7.7 51.6 62.9 11.4 n/a""" # ### `if/elif/else` conditionals # # A conditional `if` loop allows for different actions to be taken when a situation is true. For example, you may want to check that a number is positive before you do something with it. An `if` statement allows you to have a block of instructions that are executed when the experssion in the `if` statement evaluates to `True`. For example, if we just want to report months where the temperature in Hampstead is greater than 15 degrees... # In[35]: for imonth, month in enumerate(month_name): if climatology["Hampstead"][imonth] >= 15: print month, "\t", climatology[station][imonth] # `if` statements can evaluate multiple conditions, each with a different set of actions. We can add additional tests with the `elif` statement, and you should always also have an `else` statement, which is triggered if neither the `if` or any other `elif` statements have been true. # In[36]: for imonth, month in enumerate(month_name): if climatology["Hampstead"][imonth] >= 20: print month, "\t", climatology[station][imonth], "->", "Shorts" elif climatology["Hampstead"][imonth] >= 12: print month, "\t", climatology[station][imonth], "->", "Jumper" else: print month, "\t", climatology[station][imonth], "->", "Brrrr.." # Also note the order of how the different tests are done: first, we check the first `if` statement. If this evaluates to true (in this case, temperature is more than 20 degrees), this code block is executed (prints `Shorts`). If this isn't true, then we test the first `elif` statement, and if true, it will take action (print `Jumper`). If it isn't true, then it goes to the `else` statement, and once the code is here, it will get executed (print `Brrrr`). # ## Files and remote streams # # ### Writing a text file # # In Python, working with files is quite straightforward: you open a file (either for reading or writing), you read from it (or write to it), and then close it. Let's just write our climatology to a file as a [CSV file](https://en.wikipedia.org/wiki/Comma-separated_values): # In[41]: fp = open ("climatology.txt", 'w') # Open the file for WRITING! fp.write("#Month,Hull,Sheffield,Hampstead\n") # Write file header for imonth, month in enumerate(month_name): fp.write (month) # Write month name to file for station in ["Hull", "Sheffield", "Hampstead"]: fp.write(",{}".format(climatology[station][imonth])) # Write "," and temperature for current month and station fp.write("\n") # Add new line fp.close() # close file # So we do the following: # # 1. Open the file for writing (`w` option) # 2. Write a string (terminated with a new line `\n` character) with the header of the file # 3. For each month, write the month name, as well as the temperature of every station # 4. Once all months have gone, close the file # # We can check the file contents by using the `cat` command in the UNIX shell (you can get it through the notebook by prepending it with the `!` symbol): # # In[42]: get_ipython().system('cat climatology.txt') # Here, we have used [string format templates](https://pyformat.info/) (you can also use older-fashioned [C style templates](https://www.learnpython.org/en/String_Formatting)) to substitute parts of a string. Basically, `",{}".format(14.9)` will substitute the floating point 14.9 in the space occupied by the curly braces. You can also specify what format you want the number in. For example, if we want to have zero padding and 5 decimal places, we could have written `",{:08.5f}".format(14.9)` # In[44]: print ",{:08.5f}".format(14.9) # ### Reading a text file # # Once a file is opened for reading, you can usually read it line by line. Each line will be string, and string methods will be of great use here. So, to open the file we saved previously, and print the months and temperature in Sheffield **only** when it's over 18 degrees, we would: # # In[45]: with open("climatology.txt", 'r') as fp: for line in fp: x = line.split(",") month = x[0] temp = float(x[2]) if temp > 18: print month, temp # The `with` statement is a context manager. Basically, it will open the file, and if succesful, it will do whatever is within the `with` indented block. At the end, it will automatically close the file. As it is, it's a much safer way of dealing with files than presented above, as the contents of the file are not guaranteed to be in present in the file unless the file has been properly closed. # # ### Accessing online resources # # We will briefly see how to access a text file on the internet (remember that the vast majority of content on the web can be accessed as a text file!). To do this, we will need to **import** a Python module. Python modules provide extra capacities, and allow you to do more stuff. A Python module has been written by someone and is usually read to use. You could (and should) use these modules in your code. Python comes with a [ton of modules by default](https://docs.python.org/2/library/index.html). # # Here, we'll use a module that isn't part of the default Python library, but that it's installed on the system here at UCL: the [`requests`](http://docs.python-requests.org/en/master/) module. We will demonstrate how to get a text file with El Niño/Southern Oscillation (ENSO) anomalies from NOAA (you can find [more information here](https://www.esrl.noaa.gov/psd/enso/mei)). The data are available under the following URL: [https://www.esrl.noaa.gov/psd/enso/mei/table.html](https://www.esrl.noaa.gov/psd/enso/mei/table.html). We will just # 1. Get the remote data # 2. Check that it worked # 3. Print a few lines of data (the values between years 2005 and 2015, for example) # In[49]: url = "https://www.esrl.noaa.gov/psd/enso/mei/table.html" import requests r = requests.get(url) if r.ok: for i, line in enumerate(r.text.split("\n")): if 68 < i < 80: print line # ## Some fun exercises # # [Transport for London (TfL)](http://www.tfl.gov.uk/) publishes most of its data using an [API](https://blog.tfl.gov.uk/2015/10/01/tfl-unified-api-part-1-introduction/). Basically, you can query a URL (maybe even passing some parameters), and the TfL server will send you some information. For example, to get the forecast on air quality, the URL is [https://api.tfl.gov.uk/AirQuality](https://api.tfl.gov.uk/AirQuality). If you click on that link, you will see that it returns a text file with some interesting format: a [JSON file](http://www.json.org/). However, we can treat it as a text file, and we can try to get the forecast for tomorrow, by noting how the file structure works. # # * There are two lines: current conditions and forecast # * The forecast appears after the tag `forecastText` # * We note that the carriage returns are encoded as `"<br/>` # # A simple snippet that will work is this one # In[50]: import requests r = requests.get ("https://api.tfl.gov.uk/AirQuality") for i,line in enumerate(r.text.split("}")): if i > 0: iloc = line.find ('"forecastText":') the_text = line[(iloc+len('"forecastText":')):] the_text = the_text.replace("<br/>", "\n") print the_text # However, if we import the [`json`](https://pymotw.com/2/json/) module from the Python standard library, we can access all the information as a simple Python dictionary. The structure looks like this: # # ```json # { # "$id": "1", # "$type": "Tfl.Api.Presentation.Entities.LondonAirForecast, Tfl.Api.Presentation.Entities", # "currentForecast": [ # { # "$id": "2", # "$type": "Tfl.Api.Presentation.Entities.CurrentForecast, Tfl.Api.Presentation.Entities", # "forecastBand": "Low", # "forecastID": "12771", # "forecastSummary": "Low air pollution forecast valid from Tuesday 10 October to end of Tuesday 10 October GMT", # "forecastText": "A largely cloudy and relatively mild day for Tuesday as a weak Atlantic weather front moves across south-east England. Increasingly breezy conditions should give good dispersal of locally-emitted pollutants while the Atlantic air feed will carry very little by way of imported pollution.<br/><br/>Air pollution is expected to remain 'Low' for the following pollutants throughout the forecast period:<br/><br/>Nitrogen Dioxide<br/>Ozone<br/>PM10 Particulates<br/>PM2.5 Particulates<br/>Sulphur Dioxide<br/><br/><br/><br/>", # "forecastType": "Current", # "fromDate": "2017-10-10T00:00:00Z", # "nO2Band": "Low", # "o3Band": "Low", # "pM10Band": "Low", # "pM25Band": "Low", # "publishedDate": "2017-09-10T11:58:00Z", # "sO2Band": "Low", # "toDate": "2017-10-10T23:59:00Z" # }, # { # "$id": "3", # "$type": "Tfl.Api.Presentation.Entities.CurrentForecast, Tfl.Api.Presentation.Entities", # "forecastBand": "Low", # "forecastID": "12772", # "forecastSummary": "Low air pollution forecast valid from Wednesday 11 October to end of Wednesday 11 October GMT", # "forecastText": "Wednesday is expected to be mainly dry, but showers are forecast during the afternoon and evening. <br/><br/>Similarly breezy conditions to Tuesday should give good dispersal of locally-emitted pollutants while the continued Atlantic air feed will carry very little by way of imported pollution. <br/><br/>Air pollution is expected to remain 'Low' for the following pollutants throughout the forecast period:<br/><br/>Nitrogen Dioxide<br/>Ozone<br/>PM10 Particulates<br/>PM2.5 Particulates<br/>Sulphur Dioxide<br/><br/><br/><br/><br/><br/>", # "forecastType": "Future", # "fromDate": "2017-11-10T00:00:00Z", # "nO2Band": "Low", # "o3Band": "Low", # "pM10Band": "Low", # "pM25Band": "Low", # "publishedDate": "2017-10-10T09:25:00Z", # "sO2Band": "Low", # "toDate": "2017-11-10T23:59:00Z" # } # ], # "disclaimerText": "This forecast is intended to provide information on expected pollution levels in areas of significant public exposure. It may not apply in very specific locations close to unusually strong or short-lived local sources of pollution.", # "forecastURL": "http://londonair.org.uk/forecast", # "updateFrequency": "1", # "updatePeriod": "hourly" # }``` # # We see that we have both a current and a future forecast, and that these appear to be under the key `currentForecast` stored as a list. We can replicate our previous work as: # In[63]: import json r = requests.get ("https://api.tfl.gov.uk/AirQuality") if r.ok: air_quality = json.loads(r.text) print type(air_quality) print air_quality.keys() print air_quality["currentForecast"][1]["forecastText"].replace("<br/>", "\n") # ### Exploring public transport outside Gower Street # # * You should be able to find out what (and when) buses will arrive at the bus stop outside UCL in Gower Street (the bus stop code is `490013914N`). Some context [here](https://blog.tfl.gov.uk/2015/12/07/unified-api-part-5-aot-arrivals-of-things/), but the URL is broadly `https://api.tfl.gov.uk/StopPoint/490013914N/arrivals`. # * TfL has information on all bike docking stations. You can get the entire status from this URL: [`https://api.tfl.gov.uk/bikepoint`](https://api.tfl.gov.uk/bikepoint). You should be able to find the one near to Gower Place, and find out how many free bikes are present # In[ ]: