diff --git a/numpy-reshape/README.md b/numpy-reshape/README.md index 9cc4998b56..7378490a60 100644 --- a/numpy-reshape/README.md +++ b/numpy-reshape/README.md @@ -1,6 +1,44 @@ -# Image Repository For Using NumPy reshape() to Change the Shape of an Array +# Using NumPy reshape() to Change the Shape of an Array -This image repository accompanies [Using NumPy reshape() to Change the Shape of an Array](https://realpython.com/numpy-reshape/). It contains an image that you'll need while exploring the code in the tutorial. +This folder contains the sample code and image that accompany the Real Python +tutorial [Using NumPy reshape() to Change the Shape of an Array](https://realpython.com/numpy-reshape/). + +## Setup + +Create and activate a virtual environment, then install the requirements: + +```console +$ python -m venv venv +$ source venv/bin/activate +(venv) $ python -m pip install -r requirements.txt +``` + +## Scripts + +Each script is self-contained and mirrors one section of the tutorial. The +scripts that use random data seed the generator so that the output is +reproducible. + +| Script | Tutorial section | +| --- | --- | +| `array_shape.py` | Understand the shape of NumPy arrays | +| `change_shape.py` | Change an array's shape using `reshape()` | +| `reduce_dimensions.py` | Reduce an array's number of dimensions | +| `increase_dimensions.py` | Increase an array's number of dimensions | +| `compatible_shapes.py` | Ensure the new shape is compatible | +| `order_parameter.py` | Control how data is rearranged using `order` | +| `color_image.py` | Reduce a 3D color image to two dimensions | +| `wildcard.py` | Use `-1` as an argument in `reshape()` | + +Run any script with Python: + +```console +(venv) $ python change_shape.py +``` + +`color_image.py` reads `poppy.jpg` from this folder and opens the reshaped +images in your default image viewer, so run it from this directory. ## Image Credit + - poppy.jpg: [Pixabay](https://pixabay.com/photos/poppy-summer-red-nature-flower-2381645/) by [kellepics](https://pixabay.com/users/kellepics-4893063/) diff --git a/numpy-reshape/array_shape.py b/numpy-reshape/array_shape.py new file mode 100644 index 0000000000..2bd066d88e --- /dev/null +++ b/numpy-reshape/array_shape.py @@ -0,0 +1,8 @@ +"""Inspect the shape and number of dimensions of a NumPy array.""" + +import numpy as np + +numbers = np.array([[1, 2, 3, 4], [5, 6, 7, 8]]) +print(numbers) +print(f"Shape: {numbers.shape}") +print(f"Number of dimensions: {numbers.ndim}") diff --git a/numpy-reshape/change_shape.py b/numpy-reshape/change_shape.py new file mode 100644 index 0000000000..78d5d75638 --- /dev/null +++ b/numpy-reshape/change_shape.py @@ -0,0 +1,19 @@ +"""Change an array's shape with reshape() without changing its data. + +The random generator is seeded so that the output is reproducible. +""" + +import numpy as np + +rng = np.random.default_rng(seed=42) +results = rng.integers(0, 100, size=(5, 10)) +print(f"Original results, shape {results.shape}:") +print(results) + +# Reshape the five classes of ten scores into a single row +year_results = results.reshape((1, 50)) +print(f"\nReshaped to one row, shape {year_results.shape}:") +print(year_results) + +# year_results is still 2D, so you index it with both a row and a column +print(f"\nFirst score: {year_results[0, 0]}") diff --git a/numpy-reshape/color_image.py b/numpy-reshape/color_image.py new file mode 100644 index 0000000000..918841b950 --- /dev/null +++ b/numpy-reshape/color_image.py @@ -0,0 +1,23 @@ +"""Reshape a 3D color image into a 2D triptych of its color channels. + +Run this script from the folder that contains poppy.jpg. Each reshaped +image opens in your default image viewer. +""" + +import numpy as np +from PIL import Image + +with Image.open("poppy.jpg") as photo: + image_array = np.array(photo) + +print(f"Image shape: {image_array.shape}") +height, width, _ = image_array.shape + +# The default order="C" interleaves each pixel's color channels +triptych_c = image_array.reshape((height, 3 * width)) +print(f"Reshaped shape: {triptych_c.shape}") +Image.fromarray(triptych_c).show() + +# order="F" places the red, green, and blue channels side by side +triptych_f = image_array.reshape((height, 3 * width), order="F") +Image.fromarray(triptych_f).show() diff --git a/numpy-reshape/compatible_shapes.py b/numpy-reshape/compatible_shapes.py new file mode 100644 index 0000000000..6f0f1399ba --- /dev/null +++ b/numpy-reshape/compatible_shapes.py @@ -0,0 +1,34 @@ +"""Reshape into compatible shapes by trimming or extending the data.""" + +import numpy as np + +rng = np.random.default_rng(seed=42) +temperatures = rng.normal(18, 1, size=200) + +# An incompatible shape raises a ValueError +try: + temperatures.reshape((3, 7, 8)) +except ValueError as error: + print(f"ValueError: {error}") + +days_per_week = 7 +readings_per_day = 8 +number_of_weeks = len(temperatures) // (days_per_week * readings_per_day) +trimmed_length = number_of_weeks * days_per_week * readings_per_day + +# Option 1: trim the data so that it fits a whole number of weeks +temperatures_week = temperatures[:trimmed_length].reshape( + (number_of_weeks, days_per_week, readings_per_day) +) +print(f"Trimmed shape: {temperatures_week.shape}") + +# Option 2: extend the data with np.nan filler values +extended_length = (number_of_weeks + 1) * (days_per_week * readings_per_day) +additional_length = extended_length - len(temperatures) +temperatures_extended = np.append( + temperatures, np.full(additional_length, np.nan) +) +temperatures_week = temperatures_extended.reshape( + (number_of_weeks + 1, days_per_week, readings_per_day) +) +print(f"Extended shape: {temperatures_week.shape}") diff --git a/numpy-reshape/increase_dimensions.py b/numpy-reshape/increase_dimensions.py new file mode 100644 index 0000000000..c4d6decd7d --- /dev/null +++ b/numpy-reshape/increase_dimensions.py @@ -0,0 +1,13 @@ +"""Increase the number of dimensions of an array with reshape().""" + +import numpy as np + +rng = np.random.default_rng(seed=42) +temperatures = rng.normal(18, 1, size=200) +print(f"Original shape: {temperatures.shape}") + +# 200 readings, eight per day, gives 25 rows of eight columns +temperatures_day = temperatures.reshape((25, 8)) +print(f"Reshaped into days, shape: {temperatures_day.shape}") +print("Second day's readings:") +print(temperatures_day[1]) diff --git a/numpy-reshape/order_parameter.py b/numpy-reshape/order_parameter.py new file mode 100644 index 0000000000..637b14e3a0 --- /dev/null +++ b/numpy-reshape/order_parameter.py @@ -0,0 +1,13 @@ +"""Control how reshape() rearranges data with the order parameter.""" + +import numpy as np + +numbers = np.array([1, 2, 3, 4, 5, 6, 7, 8]) + +# Row-major (C) order fills each row before moving to the next +print('order="C":') +print(numbers.reshape((2, 4), order="C")) + +# Column-major (F) order fills each column before moving to the next +print('order="F":') +print(numbers.reshape((2, 4), order="F")) diff --git a/numpy-reshape/reduce_dimensions.py b/numpy-reshape/reduce_dimensions.py new file mode 100644 index 0000000000..f54a0f2f0b --- /dev/null +++ b/numpy-reshape/reduce_dimensions.py @@ -0,0 +1,15 @@ +"""Reduce the number of dimensions of an array with reshape().""" + +import numpy as np + +rng = np.random.default_rng(seed=42) +results = rng.integers(0, 100, size=(5, 10)) + +# Pass a one-element tuple to reshape the 2D array into a 1D array +year_results = results.reshape((50,)) +print(f"Shape: {year_results.shape}, dimensions: {year_results.ndim}") +print(f"First score: {year_results[0]}") + +# Passing a single integer gives the same 1D result +same_results = results.reshape(50) +print(f"Integer argument gives the same shape: {same_results.shape}") diff --git a/numpy-reshape/requirements.txt b/numpy-reshape/requirements.txt new file mode 100644 index 0000000000..733af6969c --- /dev/null +++ b/numpy-reshape/requirements.txt @@ -0,0 +1,2 @@ +numpy==2.5.0 +Pillow==12.2.0 diff --git a/numpy-reshape/wildcard.py b/numpy-reshape/wildcard.py new file mode 100644 index 0000000000..52a1cc64ac --- /dev/null +++ b/numpy-reshape/wildcard.py @@ -0,0 +1,16 @@ +"""Use -1 as a wildcard dimension in reshape().""" + +import numpy as np + +rng = np.random.default_rng(seed=42) +temperatures = rng.normal(18, 1, size=200) + +# Let reshape() infer the first dimension's length with -1 +temperatures_day = temperatures.reshape((-1, 8)) +print(f"Inferred shape: {temperatures_day.shape}") + +# Use -1 to flatten an array of any shape into a single dimension +numbers = rng.integers(1, 100, (2, 4, 3, 3)) +print(f"Original shape: {numbers.shape}") +numbers_flattened = numbers.reshape(-1) +print(f"Flattened shape: {numbers_flattened.shape}")