Annotate something on a matplotlib candlestick chart - python

The following snippet of code is creating a candlestick chart with 4 price bars. The lines of code written between the "NOT WORKING" tags are supposed to annotate the word 'BUY' on the second price bar following the coordinates stored into the variables d (x-axis) and h (y-axis). However, this does not work cause there's no annotation into the chart.
The code below is runnable, can anyone explain me how to make an annotation on a chart like this?
from pylab import *
from matplotlib.finance import candlestick
import matplotlib.gridspec as gridspec
quotes = [(734542.0, 1.326, 1.3287, 1.3322, 1.3215), (734543.0, 1.3286, 1.3198, 1.3292, 1.3155), (734546.0, 1.321, 1.3187, 1.3284, 1.3186), (734547.0, 1.3186, 1.3133, 1.3217, 1.308)]
fig, ax = subplots()
candlestick(ax,quotes,width = 0.5)
ax.xaxis_date()
ax.autoscale_view()
#NOT WORKING
h = quotes[1][3]
d = quotes[1][0]
ax.annotate('BUY', xy = (d-1,h), xytext = (d-1, h+0.5), arrowprops = dict(facecolor='black',width=1,shrink=0.25))
#NOT WORKING
plt.show()
P.S. Embedding the statement print "(", d, ",", h, ")" gives the following output: >>>
( 734543.0 , 1.3292 ). That is exactly the point where I would like to get my arrow, so I guess the problem must be related with the visualization of the arrow and not with its creation.

Your problem is that your arrow is effectively off the matplotlib screen. You have set the xytext position to (d-1, h+0.5) which is way, way off your y-limits. The following fixes your code:
from pylab import *
from matplotlib.finance import candlestick
import matplotlib.gridspec as gridspec
quotes = [(734542.0, 1.326, 1.3287, 1.3322, 1.3215), (734543.0, 1.3286, 1.3198, 1.3292, 1.3155), (734546.0, 1.321, 1.3187, 1.3284, 1.3186), (734547.0, 1.3186, 1.3133, 1.3217, 1.308)]
fig, ax = subplots()
candlestick(ax,quotes,width = 0.5)
ax.xaxis_date()
ax.autoscale_view()
#NOT WORKING
h = quotes[1][3]
d = quotes[1][0]
ax.annotate('BUY', xy = (d-1,h), xytext = (d-1, h+0.003), arrowprops = dict(facecolor='black',width=1,shrink=0.25))
#NOT WORKING
plt.show()

Related

Moving Graph Titles in the Y axis of Subplots

This question is adapted from this answer, however the solution provided does not work and following is my result. I am interested in adding individual title on the right side for individual subgraphs.
(p.s no matter how much offset for y-axis i provide the title seems to stay at the same y-value)
from matplotlib import pyplot as plt
import numpy as np
fig, axes = plt.subplots(nrows=2)
ax0label = axes[0].set_ylabel('Axes 0')
ax1label = axes[1].set_ylabel('Axes 1')
title = axes[0].set_title('Title')
offset = np.array([-0.15, 0.0])
title.set_position(ax0label.get_position() + offset)
title.set_rotation(90)
fig.tight_layout()
plt.show()
Something like this? This is the only other way i can think of.
from matplotlib import pyplot as plt
import numpy as np
fig, axes = plt.subplots(nrows=2)
ax0label = axes[0].set_ylabel('Axes 0')
ax1label = axes[1].set_ylabel('Axes 1')
ax01 = axes[0].twinx()
ax02 = axes[1].twinx()
ax01.set_ylabel('title')
ax02.set_ylabel('title')
fig.tight_layout()
plt.show()

Matplotlib in python: equivalent to fig.set_facecolor in mpl.rcParams?

If I do:
fig = plt.figure()
fig.set_facecolor('#fff1e0')
I change the color of the surrounding margins and the background of the axis tick labels.
As I am specifying everything as rcParams:
mpl.rcParams['font.family'] = 'monospace'
mpl.rcParams['font.size'] = 7
mpl.rcParams['axes.facecolor'] = 'fff1e0'
mpl.rcParams['figure.facecolor'] = 'fff1e0'
mpl.rcParams['figure.edgecolor'] = 'fff1e0'
mpl.rcParams['axes.edgecolor'] = '6595CC'
mpl.rcParams['xtick.color'] = '1869B5'
mpl.rcParams['ytick.color'] = '1869B5'
mpl.rcParams['grid.color'] = 'EAEFF8'
I would like to know which would be the equivalent in rcParams for fig.set_facecolor.
Not sure if relevant but matplotlib is being embbeded in Tkinter.
The Artist Tutorial states that the Figure contains all Axes so you should use mpl.rcParams['figure.facecolor']
The docs for Figure support that :
facecolor : default: rcParams["figure.facecolor"]
The two methods can be tested by commenting/un-commenting the appropriate lines in the following:
from matplotlib import pyplot as plt
import matplotlib as mpl
mpl.rcParams['figure.facecolor'] = 'maroon'
fig = plt.figure()
##fig.set_facecolor('purple')
plt.plot()
plt.show()
plt.close()
# multiple Axes
fig, axs = plt.subplots(2,2)
##fig.set_facecolor('purple')
for ax,c in zip(axs.flatten(),['red','green','blue','yellow']):
ax.set_facecolor(c)
plt.show()
plt.close()
mpl.rcdefaults()
Anatomy of a Figure

matplotlib subplot with dates: cannot change xlim

I am trying to produce a subplot, plotting 3 time series over 2 different axes.
My time series go from Jan-2007 till Sep-2017 (one data point per month).
My problem is that I cannot change the limits of the x axis.
I have tried both xlim and set_xlim, but they have no effect whatsoever.
What am I doing wrong?
An example of my code is below.
import numpy as np
import pandas as pd
import datetime as dt
import dateutil
import matplotlib.pyplot as plt
import matplotlib
import matplotlib.ticker as mtick
mystart = dt.date(2007,1,1)
mydates =[mystart]
for i in np.arange(1,130):
mydates.append( mystart +dateutil.relativedelta.relativedelta(months=i))
df=pd.DataFrame()
df['month']=mydates
df['a']= np.arange(1,131)
df['b']=df['a']/2
df['c']=0.25
fig,ax=plt.subplots(2)
ax[0].set_title('My title')
l1 = ax[0].plot_date( df['month'],df['a'], label= 'a (left axis)', color='blue', ls='solid', marker ='')
l2 = ax[0].plot_date( df['month'],df['b'], label= 'b (left axis)',color='red', ls='solid', marker ='')
# THESE BELOW ARE THE TWO LINES I CANNOT GET TO WORK!!!
#plt.xlim(xmin= dt.date(2012,1,31),xmax=dt.date(2017,9,30))
ax[0].set_xlim([dt.date(2012,1,31),dt.date(2017,9,30)], auto=True)
ax[0].grid()
ax2=ax[0].twinx()
l3 = ax2.plot_date( df['month'],df['c']*100 , label= 'some % (right axis)', color='green', ls='solid', marker ='')
fmt = '%.2f%%' # Format you want the ticks, e.g. '40%'
yticks = mtick.FormatStrFormatter(fmt)
ax2.yaxis.set_major_formatter(yticks)
ls=l1+l2+l3
labs=[l.get_label() for l in ls]
ax[0].legend(ls, labs, loc='upper left')
ax[1].set_title('Something else will go here...')
plt.show()
Set the limits of the axes after you have created the twin axes and plotted to it.
ax2=ax[0].twinx()
l3 = ax2.plot_date( ... )
ax[0].set_xlim( ... )
I got it: the issue is the secondary axis.
I must repeat set_xlim for ax2, as well:
ax2.set_xlim([dt.datetime(2012,1,31),dt.datetime(2017,9,30)], auto=True)
I am not sure if this is a bug, TBH. I would have expected that creating a twin axis would have replicated the same xlim.

Change Error Bar Markers (Caplines) in Pandas Bar Plot

so I am plotting error bar of pandas dataframe. Now the error bar has a weird arrow at the top, but what I want is a horizontal line. For example, a figure like this:
But now my error bar ends with arrow instead of a horinzontal line.
Here is the code i used to generate it:
plot = meansum.plot(
kind="bar",
yerr=stdsum,
colormap="OrRd_r",
edgecolor="black",
grid=False,
figsize=(8, 2),
ax=ax,
position=0.45,
error_kw=dict(ecolor="black", elinewidth=0.5, lolims=True, marker="o"),
width=0.8,
)
So what should I change to make the error become the one I want. Thx.
Using plt.errorbar from matplotlib makes it easier as it returns several objects including the caplines which contain the marker you want to change (the arrow which is automatically used when lolims is set to True, see docs).
Using pandas, you just need to dig the correct line in the children of plot and change its marker:
import pandas as pd
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
df = pd.DataFrame({"val":[1,2,3,4],"error":[.4,.3,.6,.9]})
meansum = df["val"]
stdsum = df["error"]
plot = meansum.plot(kind='bar',yerr=stdsum,colormap='OrRd_r',edgecolor='black',grid=False,figsize=8,2),ax=ax,position=0.45,error_kw=dict(ecolor='black',elinewidth=0.5, lolims=True),width=0.8)
for ch in plot.get_children():
if str(ch).startswith('Line2D'): # this is silly, but it appears that the first Line in the children are the caplines...
ch.set_marker('_')
ch.set_markersize(10) # to change its size
break
plt.show()
The result looks like:
Just don't set lolim = True and you are good to go, an example with sample data:
import pandas as pd
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
df = pd.DataFrame({"val":[1,2,3,4],"error":[.4,.3,.6,.9]})
meansum = df["val"]
stdsum = df["error"]
plot = meansum.plot(kind='bar',yerr=stdsum,colormap='OrRd_r',edgecolor='black',grid=False,figsize=(8,2),ax=ax,position=0.45,error_kw=dict(ecolor='black',elinewidth=0.5),width=0.8)
plt.show()

Remove axis scale

I've spent some time fruitlessly searching for an answer to my question, so I think a new question is in order. Consider this plot:
The axes labels use scientific notation. On the y-axis, all is well. However, I have tried and failed to get rid off the scaling factor that Python added in the lower-right corner. I would like to either remove this factor completely and simply indicate it by the units in the axis title or have it multiplied to every tick label. Everything would look better than this ugly 1e14.
Here's the code:
import numpy as np data_a = np.loadtxt('exercise_2a.txt')
import matplotlib as mpl
font = {'family' : 'serif',
'size' : 12}
mpl.rc('font', **font)
import matplotlib.pyplot as plt
fig = plt.figure()
subplot = fig.add_subplot(1,1,1)
subplot.plot(data_a[:,0], data_a[:,1], label='$T(t)$', linewidth=2)
subplot.set_yscale('log')
subplot.set_xlabel("$t[10^{14}s]$",fontsize=14)
subplot.set_ylabel("$T\,[K]$",fontsize=14)
plt.xlim(right=max(data_a [:,0]))
plt.legend(loc='upper right')
plt.savefig('T(t).pdf', bbox_inches='tight')
Update: Incorporating Will's implementation of scientificNotation into my script, the plot now looks like
Much nicer if you ask me. Here's the complete code for anyone wanting to adopt some part of it:
import numpy as np
data = np.loadtxt('file.txt')
import matplotlib as mpl
font = {'family' : 'serif',
'size' : 16}
mpl.rc('font', **font)
import matplotlib.pyplot as plt
fig = plt.figure()
subplot = fig.add_subplot(1,1,1)
subplot.plot(data[:,0], data[:,1], label='$T(t)$', linewidth=2)
subplot.set_yscale('log')
subplot.set_xlabel("$t[s]$",fontsize=20)
subplot.set_ylabel("$T\,[K]$",fontsize=20)
plt.xlim(right=max(data [:,0]))
plt.legend(loc='upper right')
def scientificNotation(value):
if value == 0:
return '0'
else:
e = np.log10(np.abs(value))
m = np.sign(value) * 10 ** (e - int(e))
return r'${:.0f} \cdot 10^{{{:d}}}$'.format(m, int(e))
formatter = mpl.ticker.FuncFormatter(lambda x, p: scientificNotation(x))
plt.gca().xaxis.set_major_formatter(formatter)
plt.savefig('T(t).pdf', bbox_inches='tight', transparent=True)
Just divide the x-values by 1e14:
subplot.plot(data_a[:,0] / 1e14, data_a[:,1], label='$T(t)$', linewidth=2)
If you want to add the label to each individual tick, you'll have to provide a custom formatter, like in tom's answer.
If you want it to look like as nice as the ticks on your y-axis, you could provide a function to format it with LaTeX:
def scientificNotation(value):
if value == 0:
return '0'
else:
e = np.log10(np.abs(value))
m = np.sign(value) * 10 ** (e - int(e))
return r'${:.0f} \times 10^{{{:d}}}$'.format(m, int(e))
# x is the tick value; p is the position on the axes.
formatter = mpl.ticker.FuncFormatter(lambda x, p: scientificNotation(x))
plt.gca().xaxis.set_major_formatter(formatter)
Of course, this will clutter your x-axis up quite a bit, so you might end up needing to display them at an angle, for example.
You can also change the tick formatter with the ticker module.
An example would be to use a FormatStrFormatter:
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
fig,ax = plt.subplots()
ax.semilogy(np.linspace(0,5e14,50),np.logspace(3,7,50),'b-')
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%.0e'))
Also see the answers here with lots of good ideas for ways to solve this.
In addition to the good answer from Will Vousden, you can set what you write in your ticks with:
plt.xticks(range(6), range(6))
the first range(6) is the location and the second is the label.

Categories

Resources