Plotting Curved Lines in Python - python

I'd like to plot curved lines of a specific arch like shape, below is how far I've gotten using specific values (these values need to be used) but it plots straight lines.
I'm also having trouble formatting the y axis the way I want. It's a log scale and I'd like it to go up to 1 (like in the ideal plot above). Some help would be great, thanks! =)

The reason why your line is not stretching on a log scale plot is because there are no points between the points that are on the top and on the bottom. log plot does not curve the lines, only place the points on a different scale, the line between them are still straight.
To change this, we add more points between dots. and the result will become curved.
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import ScalarFormatter
# Data for plotting
t = [0.0, 62.5, 125.0, 187.5, 250, 312.5, 375, 437.5, 500]
s = [0.1, 0.005, 0.1, 0.005, 0.1, 0.005, 0.1, 0.005, 0.1]
def extendlist(l):
master = []
for i in range(len(l)-1):
x = np.linspace(l[i], l[i+1], 50)
master.extend(x)
return master
t = extendlist(t)
s = extendlist(s)
fig, ax = plt.subplots()
ax.semilogy(t, s)
ax.set(xlabel='x axis', ylabel='y axis', title='Stuff')
plt.xlim((0,500))
plt.ylim((0.001, 1))
plt.show()
This will generate what you graphed on paper.

you can use interp1d
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interp1d
t = [0.0, 62.5, 125.0, 187.5, 250, 312.5, 375, 437.5, 500]
s = [0.1, 0.005, 0.1, 0.005, 0.1, 0.005, 0.1, 0.005, 0.1]
tnew = np.linspace(0, 500, num=1001, endpoint=True)
f = interp1d(t, s)
plt.semilogy(tnew, f(tnew))
plt.ylim((0.001, 1))
plt.show()

Related

Plotting a Lorenz curve from a given NumPy file

I am trying to write a program that reads data from a NumPy file, and then uses that data to plot a Lorenz curve, but I'm not exactly sure how to make the Lorenz curve. I tried using the cumsum() function, but I was not able to plot the Lorenz curve. Here's what I have so far:
import numpy as np
import matplotlib.pyplot as plt
data = np.load('pop2010.npy')
print(data)
plt.plot(data[0]) # display all the points
plt.show()
plot_x = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
plot_y = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
plt.plot(plot_x, plot_y)
# set the labels for x, y, and title
plt.xlabel("Countries")
plt.ylabel("Wealth")
plt.title("Population-Lorenz Curve")
# save plot as png file
plt.savefig('population-lorenz.png', dpi = 200)
plt.show()
Any advice would be appreciated, thanks!
Adapting from https://zhiyzuo.github.io/Plot-Lorenz/, combining with your code.
Not quite clear from the context you provide, but I think data is what you want to plot as a Lorenz curve, and the plot_x, plot_y variables are your way of plotting the x-y line?
Note that I am using the object-oriented API instead of the pyplot API since this is what the docs now recommend--I think you'll find it's easier to work with in the long run. See https://matplotlib.org/stable/api/index.html#usage-patterns for detail.
import numpy as np
import matplotlib.pyplot as plt
data = np.load('pop2010.npy')
X_lorenz = data.cumsum() / data.sum()
X_lorenz = np.insert(X_lorenz, 0, 0)
fig, ax = plt.subplots(figsize=[6,6])
## scatter plot of Lorenz curve
ax.scatter(np.arange(X_lorenz.size)/(X_lorenz.size-1), X_lorenz,
marker='x', color='darkgreen', s=100)
## line plot of equality
ax.plot([0,1], [0,1], color='k')
# set the labels for x, y, and title
ax.set_xlabel("Countries")
ax.set_ylabel("Wealth")
ax.set_title("Population-Lorenz Curve")
plt.show()
# save plot as png file
plt.savefig('population-lorenz.png', dpi = 200)

How do I fix my slider? It isn't working as intended

I'm trying to create a slider that will set the bins in matplotlib, here is my code:
%matplotlib notebook
import matplotlib.pyplot as plt
from matplotlib.pyplot import ion
import numpy as np
import matplotlib.animation as animation
from matplotlib.widgets import RadioButtons
from matplotlib.widgets import Slider
# generate 4 random variables from the random, gamma, exponential, and uniform distributions
sample_size = 10000
normal = np.random.normal(loc=0.0, scale=1.0, size=sample_size)
gamma = np.random.gamma(shape = 1.0, scale=1.0, size=sample_size)
uniform = np.random.uniform(low=0.0, high=10.0, size=sample_size)
exponential = np.random.exponential(scale=1.0, size=sample_size)
fig, sub_plt = plt.subplots()
plt.subplots_adjust(top=0.65) # Adjust subplot to not overlap with radio box
axcolor = 'lightgoldenrodyellow'
rax = plt.axes([0.05, 0.7, 0.25, 0.25], facecolor=axcolor)
axfreq = plt.axes([0.20, 0.02, 0.65, 0.03], facecolor=axcolor)
radio = RadioButtons(rax, ('Normal', 'Gamma', 'Uniform', 'Exponential'))
slide = Slider(axfreq, 'Bins', 10.0,200.0,valinit=30.0)
def dist_func(type_l):
sub_plt.clear() # comment this line if you want to keep previous drawings
dist_dict = {'Normal':normal, 'Gamma':gamma, 'Uniform':uniform, 'Exponential':exponential}
data_type = dist_dict[type_l]
sub_plt.hist(data_type, bins=100)
radio.on_clicked(dist_func)
def bin_func(val):
slide_val = slide.val
plt.figure()
sub_plt.hist(data_type,bins=slide_val)
fig.canvas.draw_idle()
slide.on_changed(bin_func)
plt.show()
I want the value of the slider to set the bins of the histogram. This renders the slider but the slider does not set the bins as intended, in fact it doesn't do anything. Is there any way to make the bins work as intended?
I believe sub_plt.hist(data_type,bins=slide_val) is the problem, data_type isn't a global variable so you can't create a plot with an undefined variable.
I moved the canvas redrawing code to inside the dist_func so that clicking one of the radio buttons redraws the plot without having to move the slider.
It is also important to ensure the slider value is an integer (must have a discrete number of bins!)
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import RadioButtons
from matplotlib.widgets import Slider
# generate 4 random variables from the random, gamma, exponential, and uniform distributions
sample_size = 10000
normal = np.random.normal(loc=0.0, scale=1.0, size=sample_size)
gamma = np.random.gamma(shape=1.0, scale=1.0, size=sample_size)
uniform = np.random.uniform(low=0.0, high=10.0, size=sample_size)
exponential = np.random.exponential(scale=1.0, size=sample_size)
fig, sub_plt = plt.subplots()
plt.subplots_adjust(top=0.65) # Adjust subplot to not overlap with radio box
axcolor = 'lightgoldenrodyellow'
rax = plt.axes([0.05, 0.7, 0.25, 0.25], facecolor=axcolor)
axfreq = plt.axes([0.20, 0.02, 0.65, 0.03], facecolor=axcolor)
radio = RadioButtons(rax, ('Normal', 'Gamma', 'Uniform', 'Exponential'))
slide = Slider(axfreq, 'Bins', 10.0, 200.0, valinit=30.0, valstep=1)
dist_dict = {'Normal': normal, 'Gamma': gamma, 'Uniform': uniform, 'Exponential': exponential}
def dist_func(type_l, bins=100):
sub_plt.clear() # comment this line if you want to keep previous drawings
data_type = dist_dict[type_l]
sub_plt.hist(data_type, bins=bins)
fig.canvas.draw_idle()
radio.on_clicked(dist_func)
def update(a):
dist_func(radio.value_selected, bins=int(a))
# the final step is to specify that the slider needs to
# execute the above function when its value changes
slide.on_changed(update)
dist_func('Normal', bins=100)
plt.show()

Drawing heat map in python

I'm having two lists x, y representing coordinates in 2D. For example x = [1,4,0.5,2,5,10,33,0.04] and y = [2,5,44,0.33,2,14,20,0.03]. x[i] and y[i] represent one point in 2D. Now I also have a list representing "heat" values for each (x,y) point, for example z = [0.77, 0.88, 0.65, 0.55, 0.89, 0.9, 0.8,0.95]. Of course x,y and z are much higher dimensional than the example.
Now I would like to plot a heat map in 2D where x and y represents the axis coordinates and z represents the color. How can this be done in python?
This code produces a heat map. With a few more data points, the plot starts looking pretty nice and I've found it to be very quick in general even for >100k points.
import matplotlib.pyplot as plt
import matplotlib.tri as tri
import numpy as np
import math
x = [1,4,0.5,2,5,10,33,0.04]
y = [2,5,44,0.33,2,14,20,0.03]
z = [0.77, 0.88, 0.65, 0.55, 0.89, 0.9, 0.8, 0.95]
levels = [0.7, 0.75, 0.8, 0.85, 0.9]
plt.figure()
ax = plt.gca()
ax.set_aspect('equal')
CS = ax.tricontourf(x, y, z, levels, cmap=plt.get_cmap('jet'))
cbar = plt.colorbar(CS, ticks=np.sort(np.array(levels)),ax=ax, orientation='horizontal', shrink=.75, pad=.09, aspect=40,fraction=0.05)
cbar.ax.set_xticklabels(list(map(str,np.sort(np.array(levels))))) # horizontal colorbar
cbar.ax.tick_params(labelsize=8)
plt.title('Heat Map')
plt.xlabel('X Label')
plt.ylabel('Y Label')
plt.show()
Produces this image:
or if you're looking for a more gradual color change, change the tricontourf line to this:
CS = ax.tricontourf(x, y, z, np.linspace(min(levels),max(levels),256), cmap=cmap)
and then the plot will change to:
Based on this answer, you might want to do something like:
import numpy as np
from matplotlib.mlab import griddata
import matplotlib.pyplot as plt
xs0 = [1,4,0.5,2,5,10,33,0.04]
ys0 = [2,5,44,0.33,2,14,20,0.03]
zs0 = [0.77, 0.88, 0.65, 0.55, 0.89, 0.9, 0.8,0.95]
N = 30j
extent = (np.min(xs0),np.max(xs0),np.min(ys0),np.max(ys0))
xs,ys = np.mgrid[extent[0]:extent[1]:N, extent[2]:extent[3]:N]
resampled = griddata(xs0, ys0, zs0, xs, ys, interp='linear')
plt.imshow(np.fliplr(resampled).T, extent=extent,interpolation='none')
plt.colorbar()
The example here might also help: http://matplotlib.org/examples/pylab_examples/griddata_demo.html

Updating contour plot levels with matplotlib slider

I'm trying to change the values of the colour levels on a matplotlib filled contour plot using a slider. i.e contourf(x,y,z,np.linspace(a,b,n)) where the sliders would control a and b and would change the plot colour levels when a slider is moved.
The following code takes column formatted data converts it to the form required by contourf and then the the sliders are implemented.
This is what I've tried:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
data=np.genfromtxt('file.dat',skip_header=1)
len=np.sqrt(data[:,0].size)
x=np.reshape(data[:,0],(len,len))
y=np.reshape(data[:,1],(len,len))
z=np.reshape(data[:,3],(len,len))
l=plt.contourf(x,y,z,np.linspace(0,100,255))
axmax = plt.axes([0.25, 0.1, 0.65, 0.03]) #slider location and size
axmin = plt.axes([0.25, 0.15, 0.65, 0.03])
smax = Slider(axmax, 'Max',0, 100, 50) #slider properties
smin = Slider(axmin, 'Min', 0, 100, 0)
def update(val):
l.levels(np.linspace(smin.val,smax.val,255))#changing levels of plot
fig.canvas.draw_idle() #line that throws error
smax.on_changed(update)
smin.on_changed(update)
plt.show()
A large number of matplotlib errors are thrown when a slider is moved with the relevant one being 'TypeError:'numpy.ndarray' object is not callable' which is thrown by the line
fig.canvas.draw_idle()
The problem is that l.levels is a array, so you would have to change the values in this array. In my testing changing these values does not cause the plot to update. So another solution is to just clear the axis and redraw the plot.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
data=np.random.random([25,4])
data = data*100
len=np.sqrt(data[:,0].size)
x=np.reshape(data[:,0],(len,len))
y=np.reshape(data[:,1],(len,len))
z=np.reshape(data[:,3],(len,len))
l=plt.contourf(x,y,z,np.linspace(0,100,255))
contour_axis = plt.gca()
axmax = plt.axes([0.25, 0.1, 0.65, 0.03]) #slider location and size
axmin = plt.axes([0.25, 0.15, 0.65, 0.03])
smax = Slider(axmax, 'Max',0, 100, 50) #slider properties
smin = Slider(axmin, 'Min', 0, 100, 0)
def update(val):
contour_axis.clear()
contour_axis.contourf(x,y,z,np.linspace(smin.val,smax.val,255))
plt.draw()
smax.on_changed(update)
smin.on_changed(update)
plt.show()

Change colour scale increments in Python

I have created a frequency time spectrogram plot seen below.
I want to edit the colour scale so that the higher frequencies shown from 20 seconds are more prominent. I think having smaller increments at the lower end of the colour scale (blues) would achieve this but am not sure how to do it. Any help would be great!
Here is what I have so far:
import numpy as np
import matplotlib.pyplot as plt
from obspy.core import read
from obspy.signal.tf_misfit import cwt
import pylab
tr = read("whole.sac")[0]
npts = tr.stats.npts
dt = tr.stats.delta
t = np.linspace(0, dt * npts, npts)
f_min = 1
f_max = 10
scalogram = cwt(tr.data, dt, 8, f_min, f_max)
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.7, 0.60])
ax2 = fig.add_axes([0.1, 0.75, 0.75, 0.2])
ax3 = fig.add_axes([0.83, 0.1, 0.03, 0.6])
img = ax1.imshow(np.abs(scalogram)[-1::-1], extent=[t[0], t[-1], f_min, f_max],
aspect='auto', interpolation="nearest")
ax1.set_xlabel("Time after %s [s]" % tr.stats.starttime)
ax1.set_ylabel("Frequency [Hz]")
ax1.set_yscale('linear')
ax2.plot(t, tr.data, 'k')
pylab.xlim([30,72])
fig.colorbar(img, cax=ax3)
plt.show()
You could try other colormaps or make you own according to this recipe.
Or you may want to filter the data to set all values above a given threshold (e.g. 60) to the threshold value. This would use the entire range of the colormap on the range of interest. You can easily use np.clip() to do this.
So...
np.abs(scalogram)[-1::-1]
becomes
np.clip(np.abs(scalogram)[-1::-1], 0, 100)
to clip between 0 and 100.

Categories