Problems: Python Basics#
Get this problem set
Download everything (Topic1.1-Python_Basics_Problems.zip) — the notebook and
water_properties.csv, in a folder that is ready to run as-is.
The Download badge at the top of the page will also give you the notebook on its own. On Vocareum everything is already set up for you.
This is the first problem set of the course, so it does two jobs: it gets you started with Python and data files, and it shows you how every later problem set will be arranged.
How these problem sets work
Every graded set is worth 100 points and has the same three parts.
Part A — Skill Checks (30 pts). Short questions with one right answer. You assign your answer to a named variable, then run a check cell that tells you immediately whether it is accepted. You may resubmit as often as you like, so there is no reason to hand in a Part A that does not pass. The check cells look like this:
check("q1", n_rows)
Part B — Visualization (35 pts). Make a plot and say what it shows. Graded by your peers against a rubric, so there is usually more than one acceptable answer — but the rubric is specific about what has to be there.
Part C — Open Ended (35 pts). A question with no single right answer, where you are graded on the reasoning and on whether your conclusions follow from what you actually computed. Also peer graded.
The parts build on each other. Part A works out the syntax you need for Part B, and Part B produces the evidence you argue from in Part C, so do them in order.
Cells marked # YOUR CODE HERE are yours to fill in. Everything else is provided and
should run as-is.
One convention worth knowing before you start: a line like n_rows = ... is a placeholder.
The three dots are real Python — an object called Ellipsis that does nothing — and they
mark the spot where your answer goes. Replace the ..., do not add to it. If you run the
check cell without replacing it, it will tell you the question is unanswered rather
than marking it wrong.
Setup#
The dataset is data/water_properties.csv: four thermophysical properties of liquid
water, tabulated every 2 °C from the freezing point to the boiling point at 1 bar. It
comes from the NIST Chemistry WebBook, which serves the IAPWS-95 formulation — the
international standard for the properties of water.
Water is worth starting with because you already know roughly how it behaves, so when the code gives you a number you can tell whether it is sensible. That is a habit worth forming now: the first question to ask of any computed result is whether it could possibly be right.
%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
try:
plt.style.use('../settings/plot_style.mplstyle') # available inside the book
except OSError:
pass # downloaded notebook: use defaults
df = pd.read_csv('data/water_properties.csv')
df.head(10)
| temperature [C] | density [kg/m^3] | Cp [J/g/K] | viscosity [Pa*s] | thermal conductivity [W/m/K] | |
|---|---|---|---|---|---|
| 0 | 0.01 | 999.84 | 4.2194 | 0.001791 | 0.55567 |
| 1 | 2.01 | 999.94 | 4.2130 | 0.001673 | 0.56069 |
| 2 | 4.01 | 999.97 | 4.2075 | 0.001567 | 0.56549 |
| 3 | 6.01 | 999.94 | 4.2027 | 0.001471 | 0.57010 |
| 4 | 8.01 | 999.85 | 4.1987 | 0.001384 | 0.57453 |
| 5 | 10.01 | 999.70 | 4.1951 | 0.001306 | 0.57880 |
| 6 | 12.01 | 999.50 | 4.1921 | 0.001234 | 0.58291 |
| 7 | 14.01 | 999.25 | 4.1896 | 0.001168 | 0.58688 |
| 8 | 16.01 | 998.94 | 4.1874 | 0.001108 | 0.59072 |
| 9 | 18.01 | 998.60 | 4.1856 | 0.001052 | 0.59444 |
import numpy as np loads a library and gives it a short nickname, so every function from
it is written np.something — this is why NumPy calls all look alike. pd.read_csv reads
a comma-separated file into a DataFrame, a table that remembers its column names.
df.head(10) shows the first ten rows, which is always worth doing before anything else:
it is how you find out that the file is what you thought it was.
matplotlib.pyplot is the plotting library, imported as plt; you will need it in Part B.
The try/except around the style sheet is there because that file exists inside the book
but not next to a notebook you have downloaded — this way the plots simply fall back to
matplotlib’s defaults instead of raising an error.
As in the chapter, .values converts the table into a NumPy array, and columns are
selected by slicing:
X = df.values
print(X.shape)
print(df.columns.values) # which column is which
(51, 5)
['temperature [C]' 'density [kg/m^3]' 'Cp [J/g/K]' 'viscosity [Pa*s]'
'thermal conductivity [W/m/K]']
The five columns are, in order: temperature (°C), density (kg/m³), heat capacity \(C_p\) (J/g/K), viscosity (Pa·s), and thermal conductivity (W/m/K).
Reading NumPy indexing
An array is indexed X[row, column], in that order, and counting starts at zero. So
the first column is X[:, 0] and the second is X[:, 1] — which is why density, the
second column in the file, is index 1. Off-by-one errors here are the single most common
mistake in this problem set.
The colon means “everything along this axis”, so:
X[:, 1]is all rows, column 1 — a whole column.X[10, :]is row 10, all columns — a whole row.X[10, 3]is one single number, the value at row 10 and column 3.
Negative indices count backwards from the end, so X[-1, 0] is the last temperature in the
table without your needing to know how many rows there are.
To print a number readably, use an f-string: putting f in front of a string lets you drop
a variable into it inside braces, and :.4f after the name rounds it to four decimals.
print(f"mean density = {mean_density:.4f} kg/m^3")
How the self-check works, and when it can be wrong
check does not store the expected answer — it stores a hash of it, so the answers
are not sitting in this notebook. It rounds what you give it to a set number of decimals,
hashes that, and compares. Nothing is recorded anywhere; this copy is practice.
The consequence is that it can be wrong in one direction. A rounding boundary can make a
correct answer read as incorrect, and an answer very close to correct can read as
correct. If you are confident in an answer that check rejects, you are probably right —
move on, and please
open an issue so it can be fixed.
Run this once. It defines the check helper used after each Part A answer. It will
not run inside the book — use the downloaded notebook.
# Run this once. It defines `check`, used after each Part A answer below.
import hashlib
_EXPECTED = {
'q1': (5, ['5dae47c34366', 'c4424da473fa', '48b1f8b449c0', '6d1d493036d2', '783ff2033953', '08749b96adc6', 'd7cec2088c6f', '4a20cdc5ee47', '9411a8b4c718', '8f54f2b4595d', '0929d4c14afc', 'fb466e10cc2e', 'b46947b5dfa6']),
'q2': (2, ['320f0766641b', '0fd7ed66f37d', '493be04430c7', '7e8723eb895a', '4b12d8a537b1', '4f2650eaa302', 'dff2e51982c5', '969800b20539', '77a8418ea94a', 'e76a41f92846', 'f609cbc9b341', '6cb42271f6da', 'ef1e287df425', '3b36ee3af84f', '1a25b7f79324', 'ac6f7dd80434', 'c55387b17a6c', '5c94818b8773', '85c1e58a2fe1', '0b9997115471', '5f0275fd85c4']),
'q3': (4, ['af55b3313b07', '2bc6d1af03bc', '1df6ad6090ec', '869bcd209fd1', 'e022e51189d5']),
}
def check(qid, value):
"""Compare an answer against a stored digest. Practice only -- nothing is recorded."""
places, digests = _EXPECTED[qid]
if value is Ellipsis:
print(f"{qid}: not answered yet")
return
got = hashlib.sha256(f"{float(value):.{places}f}".encode()).hexdigest()[:12]
print(f"{qid}: {'correct' if got in digests else 'not the expected value'}")
Part A — Skill Checks (30 pts)#
Three questions, 10 points each. Each asks for a single number.
A1. Load the data and measure it (10 pts)#
Exercise 1
Using the array X created above, find how many rows the dataset has — that is, how many
temperatures are tabulated.
X.shape returns the array’s dimensions as (rows, columns), so the number of rows is
its first element.
Assign the number of rows to n_rows.
# YOUR CODE HERE
n_rows = ...
check("q1", n_rows)
A2. Average a column (10 pts)#
Exercise 2
Compute the mean density over all the tabulated temperatures, using np.mean.
Density is the second column, which is index 1, so X[:, 1] selects it — every row, column
one.
Assign the result to mean_density. The units are kg/m³.
# YOUR CODE HERE
mean_density = ...
check("q2", mean_density)
A3. Convert units and check against something you know (10 pts)#
Exercise 3
Viscosity is the fourth column (index 3), and NIST reports it in Pa·s. Chemical engineers more often quote the centipoise, where 1 Pa·s = 1000 cP.
Row index 10 of the table is 20.01 °C. Take the viscosity there and convert it to centipoise.
Assign the result to viscosity_cP.
# YOUR CODE HERE
viscosity_cP = ...
check("q3", viscosity_cP)
Part B — Visualization (35 pts)#
This is where matplotlib gets introduced properly. The chapter’s pattern is:
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(x, y, marker='o', ls='none')
ax.set_xlabel('...')
ax.set_ylabel('...')
plt.subplots makes two things at once and hands them back together: a figure (fig), the
canvas as a whole, and an axes (ax), the panel with the data on it. Everything after that
is a method — a function that belongs to ax and is called by writing a dot after it, in
the same way df.head() belonged to the DataFrame. So ax.plot draws on that panel and
ax.set_xlabel labels it, and if you had two panels each would take its own commands.
Axis limits work the same way, through ax.set_xlim and ax.set_ylim.
marker='o', ls='none' is how you get points with no connecting line. Arguments passed by
name like this can be given in any order, and any you leave out fall back to a default.
Exercise 4
Viscosity against temperature. Plot column 3 against column 0, with points rather than a connecting line. Label both axes, including units.
Thermal conductivity against temperature. The same again for column 4, as a second figure.
Zoom in. Redraw the viscosity plot restricted to 0–40 °C using
ax.set_xlim, and choose aax.set_ylimthat makes that range fill the axes.Read two numbers off your plots. For viscosity and for thermal conductivity, give the approximate factor by which each changes from 0 to 100 °C — highest value divided by lowest. Read them off the axes rather than computing them; Part C does the arithmetic. Then, in one sentence, say which of the two curves is closer to a straight line.
An unlabeled axis is the most common way to lose points on a plot, in this course and afterwards. Units belong on both axes of every figure you make from here on.
# YOUR CODE HERE
fig, ax = plt.subplots(figsize=(10, 5))
Part C — Open Ended (35 pts)#
This part asks you to write your first function and your first loop, so here is the shape of each.
A function is defined with def, takes whatever you list in the parentheses, and hands a
value back with return. The indented block underneath is the body — indentation is how
Python knows where a block starts and stops, so it is not optional. A string on the first
line of the body is the docstring, which is where you say what the function does.
def double(x):
"""Return twice x."""
return 2 * x
A for loop repeats the indented block once for each item in a list, with the loop
variable taking each value in turn. zip pairs up two lists so you can walk through both
at once — useful when each column index has a name you want to print alongside it.
for j, name in zip([1, 2], ['density', 'Cp']):
print(j, name)
Exercise 5
Part B suggested that water’s properties are not equally sensitive to temperature. Settle it for all four.
Write a function. Define a function that takes a column index and returns the fractional change in that column from the first row to the last:
\[ \text{fractional change} \;=\; \frac{v_\text{last} - v_\text{first}}{v_\text{first}} \]Give it a docstring saying what it does.
X[0, j]is the first row of columnjandX[-1, j]is the last.Apply it to all four property columns (1 through 4) and report the results as percentages. A
forloop over the column indices is the natural way;zipis useful if you want to pair each index with a name.Plot all four together. Divide each property column by its own value in the first row, so every curve starts at 1, and plot all four against temperature on one set of axes. Curves on one axes need a legend: pass
label='...'to eachax.plotcall and then callax.legend().Rank and compare. List the four properties in order of the absolute value of their fractional change, largest first, and report the ratio of the largest to the smallest. Then, in one or two sentences, pick one of these —
pumping the water through a pipe, where pressure drop rises with viscosity;
heating it in a heat exchanger, where the heat needed depends on \(C_p\);
and say whether treating that property as constant over 0–100 °C would be defensible, given the number you computed for it.
You are not expected to know heat exchanger design. A sensible argument from your own numbers is the whole requirement.
# YOUR CODE HERE
Additional Reading#
NIST Chemistry WebBook, Thermophysical Properties of Fluid Systems — the source of this dataset, and a useful reference throughout the course.
The matplotlib gallery — worked examples with source code, the fastest way to find out how to draw something.