How do I plot only weekdays using Python's matplotlib candlestick? - python

I'm not managing to plot matplotlib.finance.candlestick without the weekends (blank spaces between every 5 candlesticks). The example from Matplotlib's website doesn't exclude weekends either and the way to exclude weekends on other plots doesn't seem to apply to CandleSticks.
Has anybody come across this before?
ps. as requested, here is the example:
#!/usr/bin/env python
from pylab import *
from matplotlib.dates import DateFormatter, WeekdayLocator, HourLocator, \
DayLocator, MONDAY
from matplotlib.finance import quotes_historical_yahoo, candlestick,\
plot_day_summary, candlestick2
# (Year, month, day) tuples suffice as args for quotes_historical_yahoo
date1 = ( 2004, 2, 1)
date2 = ( 2004, 4, 12 )
mondays = WeekdayLocator(MONDAY) # major ticks on the mondays
alldays = DayLocator() # minor ticks on the days
weekFormatter = DateFormatter('%b %d') # Eg, Jan 12
dayFormatter = DateFormatter('%d') # Eg, 12
quotes = quotes_historical_yahoo('INTC', date1, date2)
fig = figure()
fig.subplots_adjust(bottom=0.2)
ax = fig.add_subplot(111)
ax.xaxis.set_major_locator(mondays)
ax.xaxis.set_minor_locator(alldays)
ax.xaxis.set_major_formatter(weekFormatter)
#plot_day_summary(ax, quotes, ticksize=3)
candlestick(ax, quotes, width=0.6)
ax.xaxis_date()
ax.autoscale_view()
setp( gca().get_xticklabels(), rotation=45, horizontalalignment='right')
show()

After your 'quotes' line:
weekday_quotes = [tuple([i]+list(quote[1:])) for i,quote in enumerate(quotes)]
then
candlestick(ax, weekday_quotes, width=0.6)
This will plot the data without the gaps between weekdays, now you have to change the xticks back to dates, preferably mondays. Assuming your first quote was a monday:
import matplotlib.dates as mdates
ax.set_xticks(range(0,len(weekday_quotes),5))
ax.set_xticklabels([mdates.num2date(quotes[index][0]).strftime('%b-%d') for index in ax.get_xticks()])
This is pretty gross but seems to get the job done - good luck!

While #JMJR's answer works, I find this to be more robust:
def plot(x):
plt.figure()
plt.title("VIX curve")
def idx(val=[0]):
val[0] = val[0] + 1
return val[0]
d = collections.defaultdict(idx)
# give each date an index
[d[t] for t in sorted(x.index.get_level_values('baropen_datetime').unique())]
# use the index
x['idx'] = [d[t] for t in x.index.get_level_values('baropen_datetime')]
# plot using index
x.groupby('code').apply(lambda y: plt.plot(y.idx.values,
y.close.values,
label=y.index.get_level_values('code')[0]))
plt.legend()
plt.show()
plt.close()

Related

Display Multiple Year's Data Using Custom Start/End Dates - datetime, matplotlib

I feel like this question has an obvious answer and I'm just being a bit of a fool. Say you have a couple of dataframes with datetime indices, where each dataframe is for a different year. In my case the index is every day going from June 25th to June 24th the next year:
date var
2019-06-25 107.230294
2019-06-26 104.110004
2019-06-27 104.291506
2019-06-28 111.162552
2019-06-29 112.515364
...
2020-06-20 132.840242
2020-06-21 127.641148
2020-06-22 132.797584
2020-06-23 129.094451
2020-06-24 110.408866
What I want is a single plot with multiple lines, where each line represents a year. The y-axis is my variable, var, and the x-axis should be day of the year. The x-axis should start from June 25th and end at June 24th.
This is what I've tried so far but it messes up the x-axis. Anyone know a more elegant way to do this?
fig, ax = plt.subplots()
plt.plot(average_prices19.index.strftime("%d/%m"), average_prices19.var, label = "2019-20")
plt.plot(average_prices20.index.strftime("%d/%m"), average_prices20.var, label = "2020-21")
plt.legend()
plt.show()
Well, there is a twist in this question: the list of dates in a year is not constant: on leap years there is a 'Feb-29' that is otherwise absent.
If you are comfortable glossing over this (and always representing a potential 'Feb-29' date on your plot, with missing data for non-leap years), then the following will achieve what you are seeking (assuming the data is in df with the date as DateTimeIndex):
import matplotlib.dates as mdates
fig, ax = plt.subplots()
for label, dfy in df.assign(
# note: 2000 is a leap year; the choice is deliberate
date=pd.to_datetime(df.index.strftime('2000-%m-%d')),
label=df.index.strftime('%Y')
).groupby('label'):
dfy.set_index('date')['var'].plot(ax=ax, label=str(label))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%m-%d"))
ax.legend()
Update
For larger amounts of data however, the above does not produce very legible xlabels. So instead, we can use ConciseFormatter to customize the display of xlabels (and remove the fake year 2000):
import matplotlib.dates as mdates
fig, ax = plt.subplots()
for label, dfy in df.assign(
# note: 2000 is a leap year; the choice is deliberate
date=pd.to_datetime(df.index.strftime('2000-%m-%d')),
label=df.index.strftime('%Y')
).groupby('label'):
dfy.set_index('date')['var'].plot(ax=ax, label=str(label))
ax.legend()
locator = mdates.AutoDateLocator(minticks=3, maxticks=7)
formatter = mdates.ConciseDateFormatter(
locator,
formats=['', '%b', '%d', '%H:%M', '%H:%M', '%S.%f'],
offset_formats=['', '', '%b', '%b-%d', '%b-%d', '%b-%d %H:%M']
)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
For the data in your example:
For more data:
# setup
idx = pd.date_range('2016-01-01', 'now', freq='QS')
df = pd.DataFrame(
{'var': np.random.uniform(size=len(idx))},
index=idx).resample('D').interpolate(method='polynomial', order=5)
Corresponding plot (with ConciseFormatter):

How to convert DOY (Day of year) to months (as text) in a plot?

I have plots of climate time series for daily mean temperature, precipitation and global radiation.
I generated plots like this:
https://i.ibb.co/w4x2FMN/temp-mean-1999-2018.png
On x-axis I just generated list of the numbers 1 - 365 which represent the day of year (DOY).
What I actually want is, that the x-axis is devided in month names (as strings) like this:
https://i.ibb.co/cL2zc87/rplot.jpg
I tried already a lot of different things but nothing worked.
fig = plt.figure(figsize=(10,10))
ax = plt.axes()
x = np.arange(1,366) # here I define the List with DOY
ax.fill_between(x, temp_cum['min'], temp_cum['max'], color='lightgray', label='1999-2017')
#ax.plot(x, merge_table_99_17_without, color='grey', linewidth=0.3)
ax.plot(x, temp_cum['2018'], color='black', label='2018');
ax.legend(loc='upper left')
ax.set_ylabel('daily mean temperature [°C]')
#ax.set_xlabel('DOY')
plt.show()
First you should convert your numbers to date objects as described in this post. You can use the following function.
import datetime
def serial_date_to_string(srl_no):
new_date = datetime.datetime(2018,1,1,0,0) + datetime.timedelta(srl_no - 1)
return new_date.strftime("%Y-%m-%d")
Then you have to format your x-axis to only show the month and not the full dates. This post describes how to do this in detail.
Thank you very much #AUBSieGUL.
Your second link finally helped me:
import numpy as np
import matplotlib.pyplot as plt
import datetime
import matplotlib.dates as mdates
fig = plt.figure(figsize=(12,12))
ax = plt.axes()
### I added this!
# Set the locator
locator = mdates.MonthLocator() # every month
# Specify the format - %b gives us Jan, Feb...
fmt = mdates.DateFormatter('%b')
numdays = 365
base = datetime.datetime(2018, 1, 1, 0, 0, 0, 0)
date_list = [base + datetime.timedelta(days=x) for x in range(0,numdays)]
###
###replaced all x with date_list
ax.fill_between(date_list, prec_cum['min'], prec_cum['max'], color='lightgray', label='1999-2017')
ax.plot(date_list, merge_table_99_17_cumsum_without, color='grey', linewidth=0.3)
ax.plot(date_list, prec_cum['2018'], color='black', label='2018');
ax.legend(loc='upper left')
ax.set_ylabel('cum. sums of global radiation [kW/m²]')
#ax.set_xlabel('DOY')
### I added this!
X = plt.gca().xaxis
X.set_major_locator(locator)
# Specify formatter
X.set_major_formatter(fmt)
###
plt.show()

Adding info over graph obtained properly

I was able to run the "mpl_finance" candlestick_ohlc function and the graph appeared as expected, using the following (only relevant) code:
mondays = WeekdayLocator(MONDAY) # major ticks on the mondays
alldays = DayLocator() # minor ticks on the days
weekFormatter = DateFormatter('%b %d') # e.g., Jan 12
dayFormatter = DateFormatter('%d') # e.g., 12
fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2)
ax.xaxis.set_major_locator(mondays)
ax.xaxis.set_minor_locator(alldays)
ax.xaxis.set_major_formatter(weekFormatter)
candlestick_ohlc(ax, zip(mdates.date2num(quotes.index.to_pydatetime()),
quotes['open'], quotes['high'],
quotes['low'], quotes['close']),
width=0.6)
ax.xaxis_date()
ax.autoscale_view()
plt.setp(plt.gca().get_xticklabels(), rotation=45, horizontalalignment='right')
plt.title('PETR4 daily quotes')
plt.show()
Now I would like to "add" on this graph (say) a horizontal red line at y = 26.5 ... how should I proceed?
(My real question is: how/where should I type something like axvline(...) so that I am able to make new data appear inside the same graph?)
Thanks!
Sure, DavidG. Thanks again for your help. Hope to see you in other posts.
The interested readers will be able to adapt this "real stuff" below (it´s working)!
mondays = WeekdayLocator(MONDAY) # major ticks on the mondays
alldays = DayLocator() # minor ticks on the days
weekFormatter = DateFormatter('%b %d') # e.g., Jan 12
dayFormatter = DateFormatter('%d') # e.g., 12
fig, aux = plt.subplots()
fig.subplots_adjust(bottom=0.2)
aux.xaxis.set_major_locator(mondays)
aux.xaxis.set_minor_locator(alldays)
aux.xaxis.set_major_formatter(weekFormatter)
candlestick_ohlc(aux, zip(mdates.date2num(quotes.index.to_pydatetime()),
quotes['open'], quotes['high'],
quotes['low'], quotes['close']),
width=0.6)
for i in range(len(features_period.date)):
plt.plot(quotes.index, quotes.close , 'd', color='blue')
aux.xaxis_date()
aux.autoscale_view()
plt.setp(plt.gca().get_xticklabels(), rotation=45, horizontalalignment='right')
plt.title('USIM5 daily quotes')
plt.rcParams['figure.figsize'] = [10, 10]
display(candlestick_ohlc);
(The blue dots were added to the graph created by the module used/mentioned.)
Regards,
fskilnik

Date into matplotlib graph

How can I use a date from a Sqlite database on the x-axis to make a bar graph with matplotlib?
If I convert the date to unix timestamp the graph works, but I would like to get something like this: http://i.stack.imgur.com/ouKBy.png
lowestNumber = self.c.execute('SELECT number,date, time FROM testDB ORDER BY number ASC LIMIT 1')
for rows in lowestNumber:
datesLow = rows[1]#returns 2016-02-23
splitDate = datesLow.split('-' )
spaces = ""
# tabs = '/'
# tabsDatesLow = tabs.join( splitDate )
joinDatesLow = spaces.join( splitDate )
x = int(joinDatesLow)
plt.bar(x,low, label="Minimum number of players", color="red")
plt.show()
You need to have an integer time format for plotting dates in matplotlib, and then a date formatting object is passed to format the axes. Matplotlib's date2num function can do this for you. Another good example is Matplotlib's documentation with an example here: http://matplotlib.org/examples/pylab_examples/date_demo1.html. Here is a solution yo may find useful:
import datetime
import matplotlib.pyplot as plt
from matplotlib.dates import AutoDateLocator, AutoDateFormatter, date2num
#make my own data:
date = '2016-02-23'
low = 10
#how to format dates:
date_datetime = datetime.datetime.strptime(date, '%Y-%m-%d')
int_date = date2num( date_datetime)
#create plots:
fig, ax = plt.subplots()
#plot data:
ax.bar(int_date,low, label="Minimum number of players", color="red")
#format date strings on xaxis:
locator = AutoDateLocator()
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter( AutoDateFormatter(locator) )
#adjust x limits and apply autoformatter fordisplay of dates
min_date = date2num( datetime.datetime.strptime('2016-02-16', '%Y-%m-%d') )
max_date = date2num( datetime.datetime.strptime('2016-02-28', '%Y-%m-%d') )
ax.set_xlim([min_date, max_date])
fig.autofmt_xdate()
#show plot:
plt.show()

matplotlib Plot X-axis with '%Y-%m-%d %H:%M'

I would like to plot a graph with the below sample, X-axis: 'Time', Y-axis: 'Celcius'. With the attached code, I got [09:00:00.000000 09:05:00.000000 ... 09:30:00.000000] at the x-axis, instead of [2013-01-02 09:00 2013-01-02 09:05 ... 2013-01-02 09:30].
Does anyone know what the correct way to format x-axis to the designated format is?
data = {'Celcius': [36.906441135554658, 51.286294403017202], 'Time': [datetime.datetime(2013, 1, 2, 9, 0), datetime.datetime(2013, 1, 2, 9, 30)]}
def plotTemperature(self, data):
logging.debug(data)
t = data.get('Time')
T = data.get('Celcius')
years = mdates.YearLocator() # every year
months = mdates.MonthLocator() # every month
days = mdates.DayLocator() # every day
hours = mdates.HourLocator() # every hour
minutes = mdates.MinuteLocator() # every minute
yearsFmt = mdates.DateFormatter('%Y')
hoursFmt = mdates.DateFormatter('%H')
fig, ax = plt.subplots()
ax.plot(t, T)
# format the ticks
# ax.xaxis.set_major_locator(hours)
# ax.xaxis.set_major_formatter(hoursFmt)
# ax.xaxis.set_minor_locator(minutes)
datemin = datetime.datetime(min(t).year, min(t).month, min(t).day, min(t).hour, min(t).minute)
datemax = datetime.datetime(max(t).year, max(t).month, max(t).day, max(t).hour, max(t).minute)
ax.set_xlim(datemin, datemax)
# format the coords message box
def temperature(x): return '$%1.2f'%x
ax.format_xdata = mdates.DateFormatter('%Y-%m-%d %H:%M')
ax.format_ydata = temperature
ax.grid(True)
# rotates and right aligns the x labels, and moves the bottom of the
# axes up to make room for them
fig.autofmt_xdate()
plt.show()
Add this:
import dateutil
import matplotlib.dates as mdates
ymdhFmt = mdates.DateFormatter('%Y-%m-%d %H:%M')
rule = mdates.rrulewrapper(dateutil.rrule.MINUTELY, interval=30)
loc = mdates.RRuleLocator(rule)
ax.xaxis.set_major_locator(loc)
ax.xaxis.set_major_formatter(ymdhFmt)
This answer was posted as an edit to the question matplotlib Plot X-axis with '%Y-%m-%d %H:%M' by the OP twfx under CC BY-SA 3.0.

Categories