Baffled by numpy.unique() - python

As an extension to my previous project, where the equation X[i+1]=R*X[i](1-X[i]) is used to demonstrate a chaotic system (depending on R). Now I'm trying to construct the bifurcation graph.
About the code, I defined the function to do the actual calculations, and extracting the last 100 calculated values (to ensure the equilibrium reached), in order to plot out the bifurcated R vs x[i], I'm appending each R value to a empty X-data list, and multiple (aka, the returned 100 values) x[i] to a Y-data list (so it is actually a nested list...)
The thing is, depending on the R value, x[i] can be either single value (after equilibrium reached) or multiple values. So I was thinking to "purify" the nested Y-data list by numpy.unique() to remove all the replicated values.
Weirdly, when I don't make the extra "purification" step, the code actually works.
But when I put x = np.unique(logistic_calc(R,N)) it throws me a error says ValueError: setting an array element with a sequence.
Below is the code that works...
import numpy as np
import matplotlib.pyplot as plt
R = 0.2
N = 10_000
x0 = 0.5
def logistic_calc(R,N):
x = np.empty(N)
x[0] = x0
for i in range(1, N):
x[i] = R* x[i-1] * (1 - x[i-1])
return x[-100:]
x_lst = []
y_lst = []
for r in np.linspace(0.1,4,100):
R = r
x = logistic_calc(R,N)
x_lst.append(r)
y_lst.append(x)
plt.figure(figsize=(7, 4))
plt.plot(x_lst, y_lst, ls='', marker='.',ms='0.5', c="royalblue")
plt.ylim(0, 1)
plt.grid(c="lightgray")
plt.xlabel(r"$r$")
plt.ylabel(r"$x_n$")
plt.show()

From matplotlib documentation, paragraph "Plotting multiple sets of data":
"If x and/or y are 2D arrays a separate data set will be drawn for every column. If both x and y are 2D, they must have the same shape. If only one of them is 2D with shape (N, m) the other must have length N and will be used for every data set m."
It is not explicitly written that all sublists must have the same length. But it only refers to 2D arrays and not ragged nested sequences. To understand the behavior of plt.plot, just imagine that x and y will be cast into numpy arrays. In your second case, since y_lst contains lists with different lengths, this conversion cannot be made.
So I would go for something like this:
plt.figure(figize=(7, 4))
for r in np.linspace(1, 4, 100):
x = np.unique(logistic_calc(r, N))
plt.plot([r], [x], '.', ms=.5, c="royalblue") # a little bit tricky!
# OR
# plt.plot([r] * len(x), x, '.', ms=.5, c="royalblue")
...
plt.show()

When I run your example with np.unique, I get ...
...
Traceback (most recent call last):
File "test.py", line 29, in <module>
plt.plot(x_lst, y_lst, ls='', marker='.',ms='0.5', c="royalblue")
... more stack trace
ValueError: setting an array element with a sequence.
So the error is clearly happening at line ...
plt.plot(x_lst, y_lst, ls='', marker='.',ms='0.5', c="royalblue")
because the shapes of x_lst and y_lst no longer match up when you use np.unique.
You can get the code to work by looping over each each index of x_lst and y_lst and plotting them separately ...
import numpy as np
import matplotlib.pyplot as plt
R = 0.2
N = 10_000
x0 = 0.5
def logistic_calc(R,N):
x = np.empty(N)
x[0] = x0
for i in range(1, N):
x[i] = R* x[i-1] * (1 - x[i-1])
return x[-100:]
x_lst = []
y_lst = []
for r in np.linspace(0.1,4,100):
R = r
x = logistic_calc(R,N)
x = x.reshape(100)
x_lst.append(r)
y_lst.append(np.unique(x.round(decimals=4)))
plt.figure(figsize=(7, 4))
for x, y in zip(x_lst, y_lst):
plt.plot([x]*len(y), y, ls='', marker='.',ms='0.5', c="royalblue")
plt.ylim(0, 1)
plt.grid(c="lightgray")
plt.xlabel(r"$r$")
plt.ylabel(r"$x_n$")
plt.show()

Related

How to make points representing a 2D line denser in Python

# imports
import numpy as np
import matplotlib.pyplot as plt
Problem
I have a 2 arrays, representing datapoints on a line. For example:
x = np.array([0,1,2])
y = np.array([5,6,4])
Plot the data:
plt.plot(x,y)
plt.scatter(x,y,c='r')
Get:
I'd like to have arrays where the datapoints represent the same lines as above, but they are doubled. The desired result is:
new_x = np.array([0,0.5,1,1.5,2]) # this is the result I am looking for
new_y = np.array([5,5.5,6,5,4]) # this is the result I am looking for
Plot:
plt.plot(new_x,new_y)
plt.scatter(new_x,new_y,c='r')
Result:
My attempt
My method of doing this:
x = np.array([0,1,2])
y = np.array([5,6,4])
new_x=[]
for index, each in enumerate(x):
if index!=0:
new_x.append((each+x[index-1])/2)
new_y=[]
for index, each in enumerate(y):
if index!=0:
new_y.append((each+y[index-1])/2)
new_x.extend(x)
new_y.extend(y)
Plot:
plt.plot(new_x,new_y)
plt.scatter(new_x,new_y,c='r')
Result:
Points are in the right place, the new points added are the correct points, but they are put into the new_x & new_y arrays in the wrong order. It is fixable by paying more attention to the .extend, but this does not seem a good method to me anyway, because of the iteration.
My research
linear interpolation between two data points: doesn't use 2D arrays
How to write a function that returns an interpolated value (pandas dataframe)?: deals with interpolated values of functions, not resampling lines as above
And there are some other questions with the [interpolation] tag, but I haven't found one descibing this problem above.
Question
How do I get the above described new_x and new_y, preferably without iteration?
You could use these one-liner list comprehensions:
new_x = [j for i, v in enumerate(x) for j in [v, (x[i] + x[(i + 1 if (len(x) - 1) != i else i)]) / 2]]
new_y = [j for i, v in enumerate(y) for j in [v, (y[i] + y[(i + 1 if (len(y) - 1) != i else i)]) / 2]]
plt.plot(new_x, new_y)
plt.scatter(new_x, new_y, c='r')
plt.show()
Output:

Plotting x_n = A**n * x0 in python

I'm trying to plot a matrix multiplication in python.
I have the matrix A = [[0,-1],[1,1.6]], and x0 = [[5],[-1]].
The task is to plot xn, when I know that xn = A**n * x0, for n = 1, ... ,30
This is my code so far:
import numpy as np
import matplotlib.pyplot as plt
n = 30
A = np.matrix([[0,-1],[1,1.6]])
xn = np.zeros(n)
x0 = np.matrix([[5],[-1]])
for i in range(n):
xn[i]= A**i*x0
plt.plot(xn)
plt.show()
I keep getting the value error: setting an array element with a sequence, and when it works I get a really strange plot, which is probably wrong. Any ideas on how to do it?
I'm not sure if you want to take the matrix to a power by multiplying it by itself or by exponentiating the numbers inside of it. In any case, the reason why your code is throwing a ValueError: setting an array element with a sequence. is because the matrix vector multiplication A**i * x0 returns an array of length two - i.e. a vector.
Maybe you want to plot the vectors that result from the matrix-vector product. In that case, this code should do the trick:
import numpy as np
import matplotlib.pyplot as plt
n = 30
A = np.matrix([[0,-1],[1,1.6]])
xn = np.zeros((n, 2))
x0 = np.matrix([[5],[-1]])
fig, ax = plt.subplots()
for i in range(n):
A = A # A
xn[i, :] = np.squeeze(np.dot(A, x0))
ax.plot([0, xn[i, 0]], [0, xn[i, 1]])
label = r"$A^{%i}$" % (i+1)
ax.annotate(label, xy=(xn[i, 0], xn[i, 1]))
plt.show()
Notice I changed the shape of xn - it's now (nx2) compared to n as in your code. This means the result of the matrix-vector product will fit into xn. The # notation indicates matrix multiply in python3. I also labeled the line in the resulting plot with the power the matrix was taken to. You can see the output vector changing direction as the matrix changes. I think this is a nice example of how matrices (especially 2x2) can be thought of as linear transformations when applied to vectors. This video explains that concept nicely: https://www.youtube.com/watch?v=kYB8IZa5AuE.

Cannot change value of certain parts of array

Im trying to change the rows of an array with new values in a for loop, but cannot get it to work.
Problem is related to propagation of a wave packet in quantum physics.
Ive tried using the numpy.dot() function, but that doesnt work, and i tried making an easier for loop, that works.
import numpy as np
sig = 10**(-8)
x0 = 50*10**(-9)
L = 200*10**(-9)
N = 400
Nx = 1000
x = np.linspace(x0, L, N)
expsig = np.exp(-((1/2)*(x-x0)**2)/(sig**2))
expimg = np.exp(1j*(x-x0))
Phi = (1/(np.pi**(1/4)*np.sqrt(sig))*expsig*expimg)
Boxfunc = np.zeros(shape = (N, Nx))
for i in range(0, N):
SINnpi = np.sin(((i*np.pi)/L)*x)
Boxfunc[i,:] = np.sqrt(2/L)*SINnpi
Y = Boxfunc[i,:]*Phi
I expect the output to be a 400x1000 array with new calculated values from the multiplication between Phi and Boxfunc.
I just get the error message "could not broadcast input array from shape (400) into shape (1000)" when i get to the Boxfunc in the for-loop.
There is a problem with array x, it should be x = np.linspace(x0, L, Nx), then your code works.
Or you can define Boxfunc = np.zeros(shape = (Nx, N)). The problem is from the shape between x and Boxfunc.

How to Insert an Array of Values to a Symbolic Function in Python

I want to study symbolic functions in python. I want to create y(x) = x^2 + 2x + 3 and plot it in the range [1, 255]. I want to use the subs() function to calculate the values by using the for loop. However, when I run that I get this error:
IndexError('list index out of range')
Can you help me please?
import numpy as np
import matplotlib.pyplot as plot
from sympy import *
a = [1,2,3]
x = Symbol('x')
fx = a[0]*x**2 + a[1]*x + a[2]
t = list(range(1,256))
y = np.zeros(256)
for i in t:
y[i] = fx.subs({x:t[i]})
plot.plot(t,y)
plot.show()
Just replace with the following lines:
y = np.zeros(len(t))
for i in range(len(t)):
y[i] = fx.subs({x:t[i]})
The problem was that the length of t was only 255 but the len of y was 256 in your code because you define y = np.zeros(256), hence the Index Error because there is no t[256]. I am using y = np.zeros(len(t)) because you have as many y points as t (or x) points. By the way, you are most likely to get an error in your plot command the way it is right now because you have called import matplotlib.pyplot as plot. I would simply call it plt instead of plot
Output

Using meshgrid to convert X,Y,Z triplet to three 2D arrays for surface plot in matplotlib

I'm new to Python so please be patient. I appreciate any help!
What I have: three 1D lists (xr, yr, zr), one containing x-values, the other two y- and z-values
What I want to do: create a 3D contour plot in matplotlib
I realized that I need to convert the three 1D lists into three 2D lists, by using the meshgrid function.
Here's what I have so far:
xr = np.asarray(xr)
yr = np.asarray(yr)
zr = np.asarray(zr)
X, Y = np.meshgrid(xr,yr)
znew = np.array([zr for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = znew.reshape(X.shape)
Running this gives me the following error (for the last line I entered above):
total size of new array must be unchanged
I went digging around stackoverflow, and tried using suggestions from people having similar problems. Here are the errors I get from each of those suggestions:
Changing the last line to:
Z = znew.reshape(X.shape[0])
Gives the same error.
Changing the last line to:
Z = znew.reshape(X.shape[0], len(znew))
Gives the error:
Shape of x does not match that of z: found (294, 294) instead of (294, 86436).
Changing it to:
Z = znew.reshape(X.shape, len(znew))
Gives the error:
an integer is required
Any ideas?
Well,sample code below works for me
import numpy as np
import matplotlib.pyplot as plt
xr = np.linspace(-20, 20, 100)
yr = np.linspace(-25, 25, 110)
X, Y = np.meshgrid(xr, yr)
#Z = 4*X**2 + Y**2
zr = []
for i in range(0, 110):
y = -25.0 + (50./110.)*float(i)
for k in range(0, 100):
x = -20.0 + (40./100.)*float(k)
v = 4.0*x*x + y*y
zr.append(v)
Z = np.reshape(zr, X.shape)
print(X.shape)
print(Y.shape)
print(Z.shape)
plt.contour(X, Y, Z)
plt.show()
TL;DR
import matplotlib.pyplot as plt
import numpy as np
def get_data_for_mpl(X, Y, Z):
result_x = np.unique(X)
result_y = np.unique(Y)
result_z = np.zeros((len(result_x), len(result_y)))
# result_z[:] = np.nan
for x, y, z in zip(X, Y, Z):
i = np.searchsorted(result_x, x)
j = np.searchsorted(result_y, y)
result_z[i, j] = z
return result_x, result_y, result_z
xr, yr, zr = np.genfromtxt('data.txt', unpack=True)
plt.contourf(*get_data_for_mpl(xr, yr, zr), 100)
plt.show()
Detailed answer
At the beginning, you need to find out for which values of x and y the graph is being plotted. This can be done using the numpy.unique function:
result_x = numpy.unique(X)
result_y = numpy.unique(Y)
Next, you need to create a numpy.ndarray with function values for each point (x, y) from zip(X, Y):
result_z = numpy.zeros((len(result_x), len(result_y)))
for x, y, z in zip(X, Y, Z):
i = search(result_x, x)
j = search(result_y, y)
result_z[i, j] = z
If the array is sorted, then the search in it can be performed not in linear time, but in logarithmic time, so it is enough to use the numpy.searchsorted function to search. but to use it, the arrays result_x and result_y must be sorted. Fortunately, sorting is part of the numpy.unique method and there are no additional actions to do. It is enough to replace the search (this method is not implemented anywhere and is given simply as an intermediate step) method with np.searchsorted.
Finally, to get the desired image, it is enough to call the matplotlib.pyplot.contour or matplotlib.pyplot.contourf method.
If the function value does not exist for (x, y) for all x from result_x and all y from result_y, and you just want to not draw anything, then it is enough to replace the missing values with NaN. Or, more simply, create result_z as numpy.ndarray` from NaN and then fill it in:
result_z = numpy.zeros((len(result_x), len(result_y)))
result_z[:] = numpy.nan

Categories

Resources