|
344 | 344 | "source": [ |
345 | 345 | "## Looping over some dimensions only\n", |
346 | 346 | "\n", |
347 | | - "`vectorize=True` loops over all loop dimensions, so `debug_interp` was called 12 times. But many functions can process a whole stack of vectors at once, as long as the other inputs are the same for all of them. For example, `scipy.interpolate.make_interp_spline` interpolates all vectors along the last axis at once, but only to a single set of new latitudes.\n", |
348 | | - "\n", |
349 | | - "Let's use different new latitudes at every time step. They vary along `time`, but not along `lon`, so we only need to loop over `time`. Instead of `True`, we pass the dimensions to loop over to `vectorize`. All other loop dimensions, here `lon`, are passed to our function in bulk:" |
| 347 | + "Let's use different new latitudes at every time step. They now vary along `time`, but not along `lon`. The same call as above still works:" |
350 | 348 | ] |
351 | 349 | }, |
352 | 350 | { |
|
358 | 356 | }, |
359 | 357 | "outputs": [], |
360 | 358 | "source": [ |
361 | | - "from scipy.interpolate import make_interp_spline\n", |
362 | | - "\n", |
363 | 359 | "newlat_t = xr.DataArray(\n", |
364 | 360 | " np.linspace(20, 70, 100) + np.arange(4)[:, np.newaxis], dims=(\"time\", \"lat\")\n", |
365 | 361 | ")\n", |
366 | 362 | "\n", |
| 363 | + "interped = xr.apply_ufunc(\n", |
| 364 | + " debug_interp, # first the function\n", |
| 365 | + " newlat_t,\n", |
| 366 | + " air.lat,\n", |
| 367 | + " air,\n", |
| 368 | + " input_core_dims=[[\"lat\"], [\"lat\"], [\"lat\"]],\n", |
| 369 | + " output_core_dims=[[\"lat\"]],\n", |
| 370 | + " exclude_dims={\"lat\"}, # dimensions allowed to change size. Must be set!\n", |
| 371 | + " vectorize=True,\n", |
| 372 | + ")" |
| 373 | + ] |
| 374 | + }, |
| 375 | + { |
| 376 | + "cell_type": "markdown", |
| 377 | + "id": "16", |
| 378 | + "metadata": { |
| 379 | + "tags": [] |
| 380 | + }, |
| 381 | + "source": [ |
| 382 | + "`debug_interp` is again called 12 times, but the 3 calls for the `lon` values of each `time` all use the same new latitudes. Many functions can process a whole stack of vectors at once in that case. For example, `scipy.interpolate.make_interp_spline` interpolates all vectors along the last axis at once, to a single set of new latitudes.\n", |
| 383 | + "\n", |
| 384 | + "So we only need to loop over `time`. Instead of `True`, we pass the dimensions to loop over to `vectorize`. All other loop dimensions, here `lon`, are passed to our function in bulk:" |
| 385 | + ] |
| 386 | + }, |
| 387 | + { |
| 388 | + "cell_type": "code", |
| 389 | + "execution_count": null, |
| 390 | + "id": "17", |
| 391 | + "metadata": { |
| 392 | + "tags": [] |
| 393 | + }, |
| 394 | + "outputs": [], |
| 395 | + "source": [ |
| 396 | + "from scipy.interpolate import make_interp_spline\n", |
| 397 | + "\n", |
367 | 398 | "\n", |
368 | 399 | "def debug_interp_batched(xi, x, data):\n", |
369 | 400 | " print(f\"data: {data.shape} | x: {x.shape} | xi: {xi.shape}\")\n", |
|
372 | 403 | " return make_interp_spline(x, data, k=1, axis=-1)(xi)\n", |
373 | 404 | "\n", |
374 | 405 | "\n", |
375 | | - "interped = xr.apply_ufunc(\n", |
376 | | - " debug_interp_batched,\n", |
| 406 | + "interped_batched = xr.apply_ufunc(\n", |
| 407 | + " debug_interp_batched, # only the function and vectorize changed\n", |
377 | 408 | " newlat_t,\n", |
378 | 409 | " air.lat,\n", |
379 | 410 | " air,\n", |
380 | 411 | " input_core_dims=[[\"lat\"], [\"lat\"], [\"lat\"]],\n", |
381 | 412 | " output_core_dims=[[\"lat\"]],\n", |
382 | | - " exclude_dims={\"lat\"},\n", |
| 413 | + " exclude_dims={\"lat\"}, # dimensions allowed to change size. Must be set!\n", |
383 | 414 | " vectorize=[\"time\"], # only loop over time\n", |
384 | 415 | ")\n", |
385 | | - "interped" |
| 416 | + "xr.testing.assert_allclose(interped, interped_batched)\n", |
| 417 | + "interped_batched" |
386 | 418 | ] |
387 | 419 | }, |
388 | 420 | { |
389 | 421 | "cell_type": "markdown", |
390 | | - "id": "16", |
| 422 | + "id": "18", |
391 | 423 | "metadata": { |
392 | 424 | "tags": [] |
393 | 425 | }, |
394 | 426 | "source": [ |
395 | | - "Now `debug_interp_batched` is called only 4 times, once per `time`, and receives all 3 `lon` at once: `data` has shape `(lon, lat)`.\n", |
| 427 | + "Now `debug_interp_batched` is called only 4 times, once per `time`, and receives all 3 `lon` at once: `data` has shape `(lon, lat)`. The result is the same.\n", |
396 | 428 | "\n", |
397 | 429 | "Loop dimensions that are passed in bulk follow numpy's broadcasting rules. `xi` does not vary along `lon`, so it has size 1 along that dimension, and our function has to handle that." |
398 | 430 | ] |
399 | 431 | }, |
400 | 432 | { |
401 | 433 | "cell_type": "markdown", |
402 | | - "id": "17", |
| 434 | + "id": "19", |
403 | 435 | "metadata": { |
404 | 436 | "tags": [] |
405 | 437 | }, |
|
0 commit comments