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.
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 (dropdown says Markdown
)
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, 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.
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 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. 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 //
:
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**
17 17.0 5 5.0 5 2 3
We can also compare numbers:
print 5 > 4
print 5 == 5
print 5 < 4
print 3 <= 4
True True False True
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 tabulatorprint "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"
I'm a happy string I'm a happy string I'm a happy string I'm a happy string
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:
print "Hello " + "there" + " everyone"
print len("I'm a very happy string")
Hello there everyone 23
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:
print "I'm a very happy string".replace("happy", "unhappy")
print "I'm a very happy string".replace("very", "")
I'm a very unhappy string I'm a happy string
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.
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")
11 11 -1
title
, upper
, lower
¶We can change the case of our string using these methods:
print "hello there".upper()
HELLO THERE
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
:
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()
True True False True False True
We can count the number of times a character or substring appears in a given string using count
:
print "Something or other".count("z")
print "Something or Other".count("o")
print "Something or Other".lower().count("o")
0 2 3
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:
print int("5")
print float("-273.15")
5 -273.15
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:
a = 5
b = 6
print a * b
print a - b
string1 = "something"
prin
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]
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]
33 33 [-45.0, 33] [5, 67, -45.0] [1, 5, -45.0] [33, -45.0, 67, 5, 3, 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
):
a_string = "MCD15A2.A2005097.h17v03.005.2008007175837.hdf"
location = a_string.find(".hdf")
print a_string[:location]
MCD15A2.A2005097.h17v03.005.2008007175837
Lists have a plethora of methods 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:
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
3 42 None ['Hello', 'the answer', 'is', 42, '.'] Hello ['the answer', 'is', 42, '.']
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 !
:
a_string = "MCD15A2.A2005097.h17v03.005.2008007175837.hdf"
print a_string.split(".")
print "!".join(a_string.split("."))
['MCD15A2', 'A2005097', 'h17v03', '005', '2008007175837', 'hdf'] MCD15A2!A2005097!h17v03!005!2008007175837!hdf
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.
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:
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]
Hull, June 16.1 Sheffield, June 16.1 Hampstead, June 16.8
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:
climatology = {'Hull': Hull,
'Sheffield': Sheffield,
'Hampstead': Hampstead}
print climatology['Hull'][4]
print climatology['Sheffield'][4]
print climatology['Hampstead'][4]
16.1 16.1 16.8
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?
print "A string" + 5.0
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-48-5f0f4e339251> in <module>() ----> 1 print "A string" + 5.0 TypeError: cannot concatenate 'str' and 'float' objects
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...
a_list = range(10)
print len(a_list)
print a_list[30]
10
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-50-6be43a7981e9> in <module>() 1 a_list = range(10) 2 print len(a_list) ----> 3 print a_list[30] IndexError: list index out of range
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:
a = 3/5
b = 10
print b/a
--------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) <ipython-input-54-070d61191d35> in <module>() 1 a = 3/5 2 b = 10 ----> 3 print b/a ZeroDivisionError: integer division or modulo by zero
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:
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"
>>> At the start of the loop 10.5 9.8 10.5 >>> 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:
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
Jan 7.3 6.8 7.1 Feb 7.9 7.1 7.4 Mar 10.5 9.8 10.5 Apr 12.9 12.5 13.3 May 16.1 16.1 16.8 Jun 19.1 18.8 19.9 Jul 21.6 21.1 22.4 Aug 21.5 20.6 22.0 Sep 18.6 17.7 18.8 Oct 14.5 13.5 14.6 Nov 10.3 9.5 10.3 Dec 7.6 6.9 7.4
The following snippet has the climatology for the different stations above as multi-line strings (data comes from the Met Office). The different columns are:
Try the following things:
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...
for imonth, month in enumerate(month_name):
if climatology["Hampstead"][imonth] >= 15:
print month, "\t", climatology[station][imonth]
May 16.8 Jun 19.9 Jul 22.4 Aug 22.0 Sep 18.8
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.
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.."
Jan 7.1 -> Brrrr.. Feb 7.4 -> Brrrr.. Mar 10.5 -> Brrrr.. Apr 13.3 -> Jumper May 16.8 -> Jumper Jun 19.9 -> Jumper Jul 22.4 -> Shorts Aug 22.0 -> Shorts Sep 18.8 -> Jumper Oct 14.6 -> Jumper Nov 10.3 -> Brrrr.. Dec 7.4 -> 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
).
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:
w
option)\n
character) with the header of the fileWe 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):
!cat climatology.txt
Jan,7.3,6.8,7.1 Feb,7.9,7.1,7.4 Mar,10.5,9.8,10.5 Apr,12.9,12.5,13.3 May,16.1,16.1,16.8 Jun,19.1,18.8,19.9 Jul,21.6,21.1,22.4 Aug,21.5,20.6,22.0 Sep,18.6,17.7,18.8 Oct,14.5,13.5,14.6 Nov,10.3,9.5,10.3 Dec,7.6,6.9,7.4
Here, we have used string format templates (you can also use older-fashioned C style templates) 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)
print ",{:08.5f}".format(14.9)
,14.90000
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:
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
Jun 18.8 Jul 21.1 Aug 20.6
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.
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.
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
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). The data are available under the following URL: https://www.esrl.noaa.gov/psd/enso/mei/table.html. We will just
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
2005 .32 .81 1.067 .637 .893 .612 .519 .362 .329 -.167 -.392 -.57 2006 -.438 -.424 -.527 -.575 .043 .546 .718 .77 .84 .955 1.286 .951 2007 .985 .528 .12 .02 .354 -.136 -.247 -.427 -1.175 -1.217 -1.165 -1.193 2008 -1.02 -1.388 -1.579 -.879 -.349 .164 .089 -.252 -.545 -.692 -.597 -.663 2009 -.726 -.707 -.723 -.105 .328 .779 1.06 1.073 .745 .909 1.121 1.045 2010 1.067 1.52 1.469 .99 .668 -.211 -1.099 -1.662 -1.86 -1.899 -1.49 -1.577 2011 -1.739 -1.563 -1.575 -1.399 -.202 .016 -.191 -.502 -.751 -.933 -.949 -.957 2012 -.993 -.695 -.398 .112 .769 .866 1.128 .628 .351 .081 .125 .094 2013 .096 -.08 -.037 .095 .203 -.078 -.311 -.466 -.125 .13 -.053 -.248 2014 -.275 -.266 .027 .312 1.01 1.057 .921 .961 .593 .438 .763 .558 2015 .42 .459 .631 .943 1.592 2.101 1.987 2.365 2.532 2.241 2.297 2.112
Transport for London (TfL) publishes most of its data using an API. 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. If you click on that link, you will see that it returns a text file with some interesting format: a JSON file. 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.
forecastText
"<br/>
A simple snippet that will work is this one
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
"Wednesday is expected to be mainly dry, but showers are forecast during the afternoon and evening. 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. Air pollution is expected to remain 'Low' for the following pollutants throughout the forecast period: Nitrogen Dioxide Ozone PM10 Particulates PM2.5 Particulates Sulphur Dioxide "
However, if we import the json
module from the Python standard library, we can access all the information as a simple Python dictionary. The structure looks like this:
{
"$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:
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")
<type 'dict'> [u'updateFrequency', u'updatePeriod', u'disclaimerText', u'$id', u'$type', u'currentForecast', u'forecastURL'] Wednesday is expected to be mainly dry, but showers are forecast during the afternoon and evening. 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. Air pollution is expected to remain 'Low' for the following pollutants throughout the forecast period: Nitrogen Dioxide Ozone PM10 Particulates PM2.5 Particulates Sulphur Dioxide
490013914N
). Some context here, but the URL is broadly https://api.tfl.gov.uk/StopPoint/490013914N/arrivals
.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