|
13 | 13 | \newcommand{\thetitle}{Modeling and Simulation in Python} |
14 | 14 | \newcommand{\thesubtitle}{} |
15 | 15 | \newcommand{\theauthors}{Allen B. Downey} |
16 | | -\newcommand{\theversion}{1.0} |
| 16 | +\newcommand{\theversion}{1.0.1} |
17 | 17 |
|
18 | 18 |
|
19 | 19 | %%%% Both LATEX and PLASTEX |
@@ -1417,9 +1417,10 @@ \section{Comparison operators} |
1417 | 1417 | Not equal & \py{!=} \\ |
1418 | 1418 | \end{tabular} |
1419 | 1419 |
|
1420 | | -The equals operator, \py{==}, compares two values and returns \py{True} if they are equal and \py{False} otherwise. It is easy to confuse with the {\bf assignment} operator, \py{=}, which assigns a value to a variable. For example, the following statement uses the assignment operator, which creates \py{x} if it doesn't already exist and gives it the value \py{5} |
| 1420 | +The equals operator, \py{==}, compares two values and returns \py{True} if they are equal and \py{False} otherwise. It is easy to confuse with the {\bf assignment operator}, \py{=}, which assigns a value to a variable. For example, the following statement uses the assignment operator, which creates \py{x} if it doesn't already exist and gives it the value \py{5} |
1421 | 1421 | \index{equality} |
1422 | | -\index{assignment} |
| 1422 | +\index{assignment operator} |
| 1423 | +\index{operator!assignment} |
1423 | 1424 |
|
1424 | 1425 | \begin{python} |
1425 | 1426 | x = 5 |
@@ -1486,7 +1487,7 @@ \section{Metrics} |
1486 | 1487 |
|
1487 | 1488 | \begin{python} |
1488 | 1489 | bikeshare = System(olin=10, wellesley=2, |
1489 | | - olin_empty=0, wellesley_empty=0) |
| 1490 | + olin_empty=0, wellesley_empty=0) |
1490 | 1491 | \end{python} |
1491 | 1492 |
|
1492 | 1493 | Now we can run a simulation like this: |
@@ -1756,7 +1757,7 @@ \section{World population data} |
1756 | 1757 | \index{Series} |
1757 | 1758 | \index{DataFrame} |
1758 | 1759 |
|
1759 | | -The Pandas function we'll use is \py{read_html}, which can read a web page and extract data from any tables it contains. Before we can use it, we have to {\bf import} it. You have already seen this import statement: |
| 1760 | +The Pandas function we'll use is \py{read_html}, which can read a web page and extract data from any tables it contains. Before we can use it, we have to import it. You have already seen this import statement: |
1760 | 1761 | \index{\py{read_html}} |
1761 | 1762 | \index{import statement} |
1762 | 1763 | \index{statement!import} |
@@ -2039,6 +2040,8 @@ \section{Now with System objects} |
2039 | 2040 | \index{plot} |
2040 | 2041 | \index{decorate} |
2041 | 2042 |
|
| 2043 | +%TODO: We actually use decorate in chap01.ipynb, so move this earlier. |
| 2044 | + |
2042 | 2045 | Finally, we can run it like this. |
2043 | 2046 |
|
2044 | 2047 | \begin{python} |
@@ -2286,6 +2289,101 @@ \section{Equilibrium} |
2286 | 2289 | In the next chapter we use the models we have developed to generate predictions. |
2287 | 2290 |
|
2288 | 2291 |
|
| 2292 | +\section{Disfunctions} |
| 2293 | + |
| 2294 | +When people first learn about functions, there are a few things they often find confusing. In this section I present and explain some common problems with functions. |
| 2295 | + |
| 2296 | +As an example, suppose you want a function that takes a \py{System} object with variables \py{alpha} and \py{beta} as a parameter and computes the carrying capacity, \py{-alpha/beta}. Here's a good solution: |
| 2297 | + |
| 2298 | +\begin{python} |
| 2299 | +def carrying_capacity(system): |
| 2300 | + K = -system.alpha / system.beta |
| 2301 | + return K |
| 2302 | + |
| 2303 | +sys1 = System(alpha=0.025, beta=-0.0018) |
| 2304 | +pop = carrying_capacity(sys1) |
| 2305 | +print(pop) |
| 2306 | +\end{python} |
| 2307 | + |
| 2308 | +Now let's see all the ways that can go wrong. |
| 2309 | + |
| 2310 | +Disfunction \#1: Not using parameters. In the following version, the function doesn't take any parameters; when \py{system} appears inside the function, it refers to the object we created outside the function. |
| 2311 | + |
| 2312 | +\begin{python} |
| 2313 | +# WRONG |
| 2314 | +def carrying_capacity(): |
| 2315 | + K = -system.alpha / system.beta |
| 2316 | + return K |
| 2317 | + |
| 2318 | +system = System(alpha=0.025, beta=-0.0018) |
| 2319 | +pop = carrying_capacity() |
| 2320 | +print(pop) |
| 2321 | +\end{python} |
| 2322 | + |
| 2323 | +This version actually works, but it is not very versatile. If there are several \py{System} object, this function can only work with one of them; that is, as long as one of them is named \py{System}. |
| 2324 | + |
| 2325 | +Disfunction \#2: Clobbering the parameters. When people first learn about parameters, they often write functions like this: |
| 2326 | + |
| 2327 | +\begin{python} |
| 2328 | +# WRONG |
| 2329 | +def carrying_capacity(system): |
| 2330 | + system = System(alpha=0.025, beta=-0.0018) |
| 2331 | + K = -system.alpha / system.beta |
| 2332 | + return K |
| 2333 | + |
| 2334 | +sys1 = System(alpha=0.025, beta=-0.0018) |
| 2335 | +pop = carrying_capacity(sys1) |
| 2336 | +print(pop) |
| 2337 | +\end{python} |
| 2338 | + |
| 2339 | +In this example, we have a \py{System} object named \py{sys1} that gets passed as an argument to \py{carrying_capacity}. But when the function run, it ignores the argument and immediately replaces it with a new \py{System} object. As a result, this function always returns the same value, no matter what argument is passed. |
| 2340 | + |
| 2341 | +When you write a function, you generally don't know what the values of the parameters will be. Your job is to write a function that works for any valid values. If you assign your own values to the parameters, you defeat the whole purpose of functions. |
| 2342 | + |
| 2343 | + |
| 2344 | +Disfunction \#3: No return value. Here's a version that computes the value of \py{K} but doesn't return it. |
| 2345 | + |
| 2346 | +\begin{python} |
| 2347 | +# WRONG |
| 2348 | +def carrying_capacity(system): |
| 2349 | + K = -system.alpha / system.beta |
| 2350 | + |
| 2351 | +sys1 = System(alpha=0.025, beta=-0.0018) |
| 2352 | +pop = carrying_capacity(sys1) |
| 2353 | +print(pop) |
| 2354 | +\end{python} |
| 2355 | + |
| 2356 | +A function that doesn't have a return statement always returns \py{None}, so in this example the value of \py{pop} is \py{None}. |
| 2357 | + |
| 2358 | + |
| 2359 | +Disfunction \#4: Ignoring the return value. Finally, here's a version where the function is correct, but the way it's used is not. |
| 2360 | + |
| 2361 | +\begin{python} |
| 2362 | +# WRONG |
| 2363 | +def carrying_capacity(system): |
| 2364 | + K = -system.alpha / system.beta |
| 2365 | + return K |
| 2366 | + |
| 2367 | +sys1 = System(alpha=0.025, beta=-0.0018) |
| 2368 | +carrying_capacity(sys1) |
| 2369 | +print(K) |
| 2370 | +\end{python} |
| 2371 | + |
| 2372 | +If you call a function that returns a value, you should do something with the return value. Often you assign it to a variable, as in the previous examples, but you can also use it as part of an expression. For example, you could eliminate the temporary variable \py{pop} like this: |
| 2373 | + |
| 2374 | +\begin{python} |
| 2375 | +print(carrying_capacity(sys1)) |
| 2376 | +\end{python} |
| 2377 | + |
| 2378 | +Or if you had more than one system, you could compute the total carrying capacity like this: |
| 2379 | + |
| 2380 | +\begin{python} |
| 2381 | +total = carrying_capacity(sys1) + carrying_capacity(sys2) |
| 2382 | +\end{python} |
| 2383 | + |
| 2384 | +In the notebook for this chapter, you can try out each of these examples and see what happens. |
| 2385 | + |
| 2386 | + |
2289 | 2387 | \chapter{Prediction} |
2290 | 2388 |
|
2291 | 2389 | In the previous chapter we developed a quadratic model of world population growth from 1950 to 2015. It is a simple model, but it fits the data well and the mechanisms it's based on are plausible. |
@@ -2689,7 +2787,7 @@ \section{Differential equations in SymPy} |
2689 | 2787 | % |
2690 | 2788 | \[ f{\left (t \right )} = C_{1} \exp(\alpha t) \] |
2691 | 2789 | % |
2692 | | -This is the general solution, which still contains an unspecified constant, $C_1$. To get the particular solution where $f(0) = p_0$, we substitute \py{p0} for \py{C1}. First, we have to create two more symbols: |
| 2790 | +This is the {\bf general solution}, which still contains an unspecified constant, $C_1$. To get the {\bf particular solution} where $f(0) = p_0$, we substitute \py{p0} for \py{C1}. First, we have to create two more symbols: |
2693 | 2791 | \index{general solution} |
2694 | 2792 | \index{particular solution} |
2695 | 2793 |
|
@@ -5736,6 +5834,8 @@ \section{Finishing off the problem} |
5736 | 5834 |
|
5737 | 5835 | The notebook provides some additional hints, but at this point you should have everything you need. Good luck! |
5738 | 5836 |
|
| 5837 | +If you enjoy this exercise, you might be interested in this paper: ``How to hit home runs: Optimum baseball bat swing parameters for maximum range trajectories", by Sawicki, Hubbard, and Stronge, at \url{http://aapt.scitation.org/doi/abs/10.1119/1.1604384}. |
| 5838 | + |
5739 | 5839 |
|
5740 | 5840 | \chapter{Rotation} |
5741 | 5841 |
|
|
0 commit comments