Where to Discuss?

Local Group

Preface

Goal: Realtime data plot using Pandas.

I’ve been wondering what if I can simulate sensor data and show the realtime in the same manner with real device such as thermocouple. So I’m thinking about generating random data, with adjustable feature, so I can reach any temperature that I want.

Then publish the data using MQTT topic just like what device does, subscribe the topic using other script, and finally plot the realtime data using time series in panda.

Panda Plot: MQTT with Intereference

How to Achieve

We can start with

  • Generating Random Data.
  • Pack the result in a dataframe.
  • Plot dataframe in Matplotlib.
  • Plot in time series fashioned.

Then we can discuss about other step.

  • Publishing adjustable random data using MQTT.
  • Subscribe MQTT in time series.
  • Plot realtime data as moving lines.

This video should give you an overview, about result what we want to achieve.


1: Generating Moving Numbers

Cheap Sensor Simulator

All we have to do is having number with small random changes, one number after the other. So we can have line like chart plot later.

Python Source Code

For convenience, I share the script.

Required Package

We only need numpy so we can print array with precision later.

import numpy as np
import random
from time import sleep

Skeleton

The skeleton is simple. It is only consist one constructor, one method to update number, and one main method.

class rndGenerator:
  def __init__(self):
  def update_num(self):
  def __call__(self):

Panda Plot: Moving Numbers: Skeleton

Constructor

Imagine that our device has room temperature sensor, in a beach in a tropical island. The room can’t be cooler than five celcius degree, and the room also can’t be too hot, more than fourty celcius degree.

class rndGenerator:
  def __init__(self):
    # save initial parameter
    self.lim_lower  =  5
    self.lim_upper  = 40
    self.num_normal = 21
    self.current    = self.num_normal

Panda Plot: Moving Numbers: Constructor

We also have initial value as twenty one celcius degree. Because our room have a good air conditioner.

Random Update

More fluctuation, so we can examine better

Now we can change the temperature as below. The temperatur will follow previous value.

  new_temp = new_temp + random.uniform(-1, 1)

The issue with that code, is the the fluctuation is not enough to be observed by human eye. Since we want to differ the temperatur shift manually. We need to have more bigger change range.

  def update_num(self):
    while True:
      neo = self.current
      neo += random.uniform(-5, 5)
      if self.lim_lower <= neo <= self.lim_upper:
        break

    self.current = neo

Panda Plot: Moving Numbers: Random Update

We also guard the result, so the temperature won’t pass our limit constraint.

Main

Call directly

Now we can generate as many number as we want.

  def __call__(self):
    for i in range(0, 5):
      self.update_num()
      print("%.2f" % self.current)
      yield self.current
      sleep(0.2)

Panda Plot: Moving Numbers: Main

Execute: Run

We need to creta clas instance first, then just run the method. And finally print the result.

rndGen = rndGenerator()
numbers = np.array(list(rndGen()))

np.set_printoptions(precision=2)
print(numbers)

Panda Plot: Moving Numbers: Brief

Examine Result in CLI

Command Line Interface

We can examine the result as below:

❯ python 01-random.py
19.60
19.58
15.70
15.39
10.88
[19.6  19.58 15.7  15.39 10.88]

Panda Plot: Moving Numbers: CLI Random


2: Packing to Dataframe

We generate number in a loop. And for each item in the loop, we add the data into dataframe series.

Python Source Code

The script is available, so you can just copy-paste-examine-run.

Required Package

We need panda to use dataframe.

import pandas as pd
import numpy as np
import random

from time import sleep

Panda Plot: Packing Data Frame: Import

Skeleton

We have additional update_data method.

class rndGenerator:
  def __init__(self):
  def number_setup(self):
  def update_num(self):
  def update_data(self):   
  def __call__(self):

Panda Plot: Packing Data Frame: Skeleton

Constructor

We pack list of data series with the same length in dataframe.

class rndGenerator:
  def __init__(self):
    # save initial parameter
    self.index = 0
    self.timeframe = pd.DataFrame({
      "time": [], "temp": []  })

    self.number_setup()

Panda Plot: Packing Data Frame: Constructor

Setup Number

The same initial setup as previous.

Update Number

The same update method as previous.

Append Data Frame

We can append a time frame by using concat:

    self.timeframe = pd.concat(
      [self.timeframe, new_pair])

Update Data Frame

And here the additional method. For each new number, we pack into pair.

And then append each pair into the dataframe.

  def update_data(self):
    new_num  = self.current
    new_time = pd.Timestamp.now()

    new_pair = pd.DataFrame({
      "time": new_time,
      "temp": new_num
    }, index=[self.index])

    self.index += 1
    self.timeframe = pd.concat(
      [self.timeframe, new_pair])

Panda Plot: Packing Data Frame: Update Data

Now we have time series.

Main

Do not forget to update data frame, in the loop in the main method.

  def __call__(self):
    for i in range(0, 5):
      self.update_num()
      self.update_data()

    return self.timeframe

Panda Plot: Packing Data Frame: Main

Execute: Run

The run

np.set_printoptions(precision=2)
rndGen = rndGenerator()
print(rndGen())

Panda Plot: Packing Data Frame: Execute

Output in CLI

The numpy output is very nice.

❯ python 02-panda.py
                        time       temp
0 2023-01-28 09:23:32.145785  21.924042
1 2023-01-28 09:23:32.147596  21.968360
2 2023-01-28 09:23:32.148587  24.703646
3 2023-01-28 09:23:32.149539  29.576014
4 2023-01-28 09:23:32.150507  27.768895

Panda Plot: Packing Data Frame: Output in CLI


What is Next 🤔?

We are ready with data frame. Now we are ready to plot the data frame.

Consider continue reading [ Python - Panda - Plot Series ].