1

I have a dictionary which is updated by a for loop , I am trying to plot the key value pairs in a plot which moves or slides as the number of key value pairs keep updating and the plot just shows the 50 current values from the dictionary.

so far I have made :

for k,v in plot.items():

    plt.barh(k,v , color='blue')
    plt.pause(0.3)


plt.show()

The problem is that the values keep appending and the plots keeps growing. Is there any way to show just last 50 k,v pairs from a dictionary and and keep on updating the plot. I have also tried a function :

def live_plotter(x_vec,y1_data,line1,identifier='',pause_time=0.1):
    if line1==[]:
        # this is the call to matplotlib that allows dynamic plotting
        plt.ion()
        #fig = plt.figure(figsize=(13,6))
        #ax = fig.add_subplot(111)
        # create a variable for the line so we can later update it
        line1, = plt.plot(x_vec,y1_data,'-o', alpha=0.8)
        #update plot label/title
        plt.ylabel('Cross-correlation')
        plt.title('Title: {}'.format(identifier))


    line1.set_data(x_vec, y1_data)

    plt.pause(pause_time)

    return line1 

But this also doesn't update the plot and just appends to the plot like the code above.

I have also tried the animation function :

fig = plt.figure()
    def animate():
        for k, v in plot:
            print(k, v)
            plt.barh(k, v, color='blue')
            plt.pause(0.3)

ani = matplotlib.animation.FuncAnimation(fig, animate, interval=1000)

plt.show()

Reproducible example:

import matplotlib.pyplot as plt
import matplotlib.animation

plot_base={'00002200': 986.11152642551519,
       '00002201': 989.90915858383369,
       '00002202': 992.87095144990781,
       '00002203': 994.89971071876084,
       '00002204': 992.92424216660561,
       '00002205': 993.19845750930426,
       '00002206': 988.88001766153957,
       '00002207': 988.95062195981848,
       '00002208': 993.17138084871465,
       '00002209': 993.9863425202193,
       '00002210': 993.43551440410283,
       '00002211': 993.04540624076844,
       '00002212': 991.40048097057559,
       '00002213': 993.16124311517319,
       '00002214': 994.06785011666204,
       '00002215': 985.24294182260996,
       '00002216': 990.5369409623512,
       '00002217': 991.83512034302737,
       '00002218': 993.43756392913269,
       '00002219': 989.77919409784511,
       '00002220': 988.09683378239572,
       '00002221': 992.19961090836773,
       '00002222': 992.69477342507912,
       '00002223': 992.37890673842412,
       '00002224': 991.55520651752556,
       '00002225': 992.15414070360941,
       '00002226': 991.49292821478309,
       '00002227': 994.75013161999084,
       '00002228': 991.54858727670728,
       '00002229': 993.22846583401292,
       '00002230': 993.88719133150084,
       '00002231': 992.89934842358855,
       '00002232': 991.10582582918869,
       '00002233': 993.24750746833467,
       '00002234': 992.6478137931806,
       '00002235': 991.2614284514957,
       '00002236': 994.38800336488725}

plot={}

for k,v in plot_base.items():
    plot.update({k: v})


for k,v in plot.items():
    bar, = plt.bar(k, v, color='blue')
    plt.pause(0.3)
plt.plot()


'''
def animate(i):
    bars = []
    for k, v in plot.items():
        bar, = plt.bar(k, v, color='blue')
        bars.append(bar)
    return bars


fig=plt.figure(figsize=(12 ,7))

ani = matplotlib.animation.FuncAnimation(fig, animate, interval=1000, blit=True)

plt.show()
'''

When we run this code the values from the dictionary keep appending to the x-axis , that's why I want to make it scroll able(auto-scroll) , The triple quoted code shows the animation part, this make the whole graph appear at once.

abhishake
  • 131
  • 1
  • 12
  • Are you doing anything with the data you do not want to plot? Can it be discarded? Does the data have to be in a dictionary? – wwii Jan 13 '19 at 16:33
  • I want to make a scroll-able plot so that the data which was updated when the for loop started can also be checked , if it's possible. – abhishake Jan 13 '19 at 16:37
  • I can get the data from two separate lists as well – abhishake Jan 13 '19 at 16:38
  • Maybe I misunderstood your question. Is your problem that each time you add a *line* to the plot you want to remove the *oldest* line from the plot? You want the plot to be animated so the lines are continually changing and only fifty are ever displayed? – wwii Jan 13 '19 at 17:01
  • 1
    Possible duplicate of [matplotlib animation removing lines during update](https://stackoverflow.com/questions/49071515/matplotlib-animation-removing-lines-during-update) – wwii Jan 13 '19 at 17:04
  • yes I want to change the plot and remove the oldest bar line , so that the number of key value pairs is just fifty (the latest fifty which is updated in the dictionary) – abhishake Jan 13 '19 at 17:18
  • Since dictionaries are in general not ordered, you cannot know which are the last 50 elements of it. Hence better use lists. – ImportanceOfBeingErnest Jan 13 '19 at 17:24
  • Will the new data change the scale of the x or y axes? – wwii Jan 13 '19 at 18:42
  • yes , I was just going to point that out as the "Possible duplicate of matplotlib animation removing lines during update " does not apply in my case , I tried from that example to use animation but it just shows an empty plot. I'll edit my question and add the code. – abhishake Jan 13 '19 at 19:15
  • the new data will modify the x axis continuously, the dictionary I am plotting from looks like : {1001:99.1, 1002:99.4,1003:98.3,1004:96.4,1005:98.6}, as each subsequent key:value pair is appended to the dictionary the keys plotted on x-axis keep on increasing , it's what I want to make scroll able, the y-axis scale adjusts itself with `plt.autoscale()` – abhishake Jan 13 '19 at 19:22
  • Look at the answer for the link from above, see how the animate function returns a list of lines (artists)? - try accumulating the bars (`bar=plt.barh(k, v, color='blue'); bars.append(bar) ...`) and return them, also turn blitting on `blit=True`. – wwii Jan 13 '19 at 19:33
  • @wwii I have added reproducible example of the code that I am trying , this shows the problem more elaborately, I hope this will help in making the question more clear. I have also added the animation bit that I have tried -Thanks – abhishake Jan 13 '19 at 20:06
  • @ImportanceOfBeingErnest I have added some example code to show the problem more clearly. – abhishake Jan 13 '19 at 20:08
  • I have problems understanding what exactly you are trying to achieve here. Do you want to animate the bars appearing one by one in the plot? – ImportanceOfBeingErnest Jan 13 '19 at 20:45
  • @ImportanceOfBeingErnest I want the plot to show 20 bars at a time , when a new bar is added the the first bar should be deleted from the plot or scroll forward. – abhishake Jan 13 '19 at 20:53
  • What or who adds bars? In your code all the values are already there. – ImportanceOfBeingErnest Jan 13 '19 at 20:55
  • @ImportanceOfBeingErnest I made this as an example to show the dictionary I am trying to plot from , I am running a for loop on each file as it appears in a directory , extracting two values from each file , appending the values in a dictionary and then making a bar plot , all this is done inside the for loop. The directory can contain more than 4000 files which need to me monitored in real time for these values , that's why I want to make the plot scroll-able and append new bars as the for loop continues to extract values. – abhishake Jan 13 '19 at 21:03

1 Answers1

0

I don't think I understand the data structure used here, so this focuses on the animating part. Suppose you have a function store.get_last_20_values from which to obtain the data you want to plot. Then the animation would look like:

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np

fig, ax = plt.subplots()

def animate(i):
    cat, val = store.get_last_20_values()
    ax.clear()
    ax.barh(cat, val)

ani = FuncAnimation(fig, animate, interval=1000)
plt.show()

For completeness here is how that function could look like, to make the above runnable.

class Store():
    x = []
    y = []

    def update(self):
        n = np.random.randint(0,5)
        x = np.random.randint(100000,999999, size=n).astype(str)
        y = np.random.randint(50,999, size=n)
        self.x.extend(x)
        self.y.extend(y)
        self.x = self.x[-20:]
        self.y = self.y[-20:]

    def get_last_20_values(self):
        # Some function to return 20 values. Need to adapt to custom needs.
        self.update()
        return self.x, self.y

store = Store()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • @ImpoatanceOfBeingErnest , Thanks for the suggested solution , but by problem is that I am trying to get the values out of a for loop , I am a bit confused on how to extract those values from the update function, I am appending the values to two separate lists within the for loop , if I append the values outside the for loop then I wait till the loop finishes to get the values. Is there any way to get the values in realtime as the loop is running. – abhishake Jan 14 '19 at 01:03
  • In principle, `FuncAnimation` runs a loop for you, in the sense that it repeatedly calls the `animate` function. Is it that you would rather want to let the loop run independently and let matplotlib display "snap-shots" of the current loop status? – ImportanceOfBeingErnest Jan 14 '19 at 09:40
  • I am looping through each file in a directory , so far I have been successfully been able to animate the list contents which are outside the loop. As I am appending values to the list from the for loop, the main problem is that the loop takes a long time to finish and iterate through each file in the folder and the animation doesn't appear till the loop is complete. What I want is the animation to change or scroll as each new value is extracted from the for loop. I hope this will help in clarifying the issue more. , P.S Thanks for help that you have given so far. – abhishake Jan 14 '19 at 14:25
  • Of course you can perform the search algorithm and the drawing at the same time, if you don't care about it being only as fast as matplotlib can draw new data. But, consider that if the loop by itself is already taking a lot of time it would become ***much*** slower if it were to also plot something in each step. Hence I was asking if you want to show a snapshot of where the loop currently is. – ImportanceOfBeingErnest Jan 14 '19 at 14:33
  • Speed is not the concern as later I will implement this script to monitor a directory which gets populated every ~10 seconds, so I will leave this for loop to extract values and the animate running and it will have enough time to update. I am just trying to get my head around how to plot the values like a movie (auto scrolling plot while the for loop runs) – abhishake Jan 14 '19 at 14:40
  • To summarize the problem again, the task is : There is a folder with 400 files, I am extracting the values from bytes 14-18 and 155-159 from each file as int and float in a for loop, now I want to plot the values as they are extracted from each file in real time (update the plot and scroll forward after each value is extracted) – abhishake Jan 14 '19 at 14:53
  • You will probably want to use a solution like [this one](https://stackoverflow.com/a/48389850/4124317) where you have a worker thread that has the loop running and a `FuncAnimation` in the main thread that fetches the values from the worker. – ImportanceOfBeingErnest Jan 14 '19 at 15:45