diff --git a/environment.yml b/environment.yml index ba2a15a9..1c478e9e 100644 --- a/environment.yml +++ b/environment.yml @@ -2,25 +2,16 @@ name: quantecon channels: - default dependencies: - - python=3.11 - - anaconda=2024.02 + - python=3.13 + - anaconda=2025.06 - pip - pip: - - jupyter-book==0.15.1 - - docutils==0.17.1 - - quantecon-book-theme==0.7.1 + - jupyter-book==1.0.4post1 - sphinx-tojupyter==0.3.0 - sphinxext-rediraffe==0.2.7 - - sphinx-exercise==0.4.1 + - sphinx-reredirects==0.1.4 + - sphinx-exercise==1.0.1 + - sphinx-proof==0.2.0 - ghp-import==1.1.0 - - sphinxcontrib-youtube==1.1.0 - - sphinx-togglebutton==0.3.1 - # Github Actions Bug - - requests - # Docker Requirements - - pytz - # Additional Requirements - - array-to-latex - - prettytable - - + - sphinxcontrib-youtube==1.3.0 #Version 1.3.0 is required as quantecon-book-theme is only compatible with sphinx<=5 + - sphinx-togglebutton==0.3.2 diff --git a/lectures/_config.yml b/lectures/_config.yml index c5784554..66d01e8c 100644 --- a/lectures/_config.yml +++ b/lectures/_config.yml @@ -1,21 +1,21 @@ title: Python Programming for Economics and Finance author: Thomas J. Sargent & John Stachurski logo: _static/qe-logo.png -description: This website presents a set of lectures on python programming for economics, designed and written by Thomas J. Sargent and John Stachurski. +description: This website presents a set of lectures on python programming for economics. execute: execute_notebooks: "cache" timeout: 600 # 10 minutes html: - baseurl: https://python.quantecon.org/ + baseurl: https://python-programming.quantecon.org/ latex: latex_documents: targetname: quantecon-python-programming.tex sphinx: - extra_extensions: [sphinx_multitoc_numbering, sphinxext.rediraffe, sphinx_tojupyter, sphinx_exercise, sphinx_togglebutton] + extra_extensions: [sphinx_multitoc_numbering, sphinxext.rediraffe, sphinx_exercise, sphinx_togglebutton] config: # bibtex_reference_style: author_year #TODO: enable if bibtex bibliography is used in series # false-positive links @@ -32,16 +32,18 @@ sphinx: - name: Thomas J. Sargent url: http://www.tomsargent.com/ - name: John Stachurski - url: https://johnstachurski.net/ - dark_logo: quantecon-logo-transparent.png + url: https://johnstachurski.net/ header_organisation_url: https://quantecon.org header_organisation: QuantEcon repository_url: https://github.com/QuantEcon/lecture-python-programming.myst nb_repository_url: https://github.com/QuantEcon/lecture-python-programming.notebooks + path_to_doc: lectures twitter: quantecon + dark_logo: qe-logo-dark.png twitter_logo_url: https://assets.quantecon.org/img/qe-twitter-logo.png og_logo_url: https://assets.quantecon.org/img/qe-og-logo.png - description: This website presents a set of lectures on python programming for economics, designed and written by Thomas J. Sargent and John Stachurski. + mainpage_author_fontsize: 22 + description: This website presents a set of lectures on python programming for economics. keywords: Python, QuantEcon, Quantitative Economics, Economics, Sloan, Alfred P. Sloan Foundation, Tom J. Sargent, John Stachurski analytics: google_analytics_id: G-X7DH1M2DPY @@ -49,19 +51,18 @@ sphinx: notebook_interface : classic # The interface interactive links will activate ["classic", "jupyterlab"] colab_url : https://colab.research.google.com thebe : false # Add a thebe button to pages (requires the repository to run on Binder) + languages: + - code: en + name: English + url: https://python-programming.quantecon.org + - code: fa + name: فارسی + url: https://quantecon.github.io/lecture-python-programming.fa + rtl: true + - code: zh-cn + name: 中文 + url: https://quantecon.github.io/lecture-python-programming.zh-cn + current_language: en mathjax_path: https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js rediraffe_redirects: index_toc.md: intro.md - tojupyter_static_file_path: ["source/_static", "_static"] - tojupyter_target_html: true - tojupyter_urlpath: "https://python-programming.quantecon.org/" - tojupyter_image_urlpath: "https://python-programming.quantecon.org/_static/" - tojupyter_lang_synonyms: ["ipython", "ipython3", "python"] - tojupyter_kernels: - python3: - kernelspec: - display_name: "Python" - language: python3 - name: python3 - file_extension: ".py" - tojupyter_images_markdown: true diff --git a/lectures/_static/qe-logo-dark.png b/lectures/_static/qe-logo-dark.png new file mode 100644 index 00000000..e9ead46c Binary files /dev/null and b/lectures/_static/qe-logo-dark.png differ diff --git a/lectures/_static/qe-logo-on-black.png b/lectures/_static/qe-logo-on-black.png new file mode 100644 index 00000000..e15bea9a Binary files /dev/null and b/lectures/_static/qe-logo-on-black.png differ diff --git a/lectures/_toc.yml b/lectures/_toc.yml index 302a0a0b..fd2e2321 100644 --- a/lectures/_toc.yml +++ b/lectures/_toc.yml @@ -35,6 +35,7 @@ parts: - file: writing_good_code - file: python_advanced_features - file: debugging + - file: cake_eating_numerical - caption: Other numbered: true chapters: diff --git a/lectures/cake_eating_numerical.md b/lectures/cake_eating_numerical.md new file mode 100644 index 00000000..c20faf09 --- /dev/null +++ b/lectures/cake_eating_numerical.md @@ -0,0 +1,610 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +(cake_eating_numerical)= +# Cake Eating II: Numerical Methods + +```{contents} Contents +:depth: 2 +``` + +## Overview + +In this lecture we continue the study of the cake eating problem. + +The aim of this lecture is to solve the problem using numerical +methods. + +At first this might appear unnecessary, since we already obtained the +optimal policy analytically. + +However, the cake eating problem is too simple to be useful without +modifications, and once we start modifying the problem, numerical +methods become essential. + +Hence it makes sense to introduce numerical methods now, and test them on this +simple problem. + +Since we know the analytical solution, this will allow us to assess the +accuracy of alternative numerical methods. + + +```{note} +The code below aims for clarity rather than maximum efficiency. + +In the lectures below we will explore best practice for speed and efficiency. + +Let's put these algorithm and code optimizations to one side for now. +``` + +We will use the following imports: + +```{code-cell} ipython +import matplotlib.pyplot as plt +import numpy as np +from scipy.optimize import minimize_scalar, bisect +from typing import NamedTuple +``` + +## Reviewing the Model + +Recall in particular that the Bellman equation is + +```{math} +:label: bellman-cen + +v(x) = \max_{0\leq c \leq x} \{u(c) + \beta v(x-c)\} +\quad \text{for all } x \geq 0. +``` + +where $u$ is the CRRA utility function. + +The analytical solutions for the value function and optimal policy were found +to be as follows. + +```{code-cell} python3 +def c_star(x, β, γ): + return (1 - β ** (1/γ)) * x + + +def v_star(x, β, γ): + return (1 - β**(1 / γ))**(-γ) * (x**(1-γ) / (1-γ)) +``` + +Our first aim is to obtain these analytical solutions numerically. + +## Value Function Iteration + +The first approach we will take is **value function iteration**. + +This is a form of **successive approximation**. + +The basic idea is: + +1. Take an arbitrary initial guess of $v$. +1. Obtain an update $w$ defined by + + $$ + w(x) = \max_{0\leq c \leq x} \{u(c) + \beta v(x-c)\} + $$ + +1. Stop if $w$ is approximately equal to $v$, otherwise set $v=w$ and go back to step 2. + +Let's write this a bit more mathematically. + +### The Bellman Operator + +We introduce the **Bellman operator** $T$ that takes a function v as an +argument and returns a new function $Tv$ defined by + +$$ +Tv(x) = \max_{0 \leq c \leq x} \{u(c) + \beta v(x - c)\} +$$ + +From $v$ we get $Tv$, and applying $T$ to this yields +$T^2 v := T (Tv)$ and so on. + +This is called **iterating with the Bellman operator** from initial guess +$v$. + +As we discuss in more detail in later lectures, one can use Banach's +contraction mapping theorem to prove that the sequence of functions $T^n +v$ converges to the solution to the Bellman equation. + +### Fitted Value Function Iteration + +Both consumption $c$ and the state variable $x$ are continuous. + +This causes complications when it comes to numerical work. + +For example, we need to store each function $T^n v$ in order to compute the +next iterate $T^{n+1} v$. + +But this means we have to store $T^n v(x)$ at infinitely many $x$, which is, in general, impossible. + +To circumvent this issue we will use fitted value function iteration. + +The process looks like this: + +1. Begin with an array of values $\{ v_0, \ldots, v_I \}$ representing the + values of some initial function $v$ on the grid points $\{ x_0, \ldots, x_I \}$. +1. Build a function $\hat v$ on the state space $\mathbb R_+$ by linear + interpolation, based on these data points. +1. Obtain and record the value $T \hat v(x_i)$ on each grid point + $x_i$ by repeatedly solving the maximization problem in the Bellman + equation. +1. Unless some stopping condition is satisfied, set + $\{ v_0, \ldots, v_I \} = \{ T \hat v(x_0), \ldots, T \hat v(x_I) \}$ and go to step 2. + +In step 2 we'll use continuous piecewise linear interpolation. + +### Implementation + +The `maximize` function below is a small helper function that converts a +SciPy minimization routine into a maximization routine. + +```{code-cell} python3 +def maximize(g, a, b, args): + """ + Maximize the function g over the interval [a, b]. + + We use the fact that the maximizer of g on any interval is + also the minimizer of -g. The tuple args collects any extra + arguments to g. + + Returns the maximal value and the maximizer. + """ + + objective = lambda x: -g(x, *args) + result = minimize_scalar(objective, bounds=(a, b), method='bounded') + maximizer, maximum = result.x, -result.fun + return maximizer, maximum +``` + +We'll store the parameters $\beta$ and $\gamma$ and the grid in a +`NamedTuple` called `Model`. + +```{code-cell} python3 +# Create model data structure +class Model(NamedTuple): + β: float + γ: float + x_grid: np.ndarray + +def create_cake_eating_model(β=0.96, # discount factor + γ=1.5, # degree of relative risk aversion + x_grid_min=1e-3, # exclude zero for numerical stability + x_grid_max=2.5, # size of cake + x_grid_size=120): + """ + Creates an instance of the cake eating model. + """ + x_grid = np.linspace(x_grid_min, x_grid_max, x_grid_size) + return Model(β=β, γ=γ, x_grid=x_grid) +``` + +Now we define utility functions that operate on the model: + +```{code-cell} python3 +def u(c, γ): + """ + Utility function. + """ + if γ == 1: + return np.log(c) + else: + return (c ** (1 - γ)) / (1 - γ) + +def u_prime(c, γ): + """ + First derivative of utility function. + """ + return c ** (-γ) + +def state_action_value(c, x, v_array, model): + """ + Right hand side of the Bellman equation given x and c. + """ + β, γ, x_grid = model.β, model.γ, model.x_grid + v = lambda x: np.interp(x, x_grid, v_array) + + return u(c, γ) + β * v(x - c) +``` + +We now define the Bellman operation: + +```{code-cell} python3 +def T(v, model): + """ + The Bellman operator. Updates the guess of the value function. + + * model is an instance of Model + * v is an array representing a guess of the value function + + """ + v_new = np.empty_like(v) + + for i, x in enumerate(model.x_grid): + # Maximize RHS of Bellman equation at state x + v_new[i] = maximize(state_action_value, 1e-10, x, (x, v, model))[1] + + return v_new +``` + +After defining the Bellman operator, we are ready to solve the model. + +Let's start by creating a model using the default parameterization. + +```{code-cell} python3 +model = create_cake_eating_model() +``` + +Now let's see the iteration of the value function in action. + +We start from guess $v$ given by $v(x) = u(x)$ for every $x$ grid point. + +```{code-cell} python3 +x_grid = model.x_grid +v = u(x_grid, model.γ) # Initial guess +n = 12 # Number of iterations + +fig, ax = plt.subplots() + +ax.plot(x_grid, v, color=plt.cm.jet(0), + lw=2, alpha=0.6, label='Initial guess') + +for i in range(n): + v = T(v, model) # Apply the Bellman operator + ax.plot(x_grid, v, color=plt.cm.jet(i / n), lw=2, alpha=0.6) + +ax.legend() +ax.set_ylabel('value', fontsize=12) +ax.set_xlabel('cake size $x$', fontsize=12) +ax.set_title('Value function iterations') + +plt.show() +``` + +To do this more systematically, we introduce a wrapper function +called `compute_value_function` that iterates until some convergence conditions are +satisfied. + +```{code-cell} python3 +def compute_value_function(model, + tol=1e-4, + max_iter=1000, + verbose=True, + print_skip=25): + + # Set up loop + v = np.zeros(len(model.x_grid)) # Initial guess + i = 0 + error = tol + 1 + + while i < max_iter and error > tol: + v_new = T(v, model) + + error = np.max(np.abs(v - v_new)) + i += 1 + + if verbose and i % print_skip == 0: + print(f"Error at iteration {i} is {error}.") + + v = v_new + + if error > tol: + print("Failed to converge!") + elif verbose: + print(f"\nConverged in {i} iterations.") + + return v_new +``` + +Now let's call it, noting that it takes a little while to run. + +```{code-cell} python3 +v = compute_value_function(model) +``` + +Now we can plot and see what the converged value function looks like. + +```{code-cell} python3 +fig, ax = plt.subplots() + +ax.plot(x_grid, v, label='Approximate value function') +ax.set_ylabel('$V(x)$', fontsize=12) +ax.set_xlabel('$x$', fontsize=12) +ax.set_title('Value function') +ax.legend() +plt.show() +``` + +Next let's compare it to the analytical solution. + +```{code-cell} python3 +v_analytical = v_star(model.x_grid, model.β, model.γ) +``` + +```{code-cell} python3 +fig, ax = plt.subplots() + +ax.plot(x_grid, v_analytical, label='analytical solution') +ax.plot(x_grid, v, label='numerical solution') +ax.set_ylabel('$V(x)$', fontsize=12) +ax.set_xlabel('$x$', fontsize=12) +ax.legend() +ax.set_title('Comparison between analytical and numerical value functions') +plt.show() +``` + +The quality of approximation is reasonably good for large $x$, but less so near +the lower boundary. + +The reason is that the utility function and hence value function is very +steep near the lower boundary, and hence hard to approximate. + +```{note} +One way to fix this issue is to use a nonlinear grid, with more points in the +neighborhood of zero. + +Instead of pursuing this idea, however, we will turn our attention to +working with policy functions. + +We will see that value function iteration can be avoided by iterating on a guess +of the policy function instead. + +These ideas will be explored over the next few lectures. +``` + +### Policy Function + +Let's try computing the optimal policy. + +The optimal consumption policy was shown to be + +$$ + \sigma^*(x) = \left(1-\beta^{1/\gamma} \right) x +$$ + +Let's see if our numerical results lead to something similar. + +Our numerical strategy will be to compute + +$$ +\sigma(x) = \arg \max_{0 \leq c \leq x} \{u(c) + \beta v(x - c)\} +$$ + +on a grid of $x$ points and then interpolate. + +For $v$ we will use the approximation of the value function we obtained +above. + +Here's the function: + +```{code-cell} python3 +def σ(model, v): + """ + The optimal policy function. Given the value function, + it finds optimal consumption in each state. + + * model is an instance of Model + * v is a value function array + + """ + c = np.empty_like(v) + + for i in range(len(model.x_grid)): + x = model.x_grid[i] + # Maximize RHS of Bellman equation at state x + c[i] = maximize(state_action_value, 1e-10, x, (x, v, model))[0] + + return c +``` + +Now let's pass the approximate value function and compute optimal consumption: + +```{code-cell} python3 +c = σ(model, v) +``` + +(pol_an)= +Let's plot this next to the true analytical solution + +```{code-cell} python3 +c_analytical = c_star(model.x_grid, model.β, model.γ) + +fig, ax = plt.subplots() + +ax.plot(model.x_grid, c_analytical, label='analytical') +ax.plot(model.x_grid, c, label='numerical') +ax.set_ylabel(r'$\sigma(x)$') +ax.set_xlabel('$x$') +ax.legend() + +plt.show() +``` + +The fit is reasonable but not perfect. + +We can improve it by increasing the grid size or reducing the error +tolerance in the value function iteration routine. + +However, both changes will lead to a longer compute time. + +Another possibility is to use an alternative algorithm, which offers the +possibility of faster compute time and, at the same time, more accuracy. + +## Exercises + +```{exercise} +:label: cen_ex1 + +Try the following modification of the problem. + +Instead of the cake size changing according to $x_{t+1} = x_t - c_t$, let +it change according to + +$$ +x_{t+1} = (x_t - c_t)^{\alpha} +$$ + +where $\alpha$ is a parameter satisfying $0 < \alpha < 1$. + +(We will see this kind of update rule when we study optimal growth models.) + +Make the required changes to value function iteration code and plot the value and policy functions. + +Try to reuse as much code as possible. +``` + +```{solution-start} cen_ex1 +:class: dropdown +``` + +We need to create an extended version of our model and state-action value +function. + +We'll create a new `NamedTuple` for the extended cake model and a helper +function. + +```{code-cell} python3 +# Create extended cake model data structure +class ExtendedModel(NamedTuple): + β: float + γ: float + α: float + x_grid: np.ndarray + +def create_extended_model(β=0.96, # discount factor + γ=1.5, # degree of relative risk aversion + α=0.4, # productivity parameter + x_grid_min=1e-3, # exclude zero for numerical stability + x_grid_max=2.5, # size of cake + x_grid_size=120): + """ + Creates an instance of the extended cake eating model. + """ + x_grid = np.linspace(x_grid_min, x_grid_max, x_grid_size) + return ExtendedModel(β=β, γ=γ, α=α, x_grid=x_grid) + +def extended_state_action_value(c, x, v_array, model): + """ + Right hand side of the Bellman equation for the extended cake model given x and c. + """ + β, γ, α, x_grid = model.β, model.γ, model.α, model.x_grid + v = lambda x: np.interp(x, x_grid, v_array) + + return u(c, γ) + β * v((x - c)**α) +``` + +We also need a modified Bellman operator: + +```{code-cell} python3 +def T_extended(v, model): + """ + The Bellman operator for the extended cake model. + """ + v_new = np.empty_like(v) + + for i, x in enumerate(model.x_grid): + # Maximize RHS of Bellman equation at state x + v_new[i] = maximize(extended_state_action_value, 1e-10, x, (x, v, model))[1] + + return v_new +``` + +Now create the model: + +```{code-cell} python3 +model = create_extended_model() +``` + +Here's the computed value function. + +```{code-cell} python3 +def compute_value_function_extended(model, + tol=1e-4, + max_iter=1000, + verbose=True, + print_skip=25): + """ + Compute value function for extended cake model. + """ + v = np.zeros(len(model.x_grid)) + i = 0 + error = tol + 1 + + while i < max_iter and error > tol: + v_new = T_extended(v, model) + error = np.max(np.abs(v - v_new)) + i += 1 + if verbose and i % print_skip == 0: + print(f"Error at iteration {i} is {error}.") + v = v_new + + if error > tol: + print("Failed to converge!") + elif verbose: + print(f"\nConverged in {i} iterations.") + + return v_new + +v = compute_value_function_extended(model, verbose=False) + +fig, ax = plt.subplots() + +ax.plot(model.x_grid, v, lw=2, alpha=0.6) +ax.set_ylabel('value', fontsize=12) +ax.set_xlabel('state $x$', fontsize=12) + +plt.show() +``` + +Here's the computed policy, combined with the solution we derived above for +the standard cake eating case $\alpha=1$. + +```{code-cell} python3 +def σ_extended(model, v): + """ + The optimal policy function for the extended cake model. + """ + c = np.empty_like(v) + + for i in range(len(model.x_grid)): + x = model.x_grid[i] + c[i] = maximize(extended_state_action_value, 1e-10, x, (x, v, model))[0] + + return c + +c_new = σ_extended(model, v) + +# Get the baseline model for comparison +baseline_model = create_cake_eating_model() +c_analytical = c_star(baseline_model.x_grid, baseline_model.β, baseline_model.γ) + +fig, ax = plt.subplots() + +ax.plot(baseline_model.x_grid, c_analytical, label=r'$\alpha=1$ solution') +ax.plot(model.x_grid, c_new, label=fr'$\alpha={model.α}$ solution') + +ax.set_ylabel('consumption', fontsize=12) +ax.set_xlabel('$x$', fontsize=12) + +ax.legend(fontsize=12) + +plt.show() +``` + +Consumption is higher when $\alpha < 1$ because, at least for large $x$, the return +to savings is lower. + +```{solution-end} +``` diff --git a/lectures/foo.py b/lectures/foo.py new file mode 100644 index 00000000..14cce625 --- /dev/null +++ b/lectures/foo.py @@ -0,0 +1,2 @@ + +print("foobar") diff --git a/lectures/intro.md b/lectures/intro.md index eca02efb..b0ccbec1 100644 --- a/lectures/intro.md +++ b/lectures/intro.md @@ -11,9 +11,7 @@ kernelspec: # Python Programming for Economics and Finance -This website presents a set of lectures on Python programming for economics and finance. - -This is the first text in the series, which focuses on programming in Python. +This website presents a set of lectures on Python programming for economics and finance. This is the first text in the series, which focuses on programming in Python. For an overview of the series, see [this page](https://quantecon.org/python-lectures/) diff --git a/lectures/math2.py b/lectures/math2.py new file mode 100644 index 00000000..c41e3d7a --- /dev/null +++ b/lectures/math2.py @@ -0,0 +1 @@ +pi = 'foobar' diff --git a/lectures/mathfoo.py b/lectures/mathfoo.py new file mode 100644 index 00000000..c41e3d7a --- /dev/null +++ b/lectures/mathfoo.py @@ -0,0 +1 @@ +pi = 'foobar' diff --git a/lectures/mod.py b/lectures/mod.py new file mode 100644 index 00000000..1b0c806d --- /dev/null +++ b/lectures/mod.py @@ -0,0 +1 @@ +print(__name__) diff --git a/lectures/newfile.txt b/lectures/newfile.txt new file mode 100644 index 00000000..119fdd30 --- /dev/null +++ b/lectures/newfile.txt @@ -0,0 +1,2 @@ +Testing +Testing again \ No newline at end of file diff --git a/lectures/numbers.txt b/lectures/numbers.txt new file mode 100644 index 00000000..5a6193d8 --- /dev/null +++ b/lectures/numbers.txt @@ -0,0 +1,6 @@ +prices +3 +8 + +7 +21 diff --git a/lectures/output.txt b/lectures/output.txt new file mode 100644 index 00000000..66a30a78 --- /dev/null +++ b/lectures/output.txt @@ -0,0 +1,3 @@ +Line 0: Testing + +Line 1: Testing again diff --git a/lectures/output2.txt b/lectures/output2.txt new file mode 100644 index 00000000..7696e417 --- /dev/null +++ b/lectures/output2.txt @@ -0,0 +1,5 @@ +Line 0: Testing + +Line 1: Testing again + +This is the end of the file \ No newline at end of file diff --git a/lectures/test.py b/lectures/test.py new file mode 100644 index 00000000..71c4f375 --- /dev/null +++ b/lectures/test.py @@ -0,0 +1,8 @@ +def g(x): + a = 1 + x = x + a + return x + +a = 0 +y = g(10) +print("a = ", a, "y = ", y) diff --git a/lectures/test_table.csv b/lectures/test_table.csv new file mode 100644 index 00000000..03fcd0fb --- /dev/null +++ b/lectures/test_table.csv @@ -0,0 +1,11 @@ +Date,Open,High,Low,Close,Volume,Adj Close +2009-05-21,9280.35,9286.35,9189.92,9264.15,133200,9264.15 +2009-05-20,9372.72,9399.40,9311.61,9344.64,143200,9344.64 +2009-05-19,9172.56,9326.75,9166.97,9290.29,167000,9290.29 +2009-05-18,9167.05,9167.82,8997.74,9038.69,147800,9038.69 +2009-05-15,9150.21,9272.08,9140.90,9265.02,172000,9265.02 +2009-05-14,9212.30,9223.77,9052.41,9093.73,169400,9093.73 +2009-05-13,9305.79,9379.47,9278.89,9340.49,176000,9340.49 +2009-05-12,9358.25,9389.61,9298.61,9298.61,188400,9298.61 +2009-05-11,9460.72,9503.91,9342.75,9451.98,230800,9451.98 +2009-05-08,9351.40,9464.43,9349.57,9432.83,220200,9432.83 diff --git a/lectures/us_cities.txt b/lectures/us_cities.txt new file mode 100644 index 00000000..9be9260f --- /dev/null +++ b/lectures/us_cities.txt @@ -0,0 +1,9 @@ +new york: 8244910 +los angeles: 3819702 +chicago: 2707120 +houston: 2145146 +philadelphia: 1536471 +phoenix: 1469471 +san antonio: 1359758 +san diego: 1326179 +dallas: 1223229