"ValueError: x and y must be the same size" when plotting

Question:

I’m writing a python code that implements euler’s method to solve a 1st order ODE for an arbitrary range of values of time-step h.

The simplest solution I’ve come up with is to declare a Python list to store the results at the end of the ‘outer for’ loop:

y = []

for h in (0.05, 0.1, 0.2):
    t = np.arange(0, 5 + h, h) ## Time-steps as np array

    N = np.zeros(len(t)) ## Initialize number of U^{235} nuclei array.
    N[0] = N_0

    ## Implementation of Euler's method, first-order case. One 'for' loop suffices.
    for i in range(0, len(t) - 1):
        N[i+1] = N[i] * (1.0 - h/tau)
    
    y.append(t) ## Store results in python list. Each element is an array
                ## with the corresponding value of h

Then I tried to plot the results for h = 0.05, that are stored in y[0], as x-axis:

plt.scatter(y[0], N, marker='o', facecolors='none', s=60, lw=2)

But this returns the error "ValueError: x and y must be the same size"

I don’t understand why I’m getting size error. Isn’t y[0] an one-dimensional list? So i can’t use a list as an axis for a plot?

I’m confused because it WORKS with the variable t as it is a np.array:

t = np.arange(0, 5 + h, h) ## Time-steps as np array

    N = np.zeros(len(t)) ## Initialize number of U^{235} nuclei array.
    N[0] = N_0

    ## Implementation of Euler's method, first-order case. One 'for' loop suffices.
    for i in range(0, len(t) - 1):
        N[i+1] = N[i] * (1.0 - h/tau)


plt.scatter(t, N, marker='o', facecolors='none', s=60, lw=2)
Asked By: Lucas

||

Answers:

You are not storing the various N arrays in a list, so you are trying to plot t from the first iteration (i.e. y[0]) and N from the last iteration.

If you store everything in a dictionary, you can store the time and the number of nuclei as a value and the time increment h as a key:

# Imports.
import matplotlib.pyplot as plt
import numpy as  np

# Constants.
N_0 = 1000
TAU = 5
TIME_INCREMENTS = (0.05, 0.1, 0.2)

data = {}
for h in TIME_INCREMENTS :
    t = np.arange(0, 5 + h, h) # Time-steps as np array.
    N = np.zeros(len(t)) # Initialize number of U^{235} nuclei array.
    N[0] = N_0
    # Implementation of Euler's method, first-order case. One 'for' loop suffices.
    for i in range(0, len(t) - 1):
        N[i+1] = N[i] * (1.0 - h/TAU)    
    data[h] = (t, N)
  
fig, ax = plt.subplots()
for key, values in data.items():
    ax.scatter(*values, marker='o', s=60, lw=2, label=f"h = {key:.2f}")
ax.legend()

enter image description here

When you use numpy, you want to avoid loop for performance reasons (say if you have 1M of time increments). We could turn this:

N = np.zeros(len(t))
N[0] = N_0
for i in range(0, len(t) - 1):
    N[i+1] = N[i] * (1.0 - h/TAU) 

Into this:

N = N_0 * (1.0 - h/TAU)**np.arange(len(t))
Answered By: Guimoute