How to superimpose a plot step by step by iteration in Python? - python

I have dataframes which I am trying to plot them in one single plot.
However, it needs to be step-by-step by iteration. Like the one single plot should be updated at each time loop runs.
What I am trying now is
for i in range(0, len(df))
plt.plot(df[i].values[:,0], df[i].values[:,1])
plt.show()
It seems work but it generates a graph at each iteration.
I want them all to be in one plot as it is being updated.
Thanks for your help.
Edit: Regarding the answers, you referred does not contain what I wanted.
That one is just superimposing two datasets.
What I wanted was that as a new graph is superimposed, the original figure created should be updated at the next iteration, not showing them all at once after the end of the loop.

Here's an example of a plot that gets updated automatically using matplotlib's animation feature. However, you could also call the update routine yourself, whenever necessary:
import numpy as np
import matplotlib.pyplot as plt
import pandas
import matplotlib.animation as animation
from matplotlib.animation import FuncAnimation
df = pandas.DataFrame(data=np.linspace(0, 100, 101), columns=["colA"])
fig = plt.figure()
ax = plt.gca()
ln, = ax.plot([], [], "o", mew=2, mfc="None", ms=15, mec="r")
class dataPlot(object):
def __init__(self):
self.objs = ax.plot(df.loc[0,"colA"], "g*", ms=15, mew=2, mec="g", mfc="None", label="$Data$")
fig.legend(self.objs, [l.get_label() for l in self.objs], loc="upper center", prop={"size":18}, ncol=2)
def update(self, iFrame):
for o in self.objs:
o.remove()
print("Rendering frame {:d}".format(iFrame))
self.objs = ax.plot(df.loc[iFrame,"colA"], "g*", ms=15, mew=2, mec="g", mfc="None", label="$Data$")
return ln,
dp = dataPlot()
ani = FuncAnimation(fig, dp.update, frames=df.index, blit=True)
plt.show()

Related

How can I plot the animation from the csv data with date time information?

first I would like to share the data of csv file.
date, total_cases, total_deaths
12-5-2020,6,2
13-5-2020,7,3
14-5-2020,10,2
15-5-2020,18,5
Now I want to make an animated comparison graph where the x axis will be plotted the dates and y axis will be plotted the total_cases and total_deaths.
from matplotlib import dates as mdate
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import pandas as pd
m=pd.read_csv("covid-data.csv")
m['date']=pd.to_datetime(m['date'])
m.sort_values('date',inplace=True)
cdate=m['date']
ccase=m['total_cases']
cdeath=m['total_deaths']
fig = plt.figure()
ax1 = fig.add_subplot(111)
def animate(i):
ax1.clear()
ax1.plot(cdate,ccase)
ax1.plot(cdate,cdeath)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
Now
I can't get our desired output or animation. How can I overcome this issue and get a solution?
Sorry for my english
Check this code:
from matplotlib import dates as mdate
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import pandas as pd
m = pd.read_csv("covid-data.csv")
m['date'] = pd.to_datetime(m['date'], format = '%d-%m-%Y')
m.sort_values('date', inplace = True)
cdate = m['date']
ccase = m['total_cases']
cdeath = m['total_deaths']
fig = plt.figure()
ax1 = fig.add_subplot(111)
def animate(i):
ax1.clear()
ax1.plot(cdate[:i], ccase[:i], label = 'cases')
ax1.plot(cdate[:i], cdeath[:i], label = 'deaths')
ax1.legend(loc = 'upper left')
ax1.set_xlim([cdate.iloc[0],
cdate.iloc[-1]])
ax1.set_ylim([min(ccase.iloc[0], cdeath.iloc[0]),
max(ccase.iloc[-1], cdeath.iloc[-1])])
ax1.xaxis.set_major_locator(mdate.DayLocator(interval = 5))
ax1.xaxis.set_major_formatter(mdate.DateFormatter('%d-%m-%Y'))
ani = animation.FuncAnimation(fig, animate, interval = 1000)
plt.show()
I changed your animate function in order to use the i counter (which increases by 1 at each frame). You can control what you want to change during the animation with this counter. The I added some formatting in order to improve the visualization. The code above gives me this animation:
In order to get an appreciable animation, I added some "fake" data to the one you provided, in order to have more days over which run the animation. Replace them with your data.
In the case of the error
TypeError: 'builtin_function_or_method' object is not subscriptable
Replace the .iloc[0] with [m.index[0]] and the same for .iloc[-1] with [m.index[-1]]. For example ccase.iloc[0] becomes ccase[m.index[0]].

Matplotlib graphics problems in python

I have the following graphic generated with the following code
I want to correct the x-axis display to make the date more readable.
I would also like to be able to enlarge the graph
My code is :
import requests
import urllib.parse
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
def get_api_call(ids, **kwargs):
API_BASE_URL = "https://apis.datos.gob.ar/series/api/"
kwargs["ids"] = ",".join(ids)
return "{}{}?{}".format(API_BASE_URL, "series", urllib.parse.urlencode(kwargs))
df = pd.read_csv(get_api_call(
["168.1_T_CAMBIOR_D_0_0_26", "101.1_I2NG_2016_M_22",
"116.3_TCRMA_0_M_36", "143.3_NO_PR_2004_A_21", "11.3_VMATC_2004_M_12"],
format="csv", start_date=2018
))
time = df.indice_tiempo
construccion=df.construccion
emae = df.emae_original
time = pd.to_datetime(time)
list = d = {'date':time,'const':construccion,'EMAE':emae}
dataset = pd.DataFrame(list)
plt.plot( 'date', 'EMAE', data=dataset, marker='o', markerfacecolor='blue', markersize=12, color='skyblue', linewidth=4)
plt.plot( 'date', 'const', data=dataset, marker='', color='olive', linewidth=2)
plt.legend()
To make the x-tick labels more readable, try rotating them. So use, for example, a 90 degree rotation.
plt.xticks(rotation=90)
To enlarge the size, you can define your own size using the following in the beginning for instance
fig, ax = plt.subplots(figsize=(10, 8))
I am fairly sure that this can be done by using the window itself of Matplotlib. If you have the latest version you can enlarge on a section of the graph by clicking the zoom button in the bottom left. To get the x-tick labels to be more readable you can simply click the expand button in the top right or use Sheldore's solution.

Set the maximum number of rows in legend

I need to draw several datasets within a single plot. The number of datasets varies, so I don't know a priori how many there will be.
If I just draw the legends, I get this (MCVE below):
How can I tell plt.legend() to only draw say the first 10 legends? I've looked around the plt.legends() class but there seems to be no argument to set such a value.
MCVE:
import numpy as np
import matplotlib.pyplot as plt
dataset = []
for _ in range(20):
dataset.append(np.random.uniform(0, 1, 2))
lbl = ['adfg', 'dfgb', 'cgfg', 'rtbd', 'etryt', 'frty', 'jklg', 'jklh',
'ijkl', 'dfgj', 'kbnm', 'bnmbl', 'qweqw', 'fghfn', 'dfg', 'hjt', 'dfb',
'sdgdas', 'werwe', 'dghfg']
for i, xy in enumerate(dataset):
plt.scatter(xy[0], xy[1], label=lbl[i])
plt.legend()
plt.savefig('test.png')
You can just limit the number of labels shown.
import matplotlib.pyplot as plt
maxn = 16
for i in range(25):
plt.scatter(.5, .5, label=(i//maxn)*"_"+str(i))
plt.legend()
plt.show()
This method works also for text labels of course:
import numpy as np
import matplotlib.pyplot as plt
labels = ["".join(np.random.choice(list("ABCDEFGHIJK"), size=8)) for k in range(25)]
maxn = 16
for i,l in enumerate(labels):
plt.scatter(.5, .5, label=(i//maxn)*"_"+l)
plt.legend()
plt.show()
The reason this works is that labels starting with "_" are ignored in the legend. This is used internally to give objects a label without showing them in the legend but can of course also be used by us to limit the number of elements in the legend.
I would like to suggest an alternative way to get your desired output, which I feel relies less on a "hack" of the legend labels.
You can use the function Axes.get_legend_handles_labels() to get a list of the handles and the labels of the objects that are to be put in the legend.
You can truncate these lists however you feel like, before passing them to plt.legend(). For instance:
import numpy as np
import matplotlib.pyplot as plt
dataset = []
for _ in range(20):
dataset.append(np.random.uniform(0, 1, 2))
lbl = ['adfg', 'dfgb', 'cgfg', 'rtbd', 'etryt', 'frty', 'jklg', 'jklh',
'ijkl', 'dfgj', 'kbnm', 'bnmbl', 'qweqw', 'fghfn', 'dfg', 'hjt', 'dfb',
'sdgdas', 'werwe', 'dghfg']
fig, ax = plt.subplots()
for i, xy in enumerate(dataset):
ax.scatter(xy[0], xy[1], label=lbl[i])
h,l = ax.get_legend_handles_labels()
plt.legend(h[:3], l[:3]) # <<<<<<<< This is where the magic happens
plt.show()
You could even display every other label plt.legend(h[::2], l[::2]) or whatever else you want.

Animate multiple shapes in python3 using matplotlib

Trying to animate multiple objects at once in python3 while using matplotlib animation function.
code written below is where I am thus far. I am able to create the multiple objects and display them in the figure. I did this by using a for loop containing a patches function for a rectangle. From here I was hoping to move all the individual rectangles over by a set amount by using the animation function.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.animation as animation
fig = plt.figure()
ax = fig.add_subplot(111)
plt.xlim(-100, 100)
plt.ylim(-100, 100)
width = 5
bars = 25
RB = [] # Establish RB as a Python list
for a in range(bars):
RB.append(patches.Rectangle((a*15-140,-100), width, 200,
color="blue", alpha=0.50))
def init():
for a in range(bars):
ax.add_patch(RB[a])
return RB
def animate(i):
for a in range(bars):
temp = np.array(RB[i].get_xy())
temp[0] = temp[0] + 3;
RB[i].set_XY = temp
return RB
anim = animation.FuncAnimation(fig, animate,
init_func=init,
frames=15,
interval=20,
blit=True)
plt.show()
Currently, nothing moves or happens once I run the code. I have tried to follow the examples found on the python website; but it usually results in a 'AttributeError: 'list' object has no attribute 'set_animated''.
You have to use
RB[i].set_xy(temp)
instead of set_XY = temp
The indexes in RB is wrong actually. You should change the animate function as:
def animate(i):
for a in range(bars):
temp = RB[a].get_x() + 3
RB[a].set_x(temp)
return RB

Six subplots with the same number of xticklabels in matplotlib

I am really struggling with matplotlib, escpecially with the axis settings. My goal is to set up 6 subplots in one figure, which all display different datasets but have the same amount of ticklabels.
The relevant part of my sourcecode looks like:
graph4.py:
# Import Matolotlib Modules #
import matplotlib as mpl
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
from matplotlib import ticker
import matplotlib.pyplot as plt
mpl.rcParams['font.sans-serif']='Arial' #set font to arial
# Import GTK Modules #
import gtk
#Import System Modules #
import sys
# Import Numpy Modules #
from numpy import genfromtxt
import numpy
# Import Own Modules #
import mysubplot as mysp
class graph4():
weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag']
def __init__(self, graphview):
#create new Figure
self.figure = Figure(figsize=(100,100), dpi=75)
#create six subplots within self.figure
self.subplot = []
for j in range(6):
self.subplot.append(self.figure.add_subplot(321 + j))
self.__conf_subplots__() #configure title, xlabel, ylabel and grid of all subplots
#to make it look better
self.figure.subplots_adjust(left=0.125, bottom=0.1, right=0.9, top=0.96, wspace=0.2, hspace=0.6)
#Matplotlib <-> GTK
self.canvas = FigureCanvas(self.figure) # a gtk.DrawingArea
self.canvas.set_flags(gtk.HAS_FOCUS|gtk.CAN_FOCUS)
self.canvas.grab_focus()
self.canvas.show()
graphview.pack_start(self.canvas, True, True)
#add labels and grid to all subplots
def __conf_subplots__(self):
index = 0
for i in self.subplot:
mysp.conf_subplot(i, 'Zeit', 'Menge', graph4.weekdays[index], True)
i.plot([], [], 'bo') #empty plot
index +=1
def plot(self, filename_list):
index = 0
for filename in filename_list:
data = genfromtxt(filename, delimiter=',') #load data from filename
if data.size != 0: #only if file isn't empty
if index <= len(self.subplot): #plot every file on a different subplot
mysp.plot(self.subplot[index],data[0:, 1], data[0:, 0])
index +=1
self.canvas.draw()
def clear_plot(self):
#clear axis of all subplots
for i in self.subplot:
i.cla()
self.__conf_subplots__()
mysubplot.py: (helper module)
# Import Matplotlib Modules
from matplotlib.axes import Subplot
import matplotlib.dates as md
import matplotlib.pyplot as plt
# Import Own Modules #
import mytime as myt
# Import Numpy Modules #
import numpy as np
def conf_subplot(subplot, xlabel, ylabel, title, grid):
if(xlabel != None):
subplot.set_xlabel(xlabel)
if(ylabel != None):
subplot.set_ylabel(ylabel)
if(title != None):
subplot.set_title(title)
subplot.grid(grid)
#rotate xaxis labels
plt.setp(subplot.get_xticklabels(), rotation=30, fontsize=12)
#display date on xaxis
subplot.xaxis.set_major_formatter(md.DateFormatter('%H:%M:%S'))
subplot.xaxis_date()
def plot(subplot, x, y):
subplot.plot(x, y, 'bo')
I think the best way to explain what goes wrong is with the use of screenshots. After I start my application, everything looks good:
If I double click a 'Week'-entry on the left, the method clear_plot() in graph4.py is called to reset all subplots. Then a list of filenames is passed to the method plot() in graph4.py. The method plot() opens each file and plots each dataset on a different subplot. So after I double click a entry, it looks like:
As you can see, each subplot has a different number of xtick labels, which looks pretty ugly to me. Therefore, I am looking for a solution to improve this. My first approach was to set the ticklabels manually with xaxis.set_ticklabels(), so that each subplot has the same number of ticklabels. However, as strange as it sounds, this only works on some datasets and I really don't know why. On some datasets, everything works fine and on other datasets, matplotlib is basically doing what it wants and displays xaxis labels that I didn't specify. I also tried FixedLocator(), but I got the same result. On some datasets it is working and on others, matplotlib is using a different number of xtick labels.
What am I doing wrong?
Edit:
As #sgpc suggested, I tried to use pyplot. My sourcecode now looks like this:
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
import matplotlib.dates as md
mpl.rcParams['font.sans-serif']='Arial' #set font to arial
import gtk
import sys
# Import Numpy Modules #
from numpy import genfromtxt
import numpy
# Import Own Modules #
import mysubplot as mysp
class graph2():
weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag']
def __init__(self, graphview):
self.figure, temp = plt.subplots(ncols=2, nrows=3, sharex = True)
#2d array -> list
self.axes = [ y for x in temp for y in x]
#axis: date
for i in self.axes:
i.xaxis.set_major_formatter(md.DateFormatter('%H:%M:%S'))
i.xaxis_date()
#make space and rotate xtick labels
self.figure.autofmt_xdate()
#Matplotlib <-> GTK
self.canvas = FigureCanvas(self.figure) # a gtk.DrawingArea
self.canvas.set_flags(gtk.HAS_FOCUS|gtk.CAN_FOCUS)
self.canvas.grab_focus()
self.canvas.show()
graphview.pack_start(self.canvas, True, True)
def plot(self, filename_list):
index = 0
for filename in filename_list:
data = genfromtxt(filename, delimiter=',') #get dataset
if data.size != 0: #only if file isn't empty
if index < len(self.axes): #print each dataset on a different subplot
self.axes[index].plot(data[0:, 1], data[0:, 0], 'bo')
index +=1
self.canvas.draw()
#not yet implemented
def clear_plot(self):
pass
If I plot some datasets, I get the following output:
http://i.imgur.com/3ngYTNr.png (sorry, I still don't have enough reputation to embedd pictures)
Moreover, I am not really sure if sharing the x-axis is a really good idea, because it is possible that the x-values differ in every subplot (for example: in the first subplot, the x-values ranges from 8:00am - 11:00am and in the second subplot the x-values ranges from 7:00pm - 9:00pm).
If I get rid of sharex = True, I get the following output:
http://i.imgur.com/rxHeSyJ.png (sorry, I still don't have enough reputation to embedd pictures)
As you can see, the output now looks better. However now, the labels on the x-axes are not updated. I assume that is because the last suplots are empty.
My next attempt was to use an axis for each subplot. Therefore, I made this changes:
for i in self.axes:
plt.setp(i.get_xticklabels(), visible=True, rotation = 30) #<-- I added this line...
i.xaxis.set_major_formatter(md.DateFormatter('%H:%M:%S'))
i.xaxis_date()
#self.figure.autofmt_xdate() #<--changed this line
self.figure.subplots_adjust(left=0.125, bottom=0.1, right=0.9, top=0.96, wspace=0.2, hspace=0.6) #<-- and added this line
Now I get the following output:
i.imgur.com/TmA1goE.png (sorry, I still don't have enough reputation to embedd pictures)
So with this attempt, I am basically struggling with the same problem as with Figure() and add_subplot().
I really don't know, what else I could try to make it work...
I would strongly recommend you to use pyplot.subplots() with sharex=True:
fig, axes = subplots(ncols=2, nrows=3, sharex= True)
Then you access each axes using:
ax = axes[i,j]
And you can plot doing:
ax.plot(...)
To control the number of ticks for each AxesSubplot you can use:
ax.locator_params(axis='x', nbins=6)
OBS: axis can be 'x', 'y' or 'both'

Categories