Matplotlib соединяет конец данных после обновления анимации

1

У меня проблема, подобная этой статье, но немного более расстраивающей.

Я использую matplotlib для чтения из файла, который передается из другого приложения. По какой-то причине концы данных подключаются только после того, как анимация (animation.FuncAnimation) завершила свое первое обновление. Вот несколько изображений:

Это до обновления: Изображение 174551

И это после обновления: Изображение 174551

Любые идеи относительно того, почему это может произойти? Вот мой код:

import json
import itertools
import dateutil.parser

import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
import scipy.signal as sig
import numpy as np
import pylab as plt

sensors = {}
data = []
lastLineReadNum = 0

class Sensor:

    def __init__(self, name, points = 0, lastReading = 0):
        self.points = points
        self.lastReading = lastReading
        self.name = name
        self.x = []
        self.y = []

class ScanResult:
    def __init__(self, name, id, rssi, macs, time):
        self.name = name
        self.id = id
        self.rssi = rssi
        self.macs = macs
        # Is not an integer, but a datetime.datetime
        self.time = time

def readJSONFile(filepath):

    with open(filepath, "r") as read_file:
        global lastLineReadNum
        # Load json results into an object that holds important info
        for line in itertools.islice(read_file, lastLineReadNum, None):
            temp = json.loads(line)
            # Only reads the most recent points...
            data.append(ScanResult(name = temp["dev_id"], 
                            id = temp["hardware_serial"], 
                            rssi = temp["payload_fields"]["rssis"],
                            macs = temp["payload_fields"]["macs"],
                            time = dateutil.parser.parse(temp["metadata"]["time"])))
            lastLineReadNum += 1

        return data

style.use('fivethirtyeight')
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)

def smooth(y, box_pts):
    box = np.ones(box_pts)/box_pts
    y_smooth = np.convolve(y, box, mode='same')
    return y_smooth

def determineClosestSensor():
    global sensors

    #sensors.append(Sensor(time = xs3, rssi = ys3))

def determineXAxisTime(scanresult):
    return ((scanresult.time.hour * 3600) + (scanresult.time.minute * 60) + (scanresult.time.second)) / 1000.0

def animate(i):
    data = readJSONFile(filepath = "C:/python_testing/rssi_logging_json.json")

    for scan in data:
        sensor = sensors.get(scan.name)

        # First time seeing the sensor
        if(sensor == None):
            sensors[scan.name] = Sensor(scan.name)
            sensor = sensors.get(scan.name)
            sensor.name = scan.name
            sensor.x.append(determineXAxisTime(scan))
            sensor.y.append(scan.rssi)

        else:
            sensor.x.append(determineXAxisTime(scan))
            sensor.y.append(scan.rssi)

    ax1.clear()

    #basic smoothing using nearby averages

    #y_smooth3 = smooth(np.ndarray.flatten(np.asarray(sensors.get("sentrius_sensor_3").y)), 1)

    for graphItem in sensors.itervalues():
        smoothed = smooth(np.ndarray.flatten(np.asarray(graphItem.y)), 1)
        ax1.plot(graphItem.x, smoothed, label = graphItem.name, linewidth = 2.0)

    ax1.legend()

    determineClosestSensor()

    fig.suptitle("Live RSSI Graph from Sentrius Sensors", fontsize = 14)


def main():
    ani = animation.FuncAnimation(fig, animate, interval = 15000)

    plt.show()

if __name__ == "__main__":
    main()
  • 0
    Небольшое примечание: название библиотеки - matplotlib.
Теги:
matplotlib
python-2.7

1 ответ

1
Лучший ответ

Насколько я могу судить, вы обновляете свои данные на каждом шаге анимации, добавляя к существующим наборам данных, но тогда это означает, что ваша последняя точка x с первого шага сопровождается первой точкой x на втором этапе, что приводит к перемотать в сюжет. Это отображается как линия, соединяющая последний набор данных с первым; остальная часть данных не изменяется.

Соответствующая часть animate:

def animate(i):
    data = readJSONFile(filepath = "C:/python_testing/rssi_logging_json.json")

    for scan in data:
        sensor = sensors.get(scan.name)

        # First time seeing the sensor
        if(sensor is None): # always check for None with 'is'!
            ... # stuff here    
        else:
            sensor.x.append(determineXAxisTime(scan)) # always append!
            sensor.y.append(scan.rssi)                # always append!

    ... # rest of the stuff here

Таким образом, на каждом шаге анимации вы 1. загружаете один и тот же файл JSON. 2. добавляйте те же данные к существующему датчику, идентифицированному sensors.get(scan.name) 3. sensors.get(scan.name) сюжет без использования i.

Во-первых, ваш animate должен, естественно, использовать индекс i: вы пытаетесь сделать что-то относительно шага i. Я не вижу, как i где-либо пользуюсь.

Во-вторых, ваш animate должен быть максимально легким, чтобы получить гладкую анимацию. Загрузите свои данные один раз перед построением графика и обработайте только различия чертежа в animate. Это будет связано с нарезкой или управлением вашими данными в зависимости от i.

Конечно, если файл действительно меняется с шага на шаг, и это фактическая динамика в анимации (т.е. i - фиктивная переменная, которая никогда не используется), все, что вам нужно сделать, это нуль-инициализировать все данные графика в каждом шаг. Начните с чистого листа. Затем вы перестанете видеть строки, соответствующие этим искусственным скачкам в данных. Но опять же, если вы хотите animate светлый вес, вы должны изучить манипулирование базовыми данными существующих сюжетов, а не повторять все все время (тем более, что призывы к ax1.plot будут содержать более ранние точки на холсте, что не так, как вы обычно хотите в анимации).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню