I am creating plots that include zoom inserts. The data is diverse it is impossoble for me to know what the data will be like before the program starts. I want to make the zoom insert zoom in as much as possible, without overlapping with any other element of my plot. Here is an example, where I use a zoom of 2. Ideally, I would like to automatically determine what this number should be:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes
from mpl_toolkits.axes_grid1.inset_locator import mark_inset
fig, ax = plt.subplots()
xin = np.linspace(0, np.random.uniform(.5, 4), 1000)
x_samples = np.random.uniform(0.9, 1.1, (1, 1000)) * np.sqrt(xin[:, np.newaxis])
ax.fill_between(xin, x_samples.min(1), x_samples.max(1))
axins = zoomed_inset_axes(ax, zoom=2, loc='upper left')
axins.fill_between(xin, x_samples.min(1), x_samples.max(1))
axins.set_xlim(.05, .1)
idx = np.logical_and(xin > 0.05, xin < 0.1)
axins.set_ylim(x_samples.min(1)[idx].min(), x_samples.max(1)[idx].max())
axins.set_xticks([])
axins.set_yticks([])
mark_inset(ax, axins, loc1=4, loc2=3, fc="none", ec="0.5")
plt.savefig('hey')
plt.clf()
As you can see, zoom=2 was too low of a value. I can manually set the zoom parameter to a correct value. This is a tedious process. Is there a way to automatically find the zoom parameter that will maximize the insert size while avoiding overlaps with other parts of the plot?
We can face this problem in an iterative way:
Start with the maximum possible zoom (such that the inset occupies the whole height of the plot). As a result, part of the inset will overlap the plot.
Check how much vertical gap exists before the point where the overlapping starts.
Based on the current height of the inset, scale it down to avoid overlapping.
After the rescaling, the width of the inset is also reduced, so we can scale it up again with the free vertical gap that has been left behind.
Go back to 2. until convergence / maximum number of iterations is reached.
In practice the convergence is fast and reached in less than 10 iterations with the given data.
Visually:
Code for insets at the upper-left location.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes, mark_inset
def get_inset_max_zoom(x, y, xlim_inset, ylim_inset, max_iters=10):
""" Zoom that maximizes inset size without overlapping the artists """
# width and height of the inset in non-scaled coordinates.
inset_w = xlim_inset[1] - xlim_inset[0]
inset_h = ylim_inset[1] - ylim_inset[0]
# max y-coordinate of the whole plot.
y_max_plot = y.max()
# start with maximum zoom.
y_gap = y_max_plot - y.min()
zoom = y_gap / inset_h
for i in range(max_iters):
y_affected_max = y[x < zoom * inset_w].max()
# recalculate zoom by adjusting the gap.
y_gap = y_max_plot - y_affected_max
zoom = y_gap / inset_h
return zoom
if __name__ == "__main__":
# Change the seed to show produce different values.
rng = np.random.RandomState(seed=0)
# main plot.
fig, ax = plt.subplots()
xin = np.linspace(0, rng.uniform(.5, 4), 1000)
x_samples = rng.uniform(
0.9, 1.1, (1, 1000)) * np.sqrt(xin[:, np.newaxis])
ax.fill_between(xin, x_samples.min(1), x_samples.max(1))
# get xy pairs.
y = x_samples.ravel()
x = np.repeat(xin, x_samples.shape[1])
# define the limits and location of the zoom inset.
xlim_inset = (.05, .1)
idx = np.logical_and(xin > xlim_inset[0], xin < xlim_inset[1])
ylim_inset = (x_samples.min(1)[idx].min(), x_samples.max(1)[idx].max())
loc = 'upper left'
# get max zoom.
zoom = get_inset_max_zoom(x, y, xlim_inset, ylim_inset, max_iters=5)
# create the inset.
axins = zoomed_inset_axes(ax, zoom=zoom, loc=loc, borderpad=0.5)
axins.set(
xlim=xlim_inset,
ylim=ylim_inset,
xticks=[], yticks=[])
# connect the bboxes.
mark_inset(ax, axins, loc1=4, loc2=3, fc="none", ec="0.5")
# plot within the inset.
axins.fill_between(xin, x_samples.min(1), x_samples.max(1))
Generalizing to the 4 corner locations {upper-left, upper-right, lower-right, lower-left}. For instance, with loc = 'lower right':
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes, mark_inset
def get_inset_max_zoom_given_loc(
x, y, xlim_inset, ylim_inset, loc='upper left', max_iters=10):
""" Zoom that maximizes inset size without overlapping the artists """
# width and height of the inset in non-scaled coordinates.
inset_w = xlim_inset[1] - xlim_inset[0]
inset_h = ylim_inset[1] - ylim_inset[0]
# handy variables.
is_left = 'left' in loc
is_upper = 'upper' in loc
y_min_plot, y_max_plot = y.min(), y.max()
y_xtm_plot = y_max_plot if is_upper else y_min_plot
x_max_plot = x.max()
# start with maximum zoom.
y_gap = y_max_plot - y_min_plot
zoom = y_gap / inset_h
for i in range(max_iters):
# get affected x-coordinate range.
if is_left:
x_affected = x < zoom * inset_w
else:
x_affected = x > x_max_plot - zoom * inset_w
# get affected y-coordinate extremum.
y_affected = y[x_affected]
y_affected_xtm = y_affected.max() if is_upper else y_affected.min()
# recalculate zoom by adjusting the gap.
y_gap = abs(y_xtm_plot - y_affected_xtm)
zoom = y_gap / inset_h
return zoom
if __name__ == "__main__":
# Change the seed to show produce different values.
rng = np.random.RandomState(seed=0)
# main plot.
fig, ax = plt.subplots()
xin = np.linspace(0, rng.uniform(.5, 4), 1000)
x_samples = rng.uniform(
0.9, 1.1, (1, 1000)) * np.sqrt(xin[:, np.newaxis])
ax.fill_between(xin, x_samples.min(1), x_samples.max(1))
# get xy pairs.
y = x_samples.ravel()
x = np.repeat(xin, x_samples.shape[1])
# define the limits and location of the zoom inset.
xlim_inset = (.05, .1)
idx = np.logical_and(xin > xlim_inset[0], xin < xlim_inset[1])
ylim_inset = (x_samples.min(1)[idx].min(), x_samples.max(1)[idx].max())
loc = 'lower right'
# get max zoom.
zoom = get_inset_max_zoom_given_loc(
x, y, xlim_inset, ylim_inset, loc=loc, max_iters=10)
# create the inset.
axins = zoomed_inset_axes(ax, zoom=zoom, loc=loc, borderpad=0.5)
axins.set(
xlim=xlim_inset,
ylim=ylim_inset,
xticks=[], yticks=[])
# connect the bboxes.
mark_inset(ax, axins, loc1=4, loc2=3, fc="none", ec="0.5")
# plot within the inset.
axins.fill_between(xin, x_samples.min(1), x_samples.max(1))
I am attempting to find the convolution of two rectangular pulses.
No errors are being thrown - and I am getting a suitably shaped waveform output - however, the magnitude of my answer appears to be vastly too large, and I'm also unsure of how to fit a correct x/time axis to this convolution.
Additionally, the magnitude of the convolution seems to depend on the number of samples in the two pulses (essentially the sampling frequency) - which I would say is incorrect.
As I am attempting to model a continuous time signal, rather than discrete, I have set the sampling frequency very high.
Clearly I am doing something wrong - but what is it, and how do I correct it?
Thanks very much - and apologies if some of the code is not very "pythonic" (Recent Java convert)!
EDIT: Whilst attempting to evaluate this by hand, I have found that the time axis is too small by a factor of 2; again, I don't know why this would be
import numpy as np
import matplotlib.pyplot as plt
from sympy.functions.special import delta_functions as dlta
def stepFunction(t): #create pulses below from step-functions
retval = 0
if t == 0:
retval = 1
else:
retval = dlta.Heaviside(t)
return retval
def hT (t=0, start=0, dur=8, samples=1000):
time = np.linspace(start, start + dur, samples, True)
data = np.zeros(len(time))
hTArray = np.column_stack((time, data))
for row in range(len(hTArray)):
hTArray[row][1] = 2 * (stepFunction(hTArray[row][0] - 4) - stepFunction(hTArray[row][0] - 6))
return hTArray
def xT (t=0, start=0, dur=8, samples=1000):
time = np.linspace(start, start + dur, samples, True)
data = np.zeros(len(time))
hTArray = np.column_stack((time, data))
for row in range(len(hTArray)):
hTArray[row][1] = (stepFunction(hTArray[row][0]) - stepFunction(hTArray[row][0] - 4))
return hTArray
hTArray = hT() #populate two arrays with functions
xTArray = xT()
resCon = np.convolve(hTArray[:, 1], xTArray[:, 1]) #convolute signals/array data
Xaxis = np.linspace(hTArray[0][0], hTArray[len(hTArray) - 1][0],
len(resCon), endpoint=True) # create time axis, with same intervals as original functions
#Plot the functions & convolution
plt.plot(hTArray[:, 0], hTArray[:, 1], label=r'$x1(t)$')
plt.plot(xTArray[:, 0], xTArray[:, 1], label=r'$x2(t)$')
plt.plot(Xaxis, resCon)
plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3,
ncol=2, mode="expand", borderaxespad=0.)
ax = plt.gca()
ax.grid(True)
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data', 0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data', 0))
plt.show()
When you convolute discrete signals, you need to scale appropriately to keep the signal's energy (integral over |x(t)|²) constant:
import numpy as np
import matplotlib.pyplot as plt
n = 1000
t = np.linspace(0, 8, n)
T = t[1] - t[0] # sampling width
x1 = np.where(t<=4, 1, 0) # build input functions
x2 = np.where(np.logical_and(t>=4, t<=6), 2, 0)
y = np.convolve(x1, x2, mode='full') * T # scaled convolution
ty = np.linspace(0, 2*8, n*2-1) # double time interval
# plot results:
fg, ax = plt.subplots(1, 1)
ax.plot(t, x1, label="$x_1$")
ax.plot(t, x2, label="$x_2$")
ax.plot(ty, y, label="$x_1\\star x_2$")
ax.legend(loc='best')
ax.grid(True)
fg.canvas.draw()
I am trying to draw wind roses with the following code. It used to work a few months ago.
import sys
from windrose import WindroseAxes
from matplotlib import pyplot as plt
import matplotlib.cm as cm
from numpy.random import random
from numpy import arange
import os
import numpy as np
def plot(prefix, spds, dirs):
ws = np.array(spds)
wd = np.array(dirs)
def new_axes():
fig = plt.figure(figsize=(8, 8), dpi=80, facecolor='w', edgecolor='w')
rect = [0.1, 0.1, 0.8, 0.8]
ax = WindroseAxes(fig, rect, axisbg='w')
fig.add_axes(ax)
return ax, fig
def set_legend(ax):
l = ax.legend(axespad=-0.10, title="m/s", loc=0)
plt.setp(l.get_texts(), fontsize=8)
# windrose like a stacked histogram with normed (displayed in percent) results
ax, fig = new_axes()
ax.bar(wd, ws, normed=True, opening=0.8, edgecolor='white', bins=arange(0,max(ws),5))
set_legend(ax)
tokens = prefix.split("/")[-1].split("_")
if tokens[0] == "Dust":
title = "%s Dust" % tokens[1]
else:
title = tokens[0]
plt.title(title, y=1.08)
fig.savefig("%s-fig1.png" % prefix)
def main(folder="data"):
for filename in filter(lambda x:x.endswith(".csv"), os.listdir(folder)):
path = "%s/%s" % (folder, filename)
plot_path = "%s/plots" % folder
if not os.path.exists(plot_path):
os.mkdir(plot_path)
print path
f = open(path)
f.readline()
directions = []
speeds = []
for line in f:
cols = line.split(",")
direction = cols[5]
speed = cols[6]
try:
direction = int(direction)
speed = int(speed) * 0.44704
except:
continue
directions.append(direction)
speeds.append(speed)
plot("%s/plots/%s" % (folder, filename.split(".")[0]), speeds, directions)
if __name__ == "__main__":
main(sys.argv[1])
But when I run it I get this error:
IndexError Traceback (most recent call last)
/Users/Abdulhaleem-Labban/Dropbox/windrose/process.py in <module>()
112
113 if __name__ == "__main__":
--> 114 main(sys.argv[1])
115
IndexError: list index out of range
The file that contains the data needed to draw the wind roses is called Dust_TabukWI_Wind.csv. Here's a sample:
Tabuk YR-- MO DA HRMN DIR SPD VSB MW
403750 1985 1 1 1125 240 28 0.4 32
403750 1985 1 18 1200 230 34 0.1 33
403750 1985 12 18 600 120 14 6.2 30
403750 1988 11 30 1300 340 34 0.3 32
403750 1992 12 15 900 240 31 0.3 33
403750 1992 12 15 1000 240 29 0.3 33
403750 1992 12 15 1100 240 29 0.6 33
403750 2008 1 29 1100 220 29 0.6 31
403750 2008 1 29 1200 210 34 3.1 30
403750 2008 1 29 1300 210 34 1.9 31
I think this is the script that has the "argv" but how can i run it ?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__version__ = '1.4'
__author__ = 'Lionel Roubeyrie'
__mail__ = 'lionel.roubeyrie#gmail.com'
__license__ = 'CeCILL-B'
import matplotlib
import matplotlib.cm as cm
import numpy as np
from matplotlib.patches import Rectangle, Polygon
from matplotlib.ticker import ScalarFormatter, AutoLocator
from matplotlib.text import Text, FontProperties
from matplotlib.projections.polar import PolarAxes
from numpy.lib.twodim_base import histogram2d
import matplotlib.pyplot as plt
from pylab import poly_between
RESOLUTION = 100
ZBASE = -1000 #The starting zorder for all drawing, negative to have the grid on
class WindroseAxes(PolarAxes):
"""
Create a windrose axes
"""
def __init__(self, *args, **kwargs):
"""
See Axes base class for args and kwargs documentation
"""
#Uncomment to have the possibility to change the resolution directly
#when the instance is created
#self.RESOLUTION = kwargs.pop('resolution', 100)
PolarAxes.__init__(self, *args, **kwargs)
self.set_aspect('equal', adjustable='box', anchor='C')
self.radii_angle = 67.5
self.cla()
def cla(self):
"""
Clear the current axes
"""
PolarAxes.cla(self)
self.theta_angles = np.arange(0, 360, 45)
self.theta_labels = ['E', 'N-E', 'N', 'N-W', 'W', 'S-W', 'S', 'S-E']
self.set_thetagrids(angles=self.theta_angles, labels=self.theta_labels)
self._info = {'dir' : list(),
'bins' : list(),
'table' : list()}
self.patches_list = list()
def _colors(self, cmap, n):
'''
Returns a list of n colors based on the colormap cmap
'''
return [cmap(i) for i in np.linspace(0.0, 1.0, n)]
def set_radii_angle(self, **kwargs):
"""
Set the radii labels angle
"""
null = kwargs.pop('labels', None)
angle = kwargs.pop('angle', None)
if angle is None:
angle = self.radii_angle
self.radii_angle = angle
print self.get_rmax()
radii = np.linspace(0.1, self.get_rmax(), 6)
radii_labels = [ "%.1f%%" %r for r in radii ]
radii_labels[0] = "" #Removing label 0
# radii_labels = ["" for r in radii]
null = self.set_rgrids(radii=radii, labels=radii_labels,
angle=self.radii_angle, **kwargs)
def _update(self):
self.set_rmax(rmax=np.max(np.sum(self._info['table'], axis=0)))
self.set_radii_angle(angle=self.radii_angle)
def legend(self, loc='lower left', **kwargs):
"""
Sets the legend location and her properties.
The location codes are
'best' : 0,
'upper right' : 1,
'upper left' : 2,
'lower left' : 3,
'lower right' : 4,
'right' : 5,
'center left' : 6,
'center right' : 7,
'lower center' : 8,
'upper center' : 9,
'center' : 10,
If none of these are suitable, loc can be a 2-tuple giving x,y
in axes coords, ie,
loc = (0, 1) is left top
loc = (0.5, 0.5) is center, center
and so on. The following kwargs are supported:
isaxes=True # whether this is an axes legend
prop = FontProperties(size='smaller') # the font property
pad = 0.2 # the fractional whitespace inside the legend border
shadow # if True, draw a shadow behind legend
labelsep = 0.005 # the vertical space between the legend entries
handlelen = 0.05 # the length of the legend lines
handletextsep = 0.02 # the space between the legend line and legend text
axespad = 0.02 # the border between the axes and legend edge
"""
def get_handles():
handles = list()
for p in self.patches_list:
if isinstance(p, matplotlib.patches.Polygon) or \
isinstance(p, matplotlib.patches.Rectangle):
color = p.get_facecolor()
elif isinstance(p, matplotlib.lines.Line2D):
color = p.get_color()
else:
raise AttributeError("Can't handle patches")
handles.append(Rectangle((0, 0), 0.2, 0.2,
facecolor=color, edgecolor='black'))
return handles
def get_labels():
labels = np.copy(self._info['bins'])
labels = ["[%.1f : %0.1f[" %(labels[i], labels[i+1]) \
for i in range(len(labels)-1)]
return labels
null = kwargs.pop('labels', None)
null = kwargs.pop('handles', None)
handles = get_handles()
labels = get_labels()
self.legend_ = matplotlib.legend.Legend(self, handles, labels,
loc, **kwargs)
return self.legend_
def _init_plot(self, dir, var, **kwargs):
"""
Internal method used by all plotting commands
"""
#self.cla()
null = kwargs.pop('zorder', None)
#Init of the bins array if not set
bins = kwargs.pop('bins', None)
if bins is None:
bins = np.linspace(np.min(var), np.max(var), 6)
if isinstance(bins, int):
#bins = np.linspace(np.min(var), np.max(var), bins)
bins = [0.0,5.0,10.0,15.0,20.0,25.0,30.0,35.0,40.0]
bins = np.asarray(bins)
nbins = len(bins)
#Number of sectors
nsector = kwargs.pop('nsector', None)
if nsector is None:
nsector = 16
#Sets the colors table based on the colormap or the "colors" argument
colors = kwargs.pop('colors', None)
cmap = kwargs.pop('cmap', None)
if colors is not None:
if isinstance(colors, str):
colors = [colors]*nbins
if isinstance(colors, (tuple, list)):
if len(colors) != nbins:
raise ValueError("colors and bins must have same length")
else:
if cmap is None:
cmap = cm.jet
colors = self._colors(cmap, nbins)
#Building the angles list
angles = np.arange(0, -2*np.pi, -2*np.pi/nsector) + np.pi/2
normed = kwargs.pop('normed', False)
blowto = kwargs.pop('blowto', False)
#Set the global information dictionnary
self._info['dir'], self._info['bins'], self._info['table'] = histogram(dir, var, bins, nsector, normed, blowto)
return bins, nbins, nsector, colors, angles, kwargs
def contour(self, dir, var, **kwargs):
"""
Plot a windrose in linear mode. For each var bins, a line will be
draw on the axes, a segment between each sector (center to center).
Each line can be formated (color, width, ...) like with standard plot
pylab command.
Mandatory:
* dir : 1D array - directions the wind blows from, North centred
* var : 1D array - values of the variable to compute. Typically the wind
speeds
Optional:
* nsector: integer - number of sectors used to compute the windrose
table. If not set, nsectors=16, then each sector will be 360/16=22.5°,
and the resulting computed table will be aligned with the cardinals
points.
* bins : 1D array or integer- number of bins, or a sequence of
bins variable. If not set, bins=6, then
bins=linspace(min(var), max(var), 6)
* blowto : bool. If True, the windrose will be pi rotated,
to show where the wind blow to (usefull for pollutant rose).
* colors : string or tuple - one string color ('k' or 'black'), in this
case all bins will be plotted in this color; a tuple of matplotlib
color args (string, float, rgb, etc), different levels will be plotted
in different colors in the order specified.
* cmap : a cm Colormap instance from matplotlib.cm.
- if cmap == None and colors == None, a default Colormap is used.
others kwargs : see help(pylab.plot)
"""
bins, nbins, nsector, colors, angles, kwargs = self._init_plot(dir, var,
**kwargs)
#closing lines
angles = np.hstack((angles, angles[-1]-2*np.pi/nsector))
vals = np.hstack((self._info['table'],
np.reshape(self._info['table'][:,0],
(self._info['table'].shape[0], 1))))
offset = 0
for i in range(nbins):
val = vals[i,:] + offset
offset += vals[i, :]
zorder = ZBASE + nbins - i
patch = self.plot(angles, val, color=colors[i], zorder=zorder,
**kwargs)
self.patches_list.extend(patch)
self._update()
def contourf(self, dir, var, **kwargs):
"""
Plot a windrose in filled mode. For each var bins, a line will be
draw on the axes, a segment between each sector (center to center).
Each line can be formated (color, width, ...) like with standard plot
pylab command.
Mandatory:
* dir : 1D array - directions the wind blows from, North centred
* var : 1D array - values of the variable to compute. Typically the wind
speeds
Optional:
* nsector: integer - number of sectors used to compute the windrose
table. If not set, nsectors=16, then each sector will be 360/16=22.5°,
and the resulting computed table will be aligned with the cardinals
points.
* bins : 1D array or integer- number of bins, or a sequence of
bins variable. If not set, bins=6, then
bins=linspace(min(var), max(var), 6)
* blowto : bool. If True, the windrose will be pi rotated,
to show where the wind blow to (usefull for pollutant rose).
* colors : string or tuple - one string color ('k' or 'black'), in this
case all bins will be plotted in this color; a tuple of matplotlib
color args (string, float, rgb, etc), different levels will be plotted
in different colors in the order specified.
* cmap : a cm Colormap instance from matplotlib.cm.
- if cmap == None and colors == None, a default Colormap is used.
others kwargs : see help(pylab.plot)
"""
bins, nbins, nsector, colors, angles, kwargs = self._init_plot(dir, var,
**kwargs)
null = kwargs.pop('facecolor', None)
null = kwargs.pop('edgecolor', None)
#closing lines
angles = np.hstack((angles, angles[-1]-2*np.pi/nsector))
vals = np.hstack((self._info['table'],
np.reshape(self._info['table'][:,0],
(self._info['table'].shape[0], 1))))
offset = 0
for i in range(nbins):
val = vals[i,:] + offset
offset += vals[i, :]
zorder = ZBASE + nbins - i
xs, ys = poly_between(angles, 0, val)
patch = self.fill(xs, ys, facecolor=colors[i],
edgecolor=colors[i], zorder=zorder, **kwargs)
self.patches_list.extend(patch)
def bar(self, dir, var, **kwargs):
"""
Plot a windrose in bar mode. For each var bins and for each sector,
a colored bar will be draw on the axes.
Mandatory:
* dir : 1D array - directions the wind blows from, North centred
* var : 1D array - values of the variable to compute. Typically the wind
speeds
Optional:
* nsector: integer - number of sectors used to compute the windrose
table. If not set, nsectors=16, then each sector will be 360/16=22.5°,
and the resulting computed table will be aligned with the cardinals
points.
* bins : 1D array or integer- number of bins, or a sequence of
bins variable. If not set, bins=6 between min(var) and max(var).
* blowto : bool. If True, the windrose will be pi rotated,
to show where the wind blow to (usefull for pollutant rose).
* colors : string or tuple - one string color ('k' or 'black'), in this
case all bins will be plotted in this color; a tuple of matplotlib
color args (string, float, rgb, etc), different levels will be plotted
in different colors in the order specified.
* cmap : a cm Colormap instance from matplotlib.cm.
- if cmap == None and colors == None, a default Colormap is used.
edgecolor : string - The string color each edge bar will be plotted.
Default : no edgecolor
* opening : float - between 0.0 and 1.0, to control the space between
each sector (1.0 for no space)
"""
bins, nbins, nsector, colors, angles, kwargs = self._init_plot(dir, var,
**kwargs)
null = kwargs.pop('facecolor', None)
edgecolor = kwargs.pop('edgecolor', None)
if edgecolor is not None:
if not isinstance(edgecolor, str):
raise ValueError('edgecolor must be a string color')
opening = kwargs.pop('opening', None)
if opening is None:
opening = 0.8
dtheta = 2*np.pi/nsector
opening = dtheta*opening
for j in range(nsector):
offset = 0
for i in range(nbins):
if i > 0:
offset += self._info['table'][i-1, j]
val = self._info['table'][i, j]
zorder = ZBASE + nbins - i
patch = Rectangle((angles[j]-opening/2, offset), opening, val,
facecolor=colors[i], edgecolor=edgecolor, zorder=zorder,
**kwargs)
self.add_patch(patch)
if j == 0:
self.patches_list.append(patch)
self._update()
def box(self, dir, var, **kwargs):
"""
Plot a windrose in proportional bar mode. For each var bins and for each
sector, a colored bar will be draw on the axes.
Mandatory:
* dir : 1D array - directions the wind blows from, North centred
* var : 1D array - values of the variable to compute. Typically the wind
speeds
Optional:
* nsector: integer - number of sectors used to compute the windrose
table. If not set, nsectors=16, then each sector will be 360/16=22.5°,
and the resulting computed table will be aligned with the cardinals
points.
* bins : 1D array or integer- number of bins, or a sequence of
bins variable. If not set, bins=6 between min(var) and max(var).
* blowto : bool. If True, the windrose will be pi rotated,
to show where the wind blow to (usefull for pollutant rose).
* colors : string or tuple - one string color ('k' or 'black'), in this
case all bins will be plotted in this color; a tuple of matplotlib
color args (string, float, rgb, etc), different levels will be plotted
in different colors in the order specified.
* cmap : a cm Colormap instance from matplotlib.cm.
- if cmap == None and colors == None, a default Colormap is used.
edgecolor : string - The string color each edge bar will be plotted.
Default : no edgecolor
"""
bins, nbins, nsector, colors, angles, kwargs = self._init_plot(dir, var,
**kwargs)
null = kwargs.pop('facecolor', None)
edgecolor = kwargs.pop('edgecolor', None)
if edgecolor is not None:
if not isinstance(edgecolor, str):
raise ValueError('edgecolor must be a string color')
opening = np.linspace(0.0, np.pi/16, nbins)
for j in range(nsector):
offset = 0
for i in range(nbins):
if i > 0:
offset += self._info['table'][i-1, j]
val = self._info['table'][i, j]
zorder = ZBASE + nbins - i
patch = Rectangle((angles[j]-opening[i]/2, offset), opening[i],
val, facecolor=colors[i], edgecolor=edgecolor,
zorder=zorder, **kwargs)
self.add_patch(patch)
if j == 0:
self.patches_list.append(patch)
self._update()
def histogram(dir, var, bins, nsector, normed=False, blowto=False):
"""
Returns an array where, for each sector of wind
(centred on the north), we have the number of time the wind comes with a
particular var (speed, polluant concentration, ...).
* dir : 1D array - directions the wind blows from, North centred
* var : 1D array - values of the variable to compute. Typically the wind
speeds
* bins : list - list of var category against we're going to compute the table
* nsector : integer - number of sectors
* normed : boolean - The resulting table is normed in percent or not.
* blowto : boolean - Normaly a windrose is computed with directions
as wind blows from. If true, the table will be reversed (usefull for
pollutantrose)
"""
if len(var) != len(dir):
raise ValueError, "var and dir must have same length"
angle = 360./nsector
dir_bins = np.arange(-angle/2 ,360.+angle, angle, dtype=np.float)
dir_edges = dir_bins.tolist()
dir_edges.pop(-1)
dir_edges[0] = dir_edges.pop(-1)
dir_bins[0] = 0.
var_bins = bins.tolist()
var_bins.append(np.inf)
if blowto:
dir = dir + 180.
dir[dir>=360.] = dir[dir>=360.] - 360
table = histogram2d(x=var, y=dir, bins=[var_bins, dir_bins],
normed=False)[0]
# add the last value to the first to have the table of North winds
table[:,0] = table[:,0] + table[:,-1]
# and remove the last col
table = table[:, :-1]
if normed:
table = table*100/table.sum()
return dir_edges, var_bins, table
def wrcontour(dir, var, **kwargs):
fig = plt.figure()
rect = [0.1, 0.1, 0.8, 0.8]
ax = WindroseAxes(fig, rect)
fig.add_axes(ax)
ax.contour(dir, var, **kwargs)
l = ax.legend(axespad=-0.10)
plt.setp(l.get_texts(), fontsize=8)
plt.draw()
plt.show()
return ax
def wrcontourf(dir, var, **kwargs):
fig = plt.figure()
rect = [0.1, 0.1, 0.8, 0.8]
ax = WindroseAxes(fig, rect)
fig.add_axes(ax)
ax.contourf(dir, var, **kwargs)
l = ax.legend(axespad=-0.10)
plt.setp(l.get_texts(), fontsize=8)
plt.draw()
plt.show()
return ax
def wrbox(dir, var, **kwargs):
fig = plt.figure()
rect = [0.1, 0.1, 0.8, 0.8]
ax = WindroseAxes(fig, rect)
fig.add_axes(ax)
ax.box(dir, var, **kwargs)
l = ax.legend(axespad=-0.10)
plt.setp(l.get_texts(), fontsize=8)
plt.draw()
plt.show()
return ax
def wrbar(dir, var, **kwargs):
fig = plt.figure()
rect = [0.1, 0.1, 0.8, 0.8]
ax = WindroseAxes(fig, rect)
fig.add_axes(ax)
ax.bar(dir, var, **kwargs)
l = ax.legend(axespad=-0.10)
plt.setp(l.get_texts(), fontsize=8)
plt.draw()
plt.show()
return ax
def clean(dir, var):
'''
Remove masked values in the two arrays, where if a direction data is masked,
the var data will also be removed in the cleaning process (and vice-versa)
'''
dirmask = dir.mask==False
varmask = var.mask==False
ind = dirmask*varmask
return dir[ind], var[ind]
if __name__=='__main__':
from pylab import figure, show, setp, random, grid, draw
vv=random(500)*6
dv=random(500)*360
fig = figure(figsize=(8, 8), dpi=80, facecolor='w', edgecolor='w')
rect = [0.1, 0.1, 0.8, 0.8]
ax = WindroseAxes(fig, rect, axisbg='w')
fig.add_axes(ax)
# ax.contourf(dv, vv, bins=np.arange(0,8,1), cmap=cm.hot)
# ax.contour(dv, vv, bins=np.arange(0,8,1), colors='k')
# ax.bar(dv, vv, normed=True, opening=0.8, edgecolor='white')
ax.box(dv, vv, normed=True)
l = ax.legend(axespad=-0.10)
setp(l.get_texts(), fontsize=8)
draw()
#print ax._info
show()
sys.argv is the list of command-line arguments. sys.argv[0] is the command itself, sys.argv[1] is the first argument, etc. If there were no arguments, just the command, sys.argv will have only one element, sys.argv[0], and trying to read sys.argv[1] is out of range.
Normally, if your program requires an argument, you check for this and print a usage message:
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.stderr.write("Usage: {0} <data-path>\n".format(sys.argv[0]))
sys.exit(1)
main(sys.argv[1])
Sigh, I know this is similar to several other questions, but my issue here is that I don't quite understand how the specgram function works in Matlab. I'm basing my code off this example located at http://matplotlib.org/examples/pylab_examples/specgram_demo.html :
#!/usr/bin/env python
from pylab import *
dt = 0.0005
t = arange(0.0, 20.0, dt)
s1 = sin(2*pi*100*t)
s2 = 2*sin(2*pi*400*t)
# create a transient "chirp"
mask = where(logical_and(t>10, t<12), 1.0, 0.0)
s2 = s2 * mask
# add some noise into the mix
nse = 0.01*randn(len(t))
x = s1 + s2 + nse # the signal
NFFT = 1024 # the length of the windowing segments
Fs = int(1.0/dt) # the sampling frequency
# Pxx is the segments x freqs array of instantaneous power, freqs is
# the frequency vector, bins are the centers of the time bins in which
# the power is computed, and im is the matplotlib.image.AxesImage
# instance
ax1 = subplot(211)
plot(t, x)
subplot(212, sharex=ax1)
Pxx, freqs, bins, im = specgram(x, NFFT=NFFT, Fs=Fs, noverlap=900,
cmap=cm.gist_heat)
show()
However, I need to read in a wav file, thus I change it to:
#!/usr/bin/env python
from pylab import *
import scipy.io.wavfile
dt = 0.0005
sr, x = scipy.io.wavfile.read(fname) # the signal
NFFT = 1024 # the length of the windowing segments
Fs = int(1.0/dt) # the sampling frequency
# Pxx is the segments x freqs array of instantaneous power, freqs is
# the frequency vector, bins are the centers of the time bins in which
# the power is computed, and im is the matplotlib.image.AxesImage
# instance
ax1 = subplot(211)
plot(x)
subplot(212, sharex=ax1)
Pxx, freqs, bins, im = specgram(x, NFFT=NFFT, Fs=Fs, noverlap=900,
cmap=cm.gist_heat)
show()
and in return I receive ValueError: operands could not be broadcast together with shapes (1024,) (1024,2). What do I not understand here?
Edit: full traceback:
ValueError Traceback (most recent call last)
<ipython-input-13-4952b82b74f2> in <module>()
16 subplot(212, sharex=ax1)
17 Pxx, freqs, bins, im = specgram(x, NFFT=NFFT, Fs=Fs, noverlap=900,
---> 18 cmap=cm.gist_heat)
19 show()
20
/Library/Python/2.7/site-packages/matplotlib-1.3.1-py2.7-macosx-10.8-x86_64.egg/matplotlib/pyplot.pyc in specgram(x, NFFT, Fs, Fc, detrend, window, noverlap, cmap, xextent, pad_to, sides, scale_by_freq, hold, **kwargs)
3145 window=window, noverlap=noverlap, cmap=cmap,
3146 xextent=xextent, pad_to=pad_to, sides=sides,
-> 3147 scale_by_freq=scale_by_freq, **kwargs)
3148 draw_if_interactive()
3149 finally:
/Library/Python/2.7/site-packages/matplotlib-1.3.1-py2.7-macosx-10.8-x86_64.egg/matplotlib/axes.pyc in specgram(self, x, NFFT, Fs, Fc, detrend, window, noverlap, cmap, xextent, pad_to, sides, scale_by_freq, **kwargs)
8932
8933 Pxx, freqs, bins = mlab.specgram(x, NFFT, Fs, detrend,
-> 8934 window, noverlap, pad_to, sides, scale_by_freq)
8935
8936 Z = 10. * np.log10(Pxx)
/Library/Python/2.7/site-packages/matplotlib-1.3.1-py2.7-macosx-10.8-x86_64.egg/matplotlib/mlab.pyc in specgram(x, NFFT, Fs, detrend, window, noverlap, pad_to, sides, scale_by_freq)
467
468 Pxx, freqs, t = _spectral_helper(x, x, NFFT, Fs, detrend, window,
--> 469 noverlap, pad_to, sides, scale_by_freq)
470 Pxx = Pxx.real #Needed since helper implements generically
471
/Library/Python/2.7/site-packages/matplotlib-1.3.1-py2.7-macosx-10.8-x86_64.egg/matplotlib/mlab.pyc in _spectral_helper(x, y, NFFT, Fs, detrend, window, noverlap, pad_to, sides, scale_by_freq)
262 for i in range(n):
263 thisX = x[ind[i]:ind[i]+NFFT]
--> 264 thisX = windowVals * detrend(thisX)
265 fx = np.fft.fft(thisX, n=pad_to)
266
ValueError: operands could not be broadcast together with shapes (1024,) (1024,2)
This is not an issue with the code: rather, it is an issue with the wavfile. The file has two audio channels, thus the 2nd row in the data.