Related
I'm trying to plot the output of a continuous wavelet transform of a signal that I have. The signal is just a cosine wave whose frequency decrease with time. I'm just using it to test the plotting function.
I have a function that plots the scalogram of the CWT but I'm struggling on how to pick the correct levels range to use for my contour lines.
What i would like to know:
Is increasing the amount of levels basically just making the difference between color regions smaller? With more levels the difference between adjacent levels is smaller?
If you look at the below plots you notice how there appears to be more distinct regions in the first plot (with more levels) So I would think this is a good thing. In the bottom plot the use of less levels we see that whole large dark red section as one color instead of a few different shades.
I appreciate any feedback/comments/answers or if you see any mistakes. Thank you so much!
the function I'm using to create the plots:
def plot_wavelet(ax, time2, signal, scales, waveletname = 'cmor',
cmap =plt.cm.seismic, title = '', ylabel = '', xlabel = ''):
dt=time2
coefficients, frequencies = pywt.cwt(signal, scales, waveletname, dt)
print ("coeff shape is:",coefficients.shape)
print ("frequency shape is:", frequencies.shape)
power = (abs(coefficients)) ** 2
period = frequencies
# different level lists to test
levels = [0.015625, 0.03125, 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16] #option 1
#levels = [0.015625, 0.03125, 0.0625, 0.125, 0.25, 0.5, 1] #option 2
contourlevels = np.log2(levels) #convert to log2 for plotting
time=range(2048) # Sampling frequency is 2048, so this is a 1 second sample
im = ax.contourf(time, np.log2(period), np.log2(power), contourlevels, extend='both',cmap=cmap)
ax.set_title(title, fontsize=20)
ax.set_ylabel(ylabel, fontsize=18)
ax.set_xlabel(xlabel, fontsize=18)
yticks = 2**np.arange(np.ceil(np.log2(period.min())), np.ceil(np.log2(period.max())))
ax.set_yticks(np.log2(yticks)) #original
ax.set_yticklabels(yticks) #original
ax.invert_yaxis()
ylim = ax.get_ylim()
cbar_ax = fig.add_axes([0.95, 0.5, 0.03, 0.25])
fig.colorbar(im, cax=cbar_ax, orientation="vertical")
return yticks, ylim
The only difference between the two plots below are the levels used, everything else is the same:
This is the plot with levels = [0.015625, 0.03125, 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16]
This is the plot with levels = [0.015625, 0.03125, 0.0625, 0.125, 0.25, 0.5, 1]
Wondering if its possible to create subplots of a subplot. The reason I am looking to do this is to create 3 broken axis charts on a single plot. I understand how to create a single broken axis chart with the example code below, but since a broken axis chart requires the use of subplots I am now in a position where I am trying to use subplots to create 3 columns, then subplot those columns into a subplot with 2 rows to create the broken axis chart. See below for visual explanation.
"""
EXAMPLE OF A SINGLE BROKEN AXIS CHART
"""
import matplotlib.pyplot as plt
import numpy as np
# 30 points between 0 0.2] originally made using np.random.rand(30)*.2
ptsA = np.array([
0.015, 0.166, 0.133, 0.159, 0.041, 0.024, 0.195, 0.039, 0.161, 0.018,
0.143, 0.056, 0.125, 0.096, 0.094, 0.051, 0.043, 0.021, 0.138, 0.075,
0.109, 0.195, 0.050, 0.074, 0.079, 0.155, 0.020, 0.010, 0.061, 0.008])
# Now let's make two outlier points which are far away from everything.
ptsA[[3, 14]] += .8
# 30 points between 0 0.2] originally made using np.random.rand(30)*.2
ptsB = np.array([
0.015, 0.166, 0.133, 0.159, 0.041, 0.024, 0.195, 0.039, 0.161, 0.018,
0.143, 0.056, 0.125, 0.096, 0.094, 0.051, 0.043, 0.021, 0.138, 0.075,
0.109, 0.195, 0.050, 0.074, 0.079, 0.155, 0.020, 0.010, 0.061, 0.008])
# Now let's make two outlier points which are far away from everything.
ptsB[[1, 7, 9, 13, 15]] += .95
# If we were to simply plot pts, we'd lose most of the interesting
# details due to the outliers. So let's 'break' or 'cut-out' the y-axis
# into two portions - use the top (ax) for the outliers, and the bottom
# (ax2) for the details of the majority of our data
f, (ax, ax2) = plt.subplots(2, 1, sharex=True)
# plot the same data on both axes
ax.plot(ptsB)
ax2.plot(pts)
# zoom-in / limit the view to different portions of the data
ax.set_ylim(.78, 1.) # outliers only
ax2.set_ylim(0, .22) # most of the data
# hide the spines between ax and ax2
ax.spines['bottom'].set_visible(False)
ax2.spines['top'].set_visible(False)
ax.xaxis.tick_top()
ax.tick_params(labeltop='off') # don't put tick labels at the top
ax2.xaxis.tick_bottom()
# This looks pretty good, and was fairly painless, but you can get that
# cut-out diagonal lines look with just a bit more work. The important
# thing to know here is that in axes coordinates, which are always
# between 0-1, spine endpoints are at these locations (0,0), (0,1),
# (1,0), and (1,1). Thus, we just need to put the diagonals in the
# appropriate corners of each of our axes, and so long as we use the
# right transform and disable clipping.
d = .015 # how big to make the diagonal lines in axes coordinates
# arguments to pass plot, just so we don't keep repeating them
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
ax.plot((-d, +d), (-d, +d), **kwargs) # top-left diagonal
ax.plot((1 - d, 1 + d), (-d, +d), **kwargs) # top-right diagonal
kwargs.update(transform=ax2.transAxes) # switch to the bottom axes
ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs) # bottom-left diagonal
ax2.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs) # bottom-right diagonal
# What's cool about this is that now if we vary the distance between
# ax and ax2 via f.subplots_adjust(hspace=...) or plt.subplot_tool(),
# the diagonal lines will move accordingly, and stay right at the tips
# of the spines they are 'breaking'
plt.show()
Desired Output
3 subplots, each containing 2 subplots
First of all you cannot create a subplot of a subplot. Subplots are axes objects placed in a figure and an axes cannot have "child axes".
The solution to your problem would be to create 6 subplots and apply sharex=True to the respective axes.
import matplotlib.pyplot as plt
import numpy as np
data = np.random.rand(17, 6)
data[15:, 3:] = np.random.rand(2, 3)+3.
markers=["o", "p", "s"]
colors=["r", "g", "b"]
fig=plt.figure(figsize=(10, 4))
axes = []
for i in range(3):
ax = fig.add_subplot(2,3,i+1)
axes.append(ax)
for i in range(3):
ax = fig.add_subplot(2,3,i+4, sharex=axes[i])
axes.append(ax)
for i in range(3):
# plot same data in both top and down axes
axes[i].plot(data[:,i], data[:,i+3], marker=markers[i], linestyle="", color=colors[i])
axes[i+3].plot(data[:,i], data[:,i+3], marker=markers[i], linestyle="", color=colors[i])
for i in range(3):
axes[i].spines['bottom'].set_visible(False)
axes[i+3].spines['top'].set_visible(False)
axes[i].xaxis.tick_top()
axes[i].tick_params(labeltop='off') # don't put tick labels at the top
axes[i+3].xaxis.tick_bottom()
axes[i].set_ylim([3,4])
axes[i+3].set_ylim([0,1])
axes[i].set_xlim([0,1])
#adjust space between subplots
plt.subplots_adjust(hspace=0.08, wspace=0.4)
plt.show()
Wondering if its possible to create subplots of a subplot. The reason I am looking to do this is to create 3 broken axis charts on a single plot. I understand how to create a single broken axis chart with the example code below, but since a broken axis chart requires the use of subplots I am now in a position where I am trying to use subplots to create 3 columns, then subplot those columns into a subplot with 2 rows to create the broken axis chart. See below for visual explanation.
"""
EXAMPLE OF A SINGLE BROKEN AXIS CHART
"""
import matplotlib.pyplot as plt
import numpy as np
# 30 points between 0 0.2] originally made using np.random.rand(30)*.2
ptsA = np.array([
0.015, 0.166, 0.133, 0.159, 0.041, 0.024, 0.195, 0.039, 0.161, 0.018,
0.143, 0.056, 0.125, 0.096, 0.094, 0.051, 0.043, 0.021, 0.138, 0.075,
0.109, 0.195, 0.050, 0.074, 0.079, 0.155, 0.020, 0.010, 0.061, 0.008])
# Now let's make two outlier points which are far away from everything.
ptsA[[3, 14]] += .8
# 30 points between 0 0.2] originally made using np.random.rand(30)*.2
ptsB = np.array([
0.015, 0.166, 0.133, 0.159, 0.041, 0.024, 0.195, 0.039, 0.161, 0.018,
0.143, 0.056, 0.125, 0.096, 0.094, 0.051, 0.043, 0.021, 0.138, 0.075,
0.109, 0.195, 0.050, 0.074, 0.079, 0.155, 0.020, 0.010, 0.061, 0.008])
# Now let's make two outlier points which are far away from everything.
ptsB[[1, 7, 9, 13, 15]] += .95
# If we were to simply plot pts, we'd lose most of the interesting
# details due to the outliers. So let's 'break' or 'cut-out' the y-axis
# into two portions - use the top (ax) for the outliers, and the bottom
# (ax2) for the details of the majority of our data
f, (ax, ax2) = plt.subplots(2, 1, sharex=True)
# plot the same data on both axes
ax.plot(ptsB)
ax2.plot(pts)
# zoom-in / limit the view to different portions of the data
ax.set_ylim(.78, 1.) # outliers only
ax2.set_ylim(0, .22) # most of the data
# hide the spines between ax and ax2
ax.spines['bottom'].set_visible(False)
ax2.spines['top'].set_visible(False)
ax.xaxis.tick_top()
ax.tick_params(labeltop='off') # don't put tick labels at the top
ax2.xaxis.tick_bottom()
# This looks pretty good, and was fairly painless, but you can get that
# cut-out diagonal lines look with just a bit more work. The important
# thing to know here is that in axes coordinates, which are always
# between 0-1, spine endpoints are at these locations (0,0), (0,1),
# (1,0), and (1,1). Thus, we just need to put the diagonals in the
# appropriate corners of each of our axes, and so long as we use the
# right transform and disable clipping.
d = .015 # how big to make the diagonal lines in axes coordinates
# arguments to pass plot, just so we don't keep repeating them
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
ax.plot((-d, +d), (-d, +d), **kwargs) # top-left diagonal
ax.plot((1 - d, 1 + d), (-d, +d), **kwargs) # top-right diagonal
kwargs.update(transform=ax2.transAxes) # switch to the bottom axes
ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs) # bottom-left diagonal
ax2.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs) # bottom-right diagonal
# What's cool about this is that now if we vary the distance between
# ax and ax2 via f.subplots_adjust(hspace=...) or plt.subplot_tool(),
# the diagonal lines will move accordingly, and stay right at the tips
# of the spines they are 'breaking'
plt.show()
Desired Output
3 subplots, each containing 2 subplots
First of all you cannot create a subplot of a subplot. Subplots are axes objects placed in a figure and an axes cannot have "child axes".
The solution to your problem would be to create 6 subplots and apply sharex=True to the respective axes.
import matplotlib.pyplot as plt
import numpy as np
data = np.random.rand(17, 6)
data[15:, 3:] = np.random.rand(2, 3)+3.
markers=["o", "p", "s"]
colors=["r", "g", "b"]
fig=plt.figure(figsize=(10, 4))
axes = []
for i in range(3):
ax = fig.add_subplot(2,3,i+1)
axes.append(ax)
for i in range(3):
ax = fig.add_subplot(2,3,i+4, sharex=axes[i])
axes.append(ax)
for i in range(3):
# plot same data in both top and down axes
axes[i].plot(data[:,i], data[:,i+3], marker=markers[i], linestyle="", color=colors[i])
axes[i+3].plot(data[:,i], data[:,i+3], marker=markers[i], linestyle="", color=colors[i])
for i in range(3):
axes[i].spines['bottom'].set_visible(False)
axes[i+3].spines['top'].set_visible(False)
axes[i].xaxis.tick_top()
axes[i].tick_params(labeltop='off') # don't put tick labels at the top
axes[i+3].xaxis.tick_bottom()
axes[i].set_ylim([3,4])
axes[i+3].set_ylim([0,1])
axes[i].set_xlim([0,1])
#adjust space between subplots
plt.subplots_adjust(hspace=0.08, wspace=0.4)
plt.show()
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.
EDIT:
I found myself an answer (see below) how to align the images within their subplots:
for ax in axes:
ax.set_anchor('W')
EDIT END
I have some data I plot with imshow. It's long in x direction, so I break it into multiple lines by plotting slices of the data in vertically stacked subplots. I am happy with the result but for the last subplot (not as wide as the others) which I want left aligned with the others.
The code below is tested with Python 2.7.1 and matplotlib 1.2.x.
#! /usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
x_slice = [0,3]
y_slices = [[0,10],[10,20],[20,30],[30,35]]
d = np.arange(35*3).reshape((35,3)).T
vmin = d.min()
vmax = d.max()
fig, axes = plt.subplots(len(y_slices), 1)
for i, s in enumerate(y_slices):
axes[i].imshow(
d[ x_slice[0]:x_slice[1], s[0]:s[1] ],
vmin=vmin, vmax=vmax,
aspect='equal',
interpolation='none'
)
plt.show()
results in
Given the tip by Zhenya I played around with axis.get/set_position. I tried to half the width but I don't understand the effect it has
for ax in axes:
print ax.get_position()
p3 = axes[3].get_position().get_points()
x0, y0 = p3[0]
x1, y1 = p3[1]
# [left, bottom, width, height]
axes[3].set_position([x0, y0, (x1-x0)/2, y1-y0])
get_position gives me the bbox of each subplot:
for ax in axes:
print ax.get_position()
Bbox(array([[ 0.125 , 0.72608696],
[ 0.9 , 0.9 ]]))
Bbox(array([[ 0.125 , 0.5173913 ],
[ 0.9 , 0.69130435]]))
Bbox(array([[ 0.125 , 0.30869565],
[ 0.9 , 0.4826087 ]]))
Bbox(array([[ 0.125 , 0.1 ],
[ 0.9 , 0.27391304]]))
so all the subplots have the exact same horizontal extent (0.125 to 0.9). Judging from the narrower 4th subplot the image inside the subplot is somehow centered.
Let's look at the AxesImage objects:
for ax in axes:
print ax.images[0]
AxesImage(80,348.522;496x83.4783)
AxesImage(80,248.348;496x83.4783)
AxesImage(80,148.174;496x83.4783)
AxesImage(80,48;496x83.4783)
again, the same horizontal extent for the 4th image too.
Next try AxesImage.get_extent():
for ax in axes:
print ax.images[0].get_extent()
# [left, right, bottom, top]
(-0.5, 9.5, 2.5, -0.5)
(-0.5, 9.5, 2.5, -0.5)
(-0.5, 9.5, 2.5, -0.5)
(-0.5, 4.5, 2.5, -0.5)
there is a difference (right) but the left value is the same for all so why is the 4th one centered then?
EDIT: They are all centered...
Axis.set_anchor works so far (I just hope I don't have to adjust too much manually now):
for ax in axes:
ax.set_anchor('W')
You can control the position of the subplot manually, like so:
for ax in axes:
print ax.get_position()
and
ax[3].set_position([0.1,0.2,0.3,0.4])
Alternatively, you may want to have a look at GridSpec
Often I found ax.set_position is very hard to be precise.
I would prefer to use plt.subplots_adjust(wspace=0.005) # adjust the width between the subplots to adjust the distance between the two horizontal subplots.
You can adjust the vertical distance as well.