Search code examples
pythonmatplotlibparameter-passing

Iterating (or not?) parameters for matplotlib to plot a function in python


I suspect I still don't understand argument passing entirely (already in my previous problem: fmin (scipy) and understanding parameter passing of the function in python )

I made up a simplified problem here and used iteration, inspired by this post: How to use matplotlib to plot a function with the argument on an axis without using a different n cause that is not what I need in my function.

import numpy as np
import matplotlib.pyplot as plt

%matplotlib inline

dataset=[0.2,0.5,0.02,0.4,0.7]

def functie_f(x,lamb_da):
    return lamb_da*np.power((lamb_da*x),2)
print (functie_f(0.2,7))


def LogL(q):
    lamb_da = q
    return np.log(functie_f(dataset, lamb_da)).sum()

lamb_da=2
 
y=[functie_f(dataset,lamb_da) for i in dataset]
plt.plot(dataset,y)
print(y)

print (LogL(lamb_da))
#y2=[LogL(lamb_da) for j in dataset]
#plt.plot(dataset,y2)

which gives me this outcome:

first result

The print of y shows that I have 5 times the same array and 5 times a constant function for each different array. I expected to see 1 function. What I think has happened is that functie_f goes over the same dataset multiple times, but without the iteration it also gives a wrong number of y-values (that is why I tried the iteration to give me a y-value for each x-value from the dataset.

When I want the second plot as well (=same code but with the last two lines not commented out) it is worse, also my first plot has disapeared.

you can see that in this image:

second result

So I tried to omit the iteration since that gives me too many arrays with the same y-values, but when I do that I don't have the same dimension for the x-values and y-values.

What I don't understand and would like to remediate:

  1. Why do I get more y-values when I start from the same dataset?
  2. Can I use different parameters for a function and how? (like a tuple and a single value)
  3. Is my parameter passing for LogL plot that uses functie_f correct?

Here are my attemps without iteration with the same error for the three different codes:

First I tried simply to leave out the iteration for y:


y=functie_f(dataset,lamb_da)
plt.plot(dataset,y)

Then I tried to iterate x (thinking it would take 1 y-value for every 1 x-value)

for x in dataset
    y=functie_f(dataset,lamb_da)
    plt.plot(dataset,y)

And I tried to write that under the declaration of y because maybe it also has the dataset x in its function, but that did not change anything (I also tried 'renew kernel to force it to re-run the code)

 y=functie_f(dataset,lamb_da)
 for x in dataset
     plt.plot(dataset,y)

For all three changes here above, I get the (same) following error message:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-4-347b473a5e13> in <module>
     19 y=functie_f(dataset,lamb_da)
     20 for x in dataset:
---> 21     plt.plot(dataset,y)
     22 print(y)
     23 

/home/marecage/snap/jupyter/common/lib/python3.7/site-packages/matplotlib/pyplot.py in plot(scalex, scaley, data, *args, **kwargs)
   2767     return gca().plot(
   2768         *args, scalex=scalex, scaley=scaley,
-> 2769         **({"data": data} if data is not None else {}), **kwargs)
   2770 
   2771 

/home/marecage/snap/jupyter/common/lib/python3.7/site-packages/matplotlib/axes/_axes.py in plot(self, scalex, scaley, data, *args, **kwargs)
   1633         """
   1634         kwargs = cbook.normalize_kwargs(kwargs, mlines.Line2D)
-> 1635         lines = [*self._get_lines(*args, data=data, **kwargs)]
   1636         for line in lines:
   1637             self.add_line(line)

/home/marecage/snap/jupyter/common/lib/python3.7/site-packages/matplotlib/axes/_base.py in __call__(self, data, *args, **kwargs)
    310                 this += args[0],
    311                 args = args[1:]
--> 312             yield from self._plot_args(this, kwargs)
    313 
    314     def get_next_color(self):

/home/marecage/snap/jupyter/common/lib/python3.7/site-packages/matplotlib/axes/_base.py in _plot_args(self, tup, kwargs, return_kwargs)
    496 
    497         if x.shape[0] != y.shape[0]:
--> 498             raise ValueError(f"x and y must have same first dimension, but "
    499                              f"have shapes {x.shape} and {y.shape}")
    500         if x.ndim > 2 or y.ndim > 2:

ValueError: x and y must have same first dimension, but have shapes (5,) and (10,)

Solution

  • When dataset is a regular python list, lamb_da*x is a list times an integer, which just duplicates the list x times. For example:

    a = [1, 2, 3]
    b = 2
    c = a*b
    print(c)  # [1, 2, 3, 1, 2, 3]
    

    You are also doing a list comprehension in this line:

    y = [functie_f(dataset,lamb_da) for i in dataset]
    

    There, you are calling the function the exact same way every time, so you are getting len(dataset) identical results.

    You can fix this in one of two ways.

    1. You can keep the list comprehension but actually loop over the dataset.
    import numpy as np
    import matplotlib.pyplot as plt
    
    def functie_f(x, lamb_da):
        return lamb_da*np.power((lamb_da*x), 2)
    
    lamb_da = 2
    dataset = [0.2, 0.5, 0.02, 0.4, 0.7]
    
    y = [functie_f(x, lamb_da) for x in dataset]
    plt.plot(dataset, y)
    

    Your mistake in all your loops was you were creating the variable you need (you called it i or x in different cases) but not actually using those values.

    1. You can convert dataset to a numpy array and remove the list comprehension.
    import numpy as np
    import matplotlib.pyplot as plt
    
    def functie_f(x,lamb_da):
        return lamb_da*np.power((lamb_da*x),2)
    
    lamb_da = 2
    dataset = np.array([0.2,0.5,0.02,0.4,0.7])
     
    y = functie_f(dataset, lamb_da)
    plt.plot(dataset, y)
    

    Both methods give this result:

    Note: the line looks as it does (going back and forth) because your dataset values are not sorted.