1

I am using matplotlib.scatter to plot a bifurcation diagram for a system that goes through periodic-doubling route to chaos. Using the data that can be found here, I use the following commands:

import numpy as np
import matplotlib.pyplot as plt
p_chi0,b_chi0=np.loadtxt('data.dat')
plt.scatter(p_chi0,b_chi0,s=0.2,marker=".")
plt.xlim([2.35,2.6])
plt.show()

I obtain the following plot:

enter image description here

However, as you can see the lines are not smooth. Is there a way to interpolate this type of data even though there are these bifurcations, in such a way that it is not one-to-one correspondence?

Community
  • 1
  • 1
Ohm
  • 2,312
  • 4
  • 36
  • 75

2 Answers2

2

You would get the most accurate representation of the data when plotting each data point exactly one pixel in size.

import numpy as np
import matplotlib.pyplot as plt

p, b = np.loadtxt('data/bifurcation.txt')

fig, ax = plt.subplots(figsize=(8,6), dpi=100)

# Plot one pixel sizes markers
ax.plot(p,b, ls="", marker=",")
# or with scatter.
#ax.scatter(p,b, s=(72./fig.dpi)**2, marker='o', lw=0,)

ax.set_xlim([2.35, 2.6])
fig.savefig('bifurcation.png')
plt.show()

enter image description here

This however does not look smooth at all. You will get a smooth representation by choosing a larger marker size, but then increasing the dpi and figure size. Then resampling the image down to the original dimensions would make the result look smoother.

Also the use of a Cairo based backend would be beneficial, due to Agg based backends not being accurate at stamping the markers.

import matplotlib
matplotlib.use("Qt5Cairo")
import numpy as np
import matplotlib.pyplot as plt

p, b = np.loadtxt('data/bifurcation.txt')

fig, ax = plt.subplots(figsize=(8,6), dpi=300)

ax.scatter(p,b, s=(2*72./fig.dpi)**2, marker='o', lw=0)

ax.set_xlim([2.35, 2.6])
fig.savefig('bifurcation.png')
plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
0

enter image description here

As your data points are ordered and closely spaced, all you need to do is interpolate linearly between successive data points. Also, I would increase the DPI when saving the figure.

For example:

import numpy as np
import matplotlib.pyplot as plt

p, b = np.loadtxt('data.dat')

def upsample_linearly(x, upsample_by):
    dx = np.diff(x)
    xx = [x[:-1] + fraction * dx for fraction in np.linspace(0., 1., upsample_by)]
    xx = np.transpose(xx).ravel()
    return xx

upsampled_p = upsample_linearly(p, 5)
upsampled_b = upsample_linearly(b, 5)

fig, ax = plt.subplots(1,1)
ax.scatter(upsampled_p, upsampled_b, s=0.1, marker=".")
ax.set_xlim([2.35, 2.6])

fig.savefig('./bifurcation.png', dpi=1000)
plt.show()
Paul Brodersen
  • 11,221
  • 21
  • 38
  • Unfortunately this answer does not solve the problem. It's even wrong because it creates data points, which are not there in the original data, I.e. in the range between 2.45 and 2.55 there is a single bifurcation. Yet this "solution" plots 5 arms. – ImportanceOfBeingErnest Nov 21 '18 at 18:09
  • @ImportanceOfBeingErnest MY HEART. I thought you would know me better by now (*bleeds profusely*). On a more serious note, you can't see the bifurcations in the figure of OP because of the small marker size combined with the low default DPI in`savefig`. If you download the data, plot it yourself and zoom in, you will see the additional bifurcations. I promise. – Paul Brodersen Nov 22 '18 at 14:43
  • Sorry, I do not know you at all. If you need further help finding out about your error, you may ask a new question about it. – ImportanceOfBeingErnest Nov 22 '18 at 14:49
  • @ImportanceOfBeingErnest [Item # 1](https://stackoverflow.com/q/50910165/2912349), [item #2](https://stackoverflow.com/q/47293499/2912349) + a bunch of questions where we both submitted answers. Anyway, the additional bifurcations that you see in my figure are in the data OP posted. Just plot the figure yourself, pretty please. – Paul Brodersen Nov 22 '18 at 14:57
  • I don't need to plot the figure. Your algorithm is wrong. You may easily see that the number of arms is simply the number you plug in into your function. Here you plug in 5, so you get 5 arms. Plug in 10 and you get 10 arms. – ImportanceOfBeingErnest Nov 22 '18 at 15:03
  • So what's the conclusion here? Would you remove the wrong part from the answer? Do you want me to do it? – ImportanceOfBeingErnest Nov 23 '18 at 18:27
  • 1
    Nevermind, I decided to just provide a new answer here. In case you are still struggeling to find your error, possibly a test like `test = lambda x,y: np.any((x >= 2.44) & (x <= 2.54) & (y >= 0.66) & (y <= 0.76))` would help. With the original data `test(p,b) ==False`, with the data from this answer `test(upsampled_p,upsampled_b) == True`. I guess you need to decide for yourself if you want to leave your inadequate reaction to being corrected available openly on the internet under your name. I could imagine it to be a little embarrasing for someone working in the scientific community? – ImportanceOfBeingErnest Nov 25 '18 at 17:47