mplfinance plot not saving when called outside of plot command [duplicate] - python

This question already has an answer here:
How to save candlestick chart in matplotlib finance
(1 answer)
Closed 1 year ago.
I'm new to matplotlib. I'm struggling to customise elements of my plot within the first .plot call. For example, ylabel works but xlabel doesn't. I was hoping I could separate the savefig command so that I could add/modify elements of the plot between the first .plot call and the savefig call (because all the examples I see online seem to create the plot and then modify elements separately, i.e. the line fplt.xlabel("Blah")
I notice that a lot of plot examples I found online (for line graphs and such) provide all x and y values separately, but I like the plot technique I've used as it automatically uses high, low, etc. to create candles.
So why does this code work:
fplt.plot(
dft,
type="candle",
style='charles',
addplot=extraPlot2,
ylabel=stock,
# xlabel="Blah", <= removed as doesn't work
figratio=(10, 6), volume=True,
savefig=dict(
fname=folder2,
bbox_inches="tight"
)
)
But this code doesn't (even with the hashes added):
fplt.plot(
dft,
type="candle",
style='charles',
addplot=extraPlot2,
ylabel=stock,
# xlabel="Blah", <= removed as doesn't work
figratio=(10, 6), volume=True,
)
# fplt.xlabel("Blah") <= would like to do this if I can get savefig to work
# fplt.xticks(rotation=45) <= i'd also like to do stuff like this
fplt.savefig(folder2)
I've tried making fplt.plot a variable and targeting that but with no luck.
Apologies for any poor terminology, I am very new to this.
EDIT: Imports added below for reference. And I realise why xlabel wasn't working now, as I see I was importing it.
import datetime as dt
from matplotlib.pyplot import bar, xlabel
from numpy import False_, NaN
import pandas as pd
from pandas_datareader import data as pdr
import yfinance as yf
from tkinter import EXCEPTION, Tk
from tkinter.filedialog import askopenfilename
import os
from pandas import ExcelWriter
import mplfinance as fplt
from pathlib import Path

You can import the Matplotlib pyplot module and use the gcf() ("get current figure") function to return a Figure object. It is the Figure object that has the savefig() method. E.g.,
from matplotlib import pyplot as plot
# your code here
fig = plt.gcf() # gcf is "get current figure"
fig.savefig(folder2)
Looking at this mplfinance issue it also looks like you can return the figure object with:
fig, axlist = fplt.plot(..., returnfig=True)
# save figure
fig.savefig(folder2)

Related

HourLocator() on yaxis raising runtime error unexpectedly exceeding Locator.MAXTICK

I am trying to setup a simple figure with some horizontal lines.
plt.figure()
plt.axhline(datetime.time(12,0,0,0),color='blue',ls='--',lw=3)
plt.axhline(datetime.time(18,0,0,0),color='red',ls='--',lw=3)
This works fine and I get:
which is correct.
Then I would like my yticks to be on the rounded hourly values only.
I am trying using HourLocator()
from matplotlib.dates import HourLocator, DateFormatter,
plt.gca().yaxis.set_major_locator(HourLocator()) # this fails
plt.gca().yaxis.set_major_formatter(DateFormatter('%H:%M'))
However, this generates this error.
Why is it trying to generate 570241 ticks?
RuntimeError: Locator attempting to generate 570241 ticks from 42120.0 to 65880.0: exceeds Locator.MAXTICKS
Note that matplotlib does not support datetime.time values. Admittedly, the fact that it seemingly works hides this a bit.
So you first need to use datetime.datetime instead.
import datetime
import matplotlib.pyplot as plt
fig,ax=plt.subplots()
ax.axhline(datetime.datetime(2018,7,24,12,0,0,0),color='blue',ls='--',lw=3)
ax.axhline(datetime.datetime(2018,7,24,18,0,0,0),color='red',ls='--',lw=3)
ax.autoscale()
plt.show()
Now this already gives you hourly ticks (by coincidence). But you may of course use custom locations and formats now, adding
from matplotlib.dates import HourLocator, DateFormatter
plt.gca().yaxis.set_major_locator(HourLocator())
plt.gca().yaxis.set_major_formatter(DateFormatter('%H:%M'))
as in the question will give you the desired output.

Python plt: close or clear figure does not work

I generate a lots of figures with a script which I do not display but store to harddrive. After a while I get the message
/usr/lib/pymodules/python2.7/matplotlib/pyplot.py:412: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (matplotlib.pyplot.figure) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam figure.max_num_figures).
max_open_warning, RuntimeWarning)
Thus, I tried to close or clear the figures after storing. So far, I tried all of the followings but no one works. I still get the message from above.
plt.figure().clf()
plt.figure().clear()
plt.clf()
plt.close()
plt.close('all')
plt.close(plt.figure())
And furthermore I tried to restrict the number of open figures by
plt.rcParams.update({'figure.max_num_figures':1})
Here follows a piece of sample code that behaves like described above. I added the different options I tried as comments at the places I tried them.
from pandas import DataFrame
from numpy import random
df = DataFrame(random.randint(0,10,40))
import matplotlib.pyplot as plt
plt.ioff()
#plt.rcParams.update({'figure.max_num_figures':1})
for i in range(0,30):
fig, ax = plt.subplots()
ax.hist([df])
plt.savefig("/home/userXYZ/Development/pic_test.png")
#plt.figure().clf()
#plt.figure().clear()
#plt.clf()
#plt.close() # results in an error
#plt.close('all') # also error
#plt.close(plt.figure()) # also error
To be complete, that is the error I get when using plt.close:
can't invoke "event" command: application has been destroyed
while executing "event generate $w <>"
(procedure "ttk::ThemeChanged" line 6)
invoked from within "ttk::ThemeChanged"
The correct way to close your figures would be to use plt.close(fig), as can be seen in the below edit of the code you originally posted.
from pandas import DataFrame
from numpy import random
df = DataFrame(random.randint(0,10,40))
import matplotlib.pyplot as plt
plt.ioff()
for i in range(0,30):
fig, ax = plt.subplots()
ax.hist(df)
name = 'fig'+str(i)+'.png' # Note that the name should change dynamically
plt.savefig(name)
plt.close(fig) # <-- use this line
The error that you describe at the end of your question suggests to me that your problem is not with matplotlib, but rather with another part of your code (such as ttk).
plt.show() is a blocking function, so in the above code, plt.close() will not execute until the fig windows are closed.
You can use plt.ion() at the beginning of your code to make it non-blocking. Even though this has some other implications the fig will be closed.
I was still having the same issue on Python 3.9.7, matplotlib 3.5.1, and VS Code (the issue that no combination of plt.close() closes the figure). I have three loops which the most inner loop plots more than 20 figures. The solution that is working for me is using agg as backend and del someFig after plt.close(someFig). Subsequently, the order of code would be something like:
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt
someFig = plt.figure()
.
.
.
someFig.savefig('OUTPUT_PATH')
plt.close(someFig) # --> (Note 1)
del someFig
.
.
.
NOTE 1: If this line is removed, the output figures may not be plotted correctly! Especially when the number of elements to be rendered in the figure is high.
NOTE 2: I don't know whether this solution could backfire or not, but at least it is working and not hugging RAM or preventing plotting figures!
import tensorflow as tf
from matplotlib import pyplot as plt
sample_image = tf.io.read_file(str(PATH / 'Path to your file'))
sample_image = tf.io.decode_jpeg(sample_image)
print(sample_image.shape)
plt.figure("1 - Sample Image ")
plt.title(label="Sample Image", fontsize=12, color="red")
plt.imshow(sample_image)
plt.show(block=False)
plt.pause(3)
plt.close()
plt.show(block=False)
plt.pause(interval) do the trick
This does not really solve my problem, but it is a work-around to handle the high memory consumption I faced and I do not get any of the error messages as before:
from pandas import DataFrame
from numpy import random
df = DataFrame(random.randint(0,10,40))
import matplotlib.pyplot as plt
plt.ioff()
for i in range(0,30):
plt.close('all')
fig, ax = plt.subplots()
ax.hist([df])
plt.savefig("/home/userXYZ/Development/pic_test.png")

Seaborn displays different color for different run

I followed the setting from here to make matplotlib/seaborn available to display in Zeppelin. However, with the following code:
%python
import seaborn as sns
import matplotlib
import numpy as np
matplotlib.use('Agg')
import matplotlib.pyplot as plt
plt.rcdefaults()
import StringIO
def show(p):
img = StringIO.StringIO()
p.savefig(img, format='svg')
img.seek(0)
print "%html <div style='width:600px'>" + img.buf + "</div>"
""" Prepare your plot here ... """
# Use the custom show function instead of plt.show()
x = np.random.randn(100)
ax = sns.distplot(x)
show(sns.plt)
It is strange that the displayed figure show the desired lightblue color the first time I run the code but will display different colors if I execute the same piece of code. Is there a way to force seaborn to keep constant color being displayed? Thanks.
It's not entirely clear what is meant by "running a second time".
However you may try to actually close the figure before running it again. E.g.
plt.close("all")
in order to make sure, a new figure is created which should have the same default color every time.

What is the preferred way to import pylab at a function level in Python 2.7?

I have written a relatively simple function in python that can be used to plot the time domain history of a data set as well as the frequency domain response of a data set after a fast fourier transform. In this function I use the command from pylab import * to bring in all the necessary functionality. However, despite successfully creating the plot, I get a warning stating
import * only allowed at the module level.
So if using the command from pylab import * is not the preferred methodology, how do I properly load all the necessary functionality from pylab. The code is attached below. Also, is there a way to close the figure after the function is exited, I have tried plt.close() which is not recognized for subplots?
def Time_Domain_Plot(Directory,Title,X_Label,Y_Label,X_Data,Y_Data):
# Directory: The path length to the directory where the output file is
# to be stored
# Title: The name of the output plot, which should end with .eps or .png
# X_Label: The X axis label
# Y_Label: The Y axis label
# X_Data: X axis data points (usually time at which Yaxis data was acquired
# Y_Data: Y axis data points, usually amplitude
from pylab import *
from matplotlib import rcParams
rcParams.update({'figure.autolayout': True})
Output_Location = Directory.rstrip() + Title.rstrip()
fig,plt = plt.subplots()
matplotlib.rc('xtick',labelsize=18)
matplotlib.rc('ytick',labelsize=18)
plt.set_xlabel(X_Label,fontsize=18)
plt.set_ylabel(Y_Label,fontsize=18)
plt.plot(X_Data,Y_Data,color='red')
fig.savefig(Output_Location)
plt.clear()
From the matplotlib documentation:
pylab is a convenience module that bulk imports matplotlib.pyplot (for plotting) and numpy (for mathematics and working with arrays) in a single name space. Although many examples use pylab, it is no longer recommended.
I would recommend not importing pylab at all, and instead try using
import matplotlib
import matplotlib.pyplot as plt
And then prefixing all of your pyplot functions with plt.
I also noticed that you assign the second return value from plt.subplots() to plt. You should rename that variable to something like fft_plot (for fast fourier transform) to avoid naming conflicts with pyplot.
With regards to your other question (about fig, save fig()) you're going to need to drop that first fig because it's not necessary, and you'll call savefig() with plt.savefig() because it is a function in the pyplot module. So that line will look like
plt.savefig(Output_Location)
Try something like this:
def Time_Domain_Plot(Directory,Title,X_Label,Y_Label,X_Data,Y_Data):
# Directory: The path length to the directory where the output file is
# to be stored
# Title: The name of the output plot, which should end with .eps or .png
# X_Label: The X axis label
# Y_Label: The Y axis label
# X_Data: X axis data points (usually time at which Yaxis data was acquired
# Y_Data: Y axis data points, usually amplitude
import matplotlib
from matplotlib import rcParams, pyplot as plt
rcParams.update({'figure.autolayout': True})
Output_Location = Directory.rstrip() + Title.rstrip()
fig,fft_plot = plt.subplots()
matplotlib.rc('xtick',labelsize=18)
matplotlib.rc('ytick',labelsize=18)
fft_plot.set_xlabel(X_Label,fontsize=18)
fft_plot.set_ylabel(Y_Label,fontsize=18)
plt.plot(X_Data,Y_Data,color='red')
plt.savefig(Output_Location)
plt.close()

autofmt_xdate() using backend_agg

I'm using python pandas on a headless server to generate graphs which are subsequently served up on a web page. Because of the limitations, I can't use the well-written .plot() functionality in pandas (which would automatically handle my formatting mess), and instead I must create the graph (.png file) in a way that doesn't create a new window.
Code below does everything I need, but I'm stumped on where/how to call autofmt_xdate. Run the code below and notice the x-axis date formatting mess. Any help is appreciated, and apologies for any formatting errors in posting this.
My goal is to have the x-axis dates rotated 30 degrees or so.
#! /usr/bin/python
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
import pandas as pd
rng = pd.date_range( '1/1/2011', periods=20, )
ts = pd.Series(np.random.randn(len(rng)), index=rng)
fig = Figure()
canvas = FigureCanvas(fig)
ax = fig.add_subplot(111)
ax.plot_date(ts.index, ts, '-')
#ax.autofmt_xdate(rotation=30) # this doesn't work. Help!
canvas.print_png('test.png')

Categories

Resources