I finally managed to make my animations work. The only problem comes when I save them with ffmpeg writer. A ~250 frame gif takes literally a few hours to save. It took me 3 hours to save an 11 second video.
What is making it take so long??
import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import contextily as cx
from matplotlib.offsetbox import AnchoredText
#Writer information, path, and where to save
plt.rcParams['animation.ffmpeg_path'] =r"the path I saved ffmpeg"
#writer = animation.writers['ffmpeg']
f = r"location I will save the .mp4"
# Reads the Excel sheet specified from the doc. IT ONLY OPENS .XLSM
df = pd.read_excel(r'the excel file', sheet_name='the sheet name')
# Creates a list of important datasets
df['Points'] = list(zip(df.Latitude,df.Longitude))
Longs = list(df.Longitude)
Lats = list(df.Latitude)
Time = list(df.Last_Record)
Speed = list(df.Speed)
#This is the list of Coordinates
Coords = df['Points']
#print(Coords)
#how many frames to save for the animation
savecount = len(Longs)
print("Frames: ",savecount)
#turns the dataframe into a geodataframe
gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df.Longitude, df.Latitude),crs='EPSG:4326')
#Geodataframe boundaries
minx, miny, maxx, maxy = gdf.geometry.total_bounds
print("Boundaries: ",minx, miny, maxx, maxy)
#plt background
ax = gdf.plot(figsize=(6,6), alpha =0.5, facecolor="None")
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0)
ax.margins(0,0)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.axis('off')
#North arrow
x, y, arrow_length = 0.85, 0.10, 0.07
ax.annotate('N', xy=(x, y), xytext=(x, y-arrow_length),
arrowprops=dict(facecolor='black', width=5, headwidth=15),
ha='center', va='center', fontsize=20,
xycoords=ax.transAxes)
#Use contextily to create the basemap
cx.add_basemap(ax, crs=gdf.crs.to_string())
#Saves map
plt.savefig("image name.png", dpi=300, bbox_inches='tight', format="png", transparent=False,pad_inches = 0)
plt.close()
#Read the map
plotmap = r"image name above^^"
truthplot = plt.imread(plotmap)
#Create subplot over the map
fig, ax = plt.subplots(figsize = (6,6),linewidth = 0.1, frameon=False)
plottitle = "plot title"
ax.set_title(plottitle)
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
fig.tight_layout()
def animate(i):
Time.remove(Time[0])
Speed.remove(Speed[0])
scat = ax.scatter(Longs[i], Lats[i], zorder=1, alpha= 0.5, c='r', s=7)
annotation = AnchoredText(s=("Time: " + str(Time[0]) + "\n" + "Speed: " + str(Speed[0])),
prop=dict(size=8), frameon=True, loc='upper left')
annotation.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
ax.add_artist(annotation)
ax.imshow(truthplot, extent=(minx, maxx, miny, maxy), aspect='auto')
return [annotation],[scat],[Longs],[Lats]
#make the animation
ani = FuncAnimation(fig, animate,frames = savecount, interval=20, repeat = False)
ani.save(f, fps=15,writer='ffmpeg'
)
Everything else works except saving it takes WAY longer than it should, I think.
Thank you for any help!
Related
This is an example of my code to plot and save a figure:
I'm using Python 3.7.4 and matplotlib==3.0.3.
import matplotlib.pyplot as plt
import pandas as pd
from yahoo_fin import stock_info
import statsmodels.api as sm
brk_data = stock_info.get_data("BRK-A")
with plt.style.context('dark_background'):
fig, ax = plt.subplots(figsize=(16, 9))
sm.qqplot(brk_data['adjclose'].pct_change(1).fillna(0), fit=True, line='45', ax=ax)
plt.title('QQ Plot', fontsize = 16)
ax.axvline(0, c = 'w', linestyle = "--", alpha = 0.5)
ax.grid(True,linewidth=0.30)
ax.set_xlim(4,-4)
ax.set_ylim(5,-5)
plt.savefig('qqplot.png', bbox_inches = 'tight', pad_inches = 0.4, dpi = 300, edgecolor = 'k')
plt.show()
plt.close()
This code saves and displays the plot figure correctly, as follows:
But when the plot is built inside a function, the saved picture background will stay white, making the white ticks and labels from the 'dark-background' style invisible, e.g.:
for
def qqplot2(pct, save = False):
with plt.style.context('dark_background'):
fig, ax = plt.subplots(figsize=(16, 9))
sm.qqplot(pct, fit=True, line='45', ax=ax)
plt.title('QQ Plot', fontsize = 16)
ax.axvline(0, c = 'w', linestyle = "--", alpha = 0.5)
ax.grid(True,linewidth=0.30)
ax.set_xlim(4,-4)
ax.set_ylim(5,-5)
if save == True:
plt.savefig('qqplot2.png', bbox_inches = 'tight', pad_inches = 0.4, dpi = 300, edgecolor = 'k')
plt.show()
plt.close()
else:
plt.show()
calling the function with qqplot2(brk_data['adjclose'].pct_change(1).fillna(0), save = True) will display the correct plot:
but will save the figure incorrectly:
You just need to indent your if clause in the function like this:
def qqplot2(pct, save = False):
with plt.style.context('dark_background'):
fig, ax = plt.subplots(figsize=(16, 9))
sm.qqplot(pct, fit=True, line='45', ax=ax)
plt.title('QQ Plot', fontsize = 16)
ax.axvline(0, c = 'w', linestyle = "--", alpha = 0.5)
ax.grid(True,linewidth=0.30)
ax.set_xlim(4,-4)
ax.set_ylim(5,-5)
if save == True:
plt.savefig('qqplot2.png', bbox_inches = 'tight', pad_inches = 0.4, dpi = 300, edgecolor = 'k')
plt.show()
plt.close()
else:
plt.show()
I'm trying to create a bar chart with multiple bars in Python. The bar chart should display values on top of each bar.
I have a data set like the following:
Speciality Very interested Somewhat_interested Notinterested
Big Data (Spark/Hadoop) 1332 729 127
Data Analysis / Statistics 1688 444 60
Data Journalism 429 1081 610
I have tried the following code:
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
pd_dataframe = pd.read_csv('Test-Barchart.csv')
no_of_xaxis = pd_dataframe.Speciality.nunique()
ind = np.arange(no_of_xaxis)
xcord = pd_dataframe['Speciality'].tolist()
veryinterestedlist = pd_dataframe['Very interested'].tolist()
somewhatlist = pd_dataframe['Somewhat interested'].tolist()
notinterestedlist = pd_dataframe['Not interested'].tolist()
fig=plt.figure()
ax = fig.add_subplot(111)
width=0.8
rects1 = ax.bar(ind, veryinterestedlist, width, color='r')
rects2 = ax.bar(ind, somewhatlist, width, color='g')
rects3 = ax.bar(ind+width*2, notinterestedlist, width, color='b')
ax.legend( (rects1[0], rects2[0], rects3[0]), ('Very Interested',
'Somewhat Interested', 'Not Interested') )
def autolabel(rects):
for rect in rects:
h = rect.get_height()
ax.text(rect.get_x()+rect.get_width()/2., 1.05*h, '%d'%int(h),
ha='center', va='bottom')
autolabel(rects1)
autolabel(rects2)
autolabel(rects3)
ax.set_xticks(ind+width)
ax.set_xticklabels( xcord )
plt.show()
The problem is, plt.show() is not showing anything!
I don't have any errors in the code.
Could you please help me resolve this problem?
Also how can i change bar color to hex code color instead of r,g or b? e.g. #5bc0de
Small changes to your code:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
pd_dataframe = pd.read_csv('Test-Barchart.csv')
no_of_xaxis = pd_dataframe.Speciality.nunique()
ind = np.arange(no_of_xaxis)
width = 0.1
xcord = pd_dataframe['Speciality'].tolist()
veryinterestedlist = pd_dataframe['Very interested'].tolist()
somewhatlist = pd_dataframe['Somewhat interested'].tolist()
notinterestedlist = pd_dataframe['Not interested'].tolist()
fig, ax = plt.subplots()
rects1 = ax.bar(ind, veryinterestedlist, width, color='g')
rects2 = ax.bar(ind + width, somewhatlist, width, color='c')
rects3 = ax.bar(ind+2*width, notinterestedlist, width, color='r')
# add some text for labels, title and axes ticks
ax.set_ylabel('y label')
ax.set_title('Title')
ax.set_xticks(ind + width)
ax.set_xticklabels(xcord)
ax.legend( (rects1[0], rects2[0], rects3[0]), ('Very Interested',
'Somewhat Interested', 'Not Interested') )
def autolabel(rects):
"""
Attach a text label above each bar displaying its height
"""
for rect in rects:
height = rect.get_height()
ax.text(rect.get_x() + rect.get_width()/2., 1.05*height,
'%d' % int(height),
ha='center', va='bottom')
autolabel(rects1)
autolabel(rects2)
autolabel(rects3)
plt.show()
and you get:
Reference: Grouped bar chart with labels
I have data stored in stock_data in JSON format (which can be any arbitrary data). I want to plot 4 axes, and WHEN there is new data, update the graph (through animate I am assuming).
I only want this to occur when using INTRADAY data (as you can see I have an if intraday check at the bottom). I am pulling this intraday data from an API. This API updates every minute or so, and only during certain hours. I don't mind if it doesn't update instantly, but ideally within a 1 minute period of new data.
I have tried pulling new data and comparing it to the old DF (as you can see at the end of the code) and putting it in a while True: loop, however the graph fails to render. I have tried simply putting the entire function in a loop and rendering the graph every time - this not only takes ages to render, but if I am zoomed in on the graph, it completely resets it. I figure this is a problem with redrawing?
Finally, I am unsure what to put in the animation.FuncAnimation() either.. I have excluded ax3 and ax4 because they'll act the same as ax2 for demonstration purposes. Help is much appreciated.
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.dates as mdates
import matplotlib.animation as animation
from mpl_finance import candlestick_ohlc
import numpy as np
## CANDLESTICK GRAPH ##
def candlestick(symbol, MA1 = 20, MA2 = 200):
try:
## arbitrary colors ##
candle_upcol = '#cccccc'
candle_downcol = '#cccccc'
fill_col = '#cccccc'
bg_col = '#cccccc'
spine_col = '#cccccc'
## load stocks ##
stock_data = pd.DataFrame.from_dict(json.load(open('db/AAPL.txt')), orient = 'index', dtype = np.float64)
stock_data = stock_data.values
## BEGIN PLOTTING ##
start_point = len(stock_data[max(MA1, MA2)-1:])
fig = plt.figure(facecolor=bg_col)
#set grids
ax1 = plt.subplot2grid((8,4), (1,0), rowspan = 5, colspan = 4, facecolor = bg_col)
ax2 = plt.subplot2grid((8,4), (7,0), rowspan = 1, colspan = 4, sharex = ax1, facecolor= bg_col)
ax3 = plt.subplot2grid((8,4), (0,0), rowspan = 1, colspan = 4, sharex = ax1, facecolor = bg_col)
ax4 = plt.subplot2grid((8,4), (6,0), rowspan = 1, colspan = 4, sharex = ax1, facecolor = bg_col)
#PRICE plot (AX1)
candlestick_ohlc(ax1, stock_data[-start_point:,0:5], width = 0.6, colorup = candle_upcol, colordown = candle_downcol)
ax1.xaxis.set_major_locator(mticker.MaxNLocator(10))
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax1.grid(True)
plt.setp(ax1.get_xticklabels(), visible=False) #remove x ticks
#MOVING AVERAGES plot (AX1)
if MA1 != 0:
av1 = moving_average(stock_data[:,4], MA1) #using close prices
label_ma1 = '{MA} SMA'.format(MA = str(MA1))
ax1.plot(stock_data[-start_point:,0], av1[-start_point:], label = label_ma1, color = '#aec6cf', linewidth = .8)
if MA2 != 0:
av2 = moving_average(stock_data[:,4], MA2) #using close prices
label_ma2 = '{MA} SMA'.format(MA = str(MA2))
ax1.plot(stock_data[-start_point:,0], av2[-start_point:], label = label_ma2, color = '#ffb347', linewidth = .8)
if MA1 != 0 or MA2 != 0:
ax1.text(0, 1, 'MA ({MA1}, {MA2})'.format(MA1 = str(MA1), MA2 = str(MA2)), va = 'top', ha = 'left', color = 'w', transform = ax1.transAxes, alpha = 0.5, fontweight = 'bold')
#VOLUME plot (AX2)
volume_min = 0 #stock_data[:,5].min()
ax2.plot(stock_data[-start_point:,0], stock_data[-start_point:,5], '#00ffe8', linewidth = .8)
ax2.fill_between(stock_data[-start_point:,0], volume_min, stock_data[-start_point:,5], facecolor = fill_col, alpha = 0.5)
ax2.axes.yaxis.set_ticklabels([]) #remove y ticks
ax2.text(0, 1, 'VOLUME', va = 'top', ha = 'left', color = 'w', transform = ax2.transAxes, alpha = 0.5, fontweight = 'bold')
#RSI plot (AX3)
#similar to VOL
#MACD plot (AX4)
#similar to VOL
#SHARED plot (ALL AX)
for all_ax in (ax1, ax2''', ax3, ax4'''):
plt.setp(all_ax.spines.values(), color=spine_col)
all_ax.tick_params(axis='both', colors = 'w')
all_ax.yaxis.label.set_color("w")
all_ax.yaxis.tick_right()
all_ax.xaxis.set_tick_params(labelsize=9)
all_ax.yaxis.set_tick_params(labelsize=9)
#ENTIRE plot
plt.subplots_adjust(hspace = 0)
fig.autofmt_xdate()
fig.suptitle('{STOCK}'.format(STOCK = symbol), color = 'w', fontweight='bold', alpha = 0.75)
print('Drawing graph.')
if data_type != 'Intraday':
print('Graphing complete.')
else:
#this will be replaced by an API fetch function at some point, this is just for testing if animation works.. needs a sleep function? and while True loop..?
new_stock_data = pd.DataFrame.from_dict(json.load(open('db/AAPL_new.txt')), orient = 'index', dtype = np.float64)
new_stock_data = new_stock_data.values
if (new_stock_data[-1] == stock_data[-1]).all() == False:
stock_data = np.vstack([stock_data, new_stock_data[-1]])
#ani = animation.FuncAnimation(fig, '''???''', interval = 10000) #blit=True?
plt.show()
except:
print('Failed main loop.')
A FuncAnimation will draw (or blit) repeatedly at a rate given by the interval. In case that is not desired, one could use a timer instead. The timer calls a function that will, depending on some condition either do nothing, or update the plot with new data. This way you make sure to only draw the canvas when new data is available (i.e. condition is true).
import datetime
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
t = []
x = []
line, = ax.plot_date(t,x, ls="-")
def update():
now = datetime.datetime.now()
if np.random.rand() > 0.9:
t.append(now)
x.append(np.random.randn())
line.set_data(t,x)
ax.relim()
ax.autoscale_view()
fig.canvas.draw_idle()
message = "new data drawn"
else:
message = "no new data"
print(now.time(), message)
timer = fig.canvas.new_timer(interval=200)
timer.add_callback(update)
timer.start()
plt.show()
I'm working with the following class:
import numpy as np
import matplotlib
matplotlib.use('Qt4Agg')
import matplotlib.pyplot as plt
import matplotlib.ticker as plticker
class matplotLIV():
def __init__(self, BaseFilename, temperatures, length=None, width=None, area=None, title = '', ylim=None):
self.BaseFilename = BaseFilename
self.temperatures = temperatures
if length and width:
self.length = length
self.width = width
self.area = length*width*1e-5
else:
self.area = area
self.title = title
self.ylim = ylim
filenames = [("%s_%sK.txt" % (self.BaseFilename, str(temp)), temp) for temp in self.temperatures]
self.rawData = [(np.loadtxt(fname), temp) for fname, temp in filenames]
self.colors = colors = ['#1b9e77', '#d95f02', '#7570b3', '#e7298a', '#e6ab02', '#a6761d', '#666666']
self.maxValueRow = (0,0,0)
def plot(self):
self.fig = plt.figure()
self.ax1 = self.fig.add_subplot(111)
ax1 = self.ax1
ax1.tick_params(bottom='off')
ax1.xaxis.tick_top()
self.ax2 = ax1.twinx()
ax2 = self.ax2
self.ax3 = ax2.twiny()
ax3 = self.ax3
ax3.xaxis.tick_bottom()
ax1.set_xlabel("current / A")
ax1.xaxis.set_label_position('top')
ax1.set_ylabel("voltage / V")
ax2.set_ylabel("light intensity / arb. u.")
ax3.set_xlabel(r'current density / $\mathregular{Acm^{-2}}$')
ax3.xaxis.set_label_position('bottom')
for i, (datafile, label) in enumerate(self.rawData):
self.checkMaxValues(datafile)
ax1.plot( datafile[:,0], datafile[:,1], color=self.colors[i], label='%sK' % str(label))
ax2.plot( datafile[:,0], datafile[:,2], color=self.colors[i], label='%sK' % str(label), linewidth=2)
ax1.margins(x=0)
ax1.grid(True, axis='y')
ax3.grid(True)
start, end = ax1.get_xlim()
self.setAxesScale(ax1, ax2)
if self.ylim:
ax2.set_ylim(top=self.ylim)
ax3.set_xlim(start/self.area, end/self.area)
leg = ax2.legend(loc='upper left')
self.fig.suptitle(self.title, y=0.98, weight='bold')
self.fig.subplots_adjust(top=0.86)
loc = plticker.MultipleLocator(base=20.0) # this locator puts ticks at regular intervals
ax3.xaxis.set_major_locator(loc)
def checkMaxValues(self, data):
maxInd = data.argmax(axis=0)[2]
if data[maxInd][2] > self.maxValueRow[2]:
self.maxValueRow = data[maxInd]
def setAxesScale(self, ax1, ax2):
yrange = ax1.get_ylim()
y1Fraction = self.maxValueRow[1]/yrange[1]
y2Fraction = y1Fraction - 0.02
ax2.set_ylim(top=self.maxValueRow[2]/y2Fraction)
def show(self):
plt.savefig(self.BaseFilename + '.pdf')
plt.show()
which you can run with this sample code:
import matplotLIV as mpliv
######## configuration
BaseFilename = "testdata"
temperatures = (5,)
area = 1e-8
######## end of configuration
liv = mpliv.matplotLIV(BaseFilename, temperatures, area=area)
liv.plot()
liv.show()
on this file: http://pastebin.com/GMAC3mUu
The problem that I'm experiencing is that the legend is transparent to the grid. Oddly enough, it is only the vertical grid that you can see through the legend box:
Is this a bug? If not, how do I set the legend so it is NOT transparent?
The problem is the vertical grid is on ax3, and the legend is on ax2, so the grid is plotted after the legend.
One way around this is pasted below (just the section you need to modify). You need to plot the legend on ax3, and explicitly tell it which lines and labels you want.
# make a list for the lines that you are plotting
l1 = []
l2 = []
for i, (datafile, label) in enumerate(self.rawData):
self.checkMaxValues(datafile)
# Give your lines some names (l1,l2)
l1+=ax1.plot( datafile[:,0], datafile[:,1], color=self.colors[i], label='%sK' % str(label))
l2+=ax2.plot( datafile[:,0], datafile[:,2], color=self.colors[i], label='%sK' % str(label), linewidth=2)
# Define which lines to put in the legend. If you want l1 too, then use lns = l1+l2
lns = l2
labs = [l.get_label() for l in lns]
ax1.margins(x=0)
ax1.grid(True, axis='y')
ax3.grid(True)
start, end = ax1.get_xlim()
self.setAxesScale(ax1, ax2)
if self.ylim:
ax2.set_ylim(top=self.ylim)
ax3.set_xlim(start/self.area, end/self.area)
# Set the legend on ax3, not ax2
leg = ax3.legend(lns,labs,loc='upper left')
I am trying to animate different objects in the same graph using pyplot's funcanimation.
It works almost as I expect it to, except for the order in which the different elements are displayed in. So the plot curve, text and legend are shown behind the image where they are barely seen.
Here is my (not so) minimal working example:
#! /usr/bin/python
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import random
def init():
line_simu.set_data([], [])
time_text.set_text('')
imobj.set_data(np.zeros((100, 100)))
imobj.set_zorder(0)
time_text.set_zorder(10)
return line_simu, time_text, imobj
def animate(i):
imobj.set_zorder(0)
time_text.set_zorder(10)
y_simu = np.linspace(0,100, 100)
x_simu = np.linspace(-10, 10, 100)
line_simu.set_data(x_simu, y_simu)
time_text.set_text('time = %.1f' % i )
global data
imobj.set_data( data + np.random.random((100,1)) * 0.5 )
return line_simu, time_text, imobj
def forceAspect(ax,aspect=1):
im = ax.get_images()
extent = im[0].get_extent()
ax.set_aspect(abs((extent[1]-extent[0])/(extent[3]-extent[2]))/aspect)
fig = plt.figure()
ax = plt.axes(xlim=(-15,15), ylim=(-110, 0) , aspect=1)
data = np.random.random((100,100)) - .5
imobj = ax.imshow( data , extent=[-15,15, -110, 0.0], origin='lower', cmap=plt.cm.gray, vmin=-2, vmax=2, alpha=.7, zorder=0, aspect=1)
line_simu, = ax.plot([], [],"r--", lw=2, markersize=4 , label = "Some curve" , zorder= 2 )
time_text = ax.text(-14.9, -108, '', zorder=10)
l = plt.legend(loc='lower right', prop={'size':8} )
l.set_zorder(200)
forceAspect(ax,aspect=1)
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=range( 50), interval=3000, blit=True)
plt.show()
Without animation, I can easily control the order of the different elements with set_zorder, but when the animation updates the image, this order is lost. I tried to set the zorder in the init function and again in the animate function, without success.
I am very thankful for any help on that matter.
To answer my own question: It seems that the order in witch the init() and animate() functions return objects controls the order in which they are displayed. Additionally those functions should return the legend object in order to include it in the animation.
Here is my corrected code:
#! /usr/bin/python
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import random
def init():
imobj.set_data(np.zeros((100, 100)))
line_simu.set_data([], [])
time_text.set_text('time = 0.0')
return imobj , line_simu, time_text, l
def animate(i):
global data
imobj.set_data( data + np.random.random((100,1)) * 0.5 )
imobj.set_zorder(0)
y_simu = np.linspace(-100,-10, 100)
x_simu = np.linspace(-10, 10, 100)
line_simu.set_data(x_simu, y_simu)
time_text.set_text('time = %.1f' % i )
return imobj , line_simu, time_text, l
def forceAspect(ax,aspect=1):
im = ax.get_images()
extent = im[0].get_extent()
ax.set_aspect(abs((extent[1]-extent[0])/(extent[3]-extent[2]))/aspect)
fig = plt.figure()
ax = plt.axes(xlim=(-15,15), ylim=(-110, 0) , aspect=1)
data = np.random.random((100,100)) - .5
imobj = ax.imshow( data , extent=[-15,15, -110, 0.0], origin='lower', cmap=plt.cm.gray, vmin=-2, vmax=2, alpha=1.0, zorder=1, aspect=1)
line_simu, = ax.plot([], [],"r--", lw=2, markersize=4 , label = "Some curve" , zorder= 1 )
time_text = ax.text(-14.0, -108, '', zorder=10)
forceAspect(ax,aspect=1)
l = plt.legend(loc='lower right', prop={'size':8} )
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=range( 50), interval=500, blit=True)
plt.show()