|
From: Paul T. <pau...@gm...> - 2012-10-09 03:03:49
|
I often have to make graphs with time series, with each point being the
start of a week. Below is the result I wish:
However, in order to make the secondary x axis the the month labels, I
need something like 40 lines of code. My strategy consists in first
drawing the rates in the normal way and using matplotlib's dateFormatter
to set the week numbers. That part is easy.
In order to draw the x axis below to show when the weeks occur, I draw
two more lines, and makes these lines invisible. The first line has as
the x points the start of each month, and the y values as the
corresponding rate. The second line has as its x value the middle of
each month, with the corresponding y value.
Below is my code. I have to use such a graph quite often--so often, in
fact, that if I don't find an easier way to make the secondary x axis, I
will store the code in my own library for re-use. Does anyone know of a
simpler, more elegant way?
Thanks!
Paul
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates
from datetime import datetime
from datetime import timedelta
def unique_months(dates, rates):
"""
returns two lists, one of the first time a month occurs, the
second, the
rates representing that month
"""
y = [rates[0]]
temp = dates[0] - timedelta(days=dates[0].day)
months = [temp]
found = [dates[0].month]
counter = 0
for d in dates:
month = d.month
if month not in found:
new_date = d - timedelta(days=d.day)
months.append(new_date)
y.append(rates[counter])
found.append(month)
counter += 1
return (months, y)
def half_months(dates, defects):
"""
returns two lists, one the middle of the month occurs, the second, the
rates representing that month
"""
months, y = unique_months(dates, defects)
new_months =[]
new_y = []
counter = 0
for m in months:
new_date = m - timedelta(days=m.day) + timedelta(days=15)
if new_date <= months[-1]:
new_months.append(new_date)
new_y.append(y[counter])
counter += 1
return new_months, new_y
dates = [datetime(2012,8,19), datetime(2012,8,26),datetime(2012, 9, 3),
datetime(2012,9,10), datetime(2012,9,17), datetime(2012,9,24),
datetime(2012,10,1), datetime(2012,10,8)]
rates = [2,3,4,2,5,3,7,2]
fig = plt.figure()
fig.set_size_inches(3,3)
x1, y1 = unique_months(dates, rates)
ax = fig.add_subplot(1,1,1)
ax.yaxis.grid(True, linestyle='-', which='major', color='lightgrey',
alpha=0.5)
ax.set_axisbelow(True)
fig.subplots_adjust(bottom=0.2, right=0.85, wspace=.8, hspace=.8)
# plot dates and rates
ax.plot(dates, rates)
ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%W'))
# start with bottom axis
x2, y2 = half_months(dates, rates)
# add tick marks for start of month
# x1 starts at first of each mont; y1 doesn't matter
newax = fig.add_axes(ax.get_position())
newax.spines['bottom'].set_position(('outward', 25))
newax.patch.set_visible(False)
newax.yaxis.set_visible(False)
newax.plot_date(x1, y1, visible=False)
newax.xaxis.set_major_locator( matplotlib.dates.MonthLocator())
newax.set_xticklabels([])
# set labels for months, inbetween tick marsk
# x2 is 15th of each month
axmlab = fig.add_axes(ax.get_position())
axmlab.spines['bottom'].set_position(('outward', 25))
axmlab.patch.set_visible(False)
axmlab.yaxis.set_visible(False)
axmlab.plot_date(x2, y2, visible=False)
axmlab.xaxis.set_major_formatter( matplotlib.dates.DateFormatter('%b'))
axmlab.xaxis.set_major_locator(matplotlib.dates.DayLocator(bymonthday=1))
# get rid of tick marks for this axis
for i, line in enumerate(axmlab.get_xticklines() + newax.get_yticklines()):
line.set_visible(False)
axmlab.plot_date(x2, y2, visible=False)
axmlab.xaxis.set_major_locator( matplotlib.dates.MonthLocator())
plt.xlabel('Week in Year')
ax.set_ylabel('Rates')
plt.savefig('temp.png', dpi=100)
|
|
From: Mike K. <mc...@gm...> - 2012-10-09 03:46:05
|
On 10/8/12 11:03 PM, Paul Tremblay wrote: > I often have to make graphs with time series, with each point being the > start of a week. Below is the result I wish: > > However, in order to make the secondary x axis the the month labels, I > need something like 40 lines of code. My strategy consists in first > drawing the rates in the normal way and using matplotlib's dateFormatter > to set the week numbers. That part is easy. > > In order to draw the x axis below to show when the weeks occur, I draw > two more lines, and makes these lines invisible. The first line has as > the x points the start of each month, and the y values as the > corresponding rate. The second line has as its x value the middle of > each month, with the corresponding y value. > > Below is my code. I have to use such a graph quite often--so often, in > fact, that if I don't find an easier way to make the secondary x axis, I > will store the code in my own library for re-use. Does anyone know of a > simpler, more elegant way? > Don't have time to prove that I can reproduce what you want, but try parasite axes with axes_grid1: the example is for one on the right-hand side, but I think it'll do what you want. http://matplotlib.org/mpl_toolkits/axes_grid/users/overview.html#axisartist-with-parasiteaxes M |
|
From: Paul T. <pau...@gm...> - 2012-10-11 00:53:11
|
A bit of searching gave me this much simpler solution:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import datetime
import matplotlib.dates as m_dates
import matplotlib.ticker as ticker
def make_month_axis(dates, y, ax, fig):
newax = fig.add_axes(ax.get_position())
newax.spines['bottom'].set_position(('outward', 25))
newax.patch.set_visible(False)
newax.yaxis.set_visible(False)
newax.plot_date(dates, y, visible=False)
newax.xaxis.set_major_locator(m_dates.MonthLocator())
newax.xaxis.set_minor_locator(m_dates.MonthLocator(bymonthday=15))
newax.xaxis.set_major_formatter(ticker.NullFormatter())
newax.xaxis.set_minor_formatter(m_dates.DateFormatter('%b'))
for tick in newax.xaxis.get_minor_ticks():
tick.tick1line.set_markersize(0)
tick.tick2line.set_markersize(0)
tick.label1.set_horizontalalignment('center')
start = datetime.datetime(2012, 8, 26)
dates = [start]
for i in range(1,10):
dates.append(dates[-1] + datetime.timedelta(days=7))
y = np.random.normal(10, 5, len(dates))
fig = plt.figure()
fig.subplots_adjust(bottom=0.2, right=0.85, wspace=.8, hspace=.8)
ax = fig.add_subplot(1,1,1)
ax.plot(dates, y)
ax.xaxis.set_major_formatter( matplotlib.dates.DateFormatter('%W'))
make_month_axis(dates = dates, y = y, ax = ax, fig = fig)
plt.show()
Paul
On 10/8/12 11:03 PM, Paul Tremblay wrote:
> I often have to make graphs with time series, with each point being
> the start of a week. Below is the result I wish:
>
> However, in order to make the secondary x axis the the month labels, I
> need something like 40 lines of code. My strategy consists in first
> drawing the rates in the normal way and using matplotlib's
> dateFormatter to set the week numbers. That part is easy.
>
> In order to draw the x axis below to show when the weeks occur, I draw
> two more lines, and makes these lines invisible. The first line has as
> the x points the start of each month, and the y values as the
> corresponding rate. The second line has as its x value the middle of
> each month, with the corresponding y value.
>
> Below is my code. I have to use such a graph quite often--so often, in
> fact, that if I don't find an easier way to make the secondary x axis,
> I will store the code in my own library for re-use. Does anyone know
> of a simpler, more elegant way?
>
> Thanks!
>
> Paul
>
> import matplotlib
> import matplotlib.pyplot as plt
> import numpy as np
> import matplotlib.dates
> from datetime import datetime
> from datetime import timedelta
>
>
> def unique_months(dates, rates):
> """
> returns two lists, one of the first time a month occurs, the
> second, the
> rates representing that month
>
> """
> y = [rates[0]]
> temp = dates[0] - timedelta(days=dates[0].day)
> months = [temp]
> found = [dates[0].month]
> counter = 0
> for d in dates:
> month = d.month
> if month not in found:
> new_date = d - timedelta(days=d.day)
> months.append(new_date)
> y.append(rates[counter])
> found.append(month)
> counter += 1
> return (months, y)
>
> def half_months(dates, defects):
> """
> returns two lists, one the middle of the month occurs, the second, the
> rates representing that month
>
> """
> months, y = unique_months(dates, defects)
> new_months =[]
> new_y = []
> counter = 0
> for m in months:
> new_date = m - timedelta(days=m.day) + timedelta(days=15)
> if new_date <= months[-1]:
> new_months.append(new_date)
> new_y.append(y[counter])
> counter += 1
> return new_months, new_y
>
> dates = [datetime(2012,8,19), datetime(2012,8,26),datetime(2012, 9,
> 3), datetime(2012,9,10), datetime(2012,9,17), datetime(2012,9,24),
> datetime(2012,10,1), datetime(2012,10,8)]
> rates = [2,3,4,2,5,3,7,2]
> fig = plt.figure()
> fig.set_size_inches(3,3)
> x1, y1 = unique_months(dates, rates)
> ax = fig.add_subplot(1,1,1)
> ax.yaxis.grid(True, linestyle='-', which='major', color='lightgrey',
> alpha=0.5)
> ax.set_axisbelow(True)
> fig.subplots_adjust(bottom=0.2, right=0.85, wspace=.8, hspace=.8)
> # plot dates and rates
> ax.plot(dates, rates)
> ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%W'))
>
> # start with bottom axis
>
> x2, y2 = half_months(dates, rates)
>
> # add tick marks for start of month
> # x1 starts at first of each mont; y1 doesn't matter
> newax = fig.add_axes(ax.get_position())
> newax.spines['bottom'].set_position(('outward', 25))
> newax.patch.set_visible(False)
> newax.yaxis.set_visible(False)
> newax.plot_date(x1, y1, visible=False)
> newax.xaxis.set_major_locator( matplotlib.dates.MonthLocator())
> newax.set_xticklabels([])
>
> # set labels for months, inbetween tick marsk
> # x2 is 15th of each month
> axmlab = fig.add_axes(ax.get_position())
> axmlab.spines['bottom'].set_position(('outward', 25))
> axmlab.patch.set_visible(False)
> axmlab.yaxis.set_visible(False)
> axmlab.plot_date(x2, y2, visible=False)
> axmlab.xaxis.set_major_formatter( matplotlib.dates.DateFormatter('%b'))
> axmlab.xaxis.set_major_locator(matplotlib.dates.DayLocator(bymonthday=1))
> # get rid of tick marks for this axis
> for i, line in enumerate(axmlab.get_xticklines() +
> newax.get_yticklines()):
> line.set_visible(False)
> axmlab.plot_date(x2, y2, visible=False)
> axmlab.xaxis.set_major_locator( matplotlib.dates.MonthLocator())
>
> plt.xlabel('Week in Year')
> ax.set_ylabel('Rates')
> plt.savefig('temp.png', dpi=100)
>
>
|