By Noël Jung
import math
from datetime import datetime
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import graph_style # Contains styling parameters for the plots.
I wrote the data into a csv file. Via the parse_dates and date_parser arguments we can specify which columns pandas should treat as timestamps. I also transform the date into a more readable format (HH:MM:SS).
def time_parser(time_stamp):
""" Specifies which format the timestamp in the input file has."""
return pd.to_datetime(time_stamp, format='%H%M%S')
# Load data, specify which column contains time info.
CSV_FILE = r"../CSV/20230416_1110_comp.csv"
temp_test_data = pd.read_csv(CSV_FILE, parse_dates=["Time"], date_parser=time_parser)
# Transform time column into a readable format.
temp_test_data["Time"] = temp_test_data["Time"].dt.strftime('%H:%M:%S')
Furthermore, I think it is good to know how much time has passed since the experiment has started. Using the DataFrame.apply method we call a function that calculates the difference in minutes between the current, and the first time value in minutes for each row.
def time_dif(t1, t2, resolution="min", input_format="%H:%M:%S"):
""" Returns differences between two time stamps."""
absl_time_1 = datetime.strptime(t1, input_format)
absl_time_2 = datetime.strptime(t2, input_format)
diff_in_secs = (absl_time_2 - absl_time_1).total_seconds()
if resolution == "min":
return int(divmod(diff_in_secs, 60)[0])
return diff_in_secs
# Create column containing the time passed from start of experiment in min.
temp_test_data["min_passed"] = temp_test_data.apply(lambda row: time_dif(temp_test_data.iloc[0]["Time"],
row["Time"],input_format='%H:%M:%S' ), axis=1)
Let's take a look at the DataFrame.
temp_test_data.head(3)
| Time | name_dev_1 | T_dev_1 | name_dev_2 | T_dev_2 | T_CPU | min_passed | |
|---|---|---|---|---|---|---|---|
| 0 | 11:10:48 | 28-3c01f09505ec | 20.562 | 28-3c01f0953253 | 20.437 | 33.6 | 0 |
| 1 | 11:11:50 | 28-3c01f09505ec | 20.562 | 28-3c01f0953253 | 20.562 | 34.0 | 1 |
| 2 | 11:12:52 | 28-3c01f09505ec | 20.625 | 28-3c01f0953253 | 20.625 | 36.0 | 2 |
In our new column, we can easily see how long the measurement has gone on for. I had two temperatures sensors connected at the same time and was also monitoring the temperature of the CPU to see whether the heat sink I had installed had any effect and to estimate whether the Pi would get hot over long run times. I also saved the name of the temperature sensors as I read them from the Pi. I was afraid that the order of which I detect the devices and read their values could change. Let's quickly confirm that the name_dev_1 and name_dev_2 always contain the same value.
devices_column_1 = temp_test_data["name_dev_1"].unique()
devices_column_2 = temp_test_data["name_dev_2"].unique()
print(f'Values sensor 1: {devices_column_1}. Values sensor 2: {devices_column_2}.')
Values sensor 1: ['28-3c01f09505ec']. Values sensor 2: ['28-3c01f0953253'].
def floor_timestamp(timestamp, old_format="%H:%M:%S", new_format="%H:%M"):
"""Rounds down (!) to minutes."""
absl_time = datetime.strptime(timestamp, old_format)
return absl_time.strftime(new_format)
def space_time_label(ticks, n_ticks=50, ceil_to_min=False):
"""Takes a list of ticks and reduces it."""
ith_of_n = int((math.floor(len(ticks)/n_ticks)))
reduced_ticks = ticks[::ith_of_n] # Retrieve every ith tick.
if ceil_to_min:
return list(map(lambda time: floor_timestamp(time), reduced_ticks))
return list(reduced_ticks)
# Set theme for graphs.
axes_style = sns.axes_style(style=graph_style.style)
sns.set(rc={"figure.figsize":(10, 4), })
sns.set_theme(style=axes_style, palette=graph_style.colors_pomegranate ,font=graph_style.FONT)
# Space y-labels, select y-ticks (every 10 values of the min_passed column).
spaced_out_ticks = space_time_label(temp_test_data["Time"], 10)
# Initialize graphs.
T_CPU_plot = sns.lineplot(x=temp_test_data["min_passed"],
y=temp_test_data["T_CPU"], **graph_style.lineplot_kwargs)
# Apply styles.
T_CPU_plot.set_title(label="CPU temperature over time", **graph_style.title_style)
T_CPU_plot.set_xlabel(xlabel="Time in minutes", **graph_style.axes_style)
T_CPU_plot.set_ylabel(ylabel="Temperature in °C", **graph_style.axes_style)
T_CPU_plot.tick_params( **graph_style.tick_style)
T_CPU_plot.set(xlim=(0,576), ylim=(30, 50));
# Rearrange dataframe from wide to long form (seaborn requirement).
long_T_dev_df = temp_test_data.melt(id_vars=["min_passed"],
value_vars=["T_dev_1", "T_dev_2"], var_name="dev_nr", value_name="T_in_C" )
# Initialize graphs.
T_dev_plot = sns.lineplot(data=long_T_dev_df, y="T_in_C", x="min_passed", hue="dev_nr", **graph_style.lineplot_kwargs)
# Apply legend styles.
handles, labels = T_dev_plot.get_legend_handles_labels()
legend_title = T_dev_plot.legend(title="Thermometer", handles=handles, labels=["1", "2",], **graph_style.legend_style)
plt.setp(legend_title.get_title(), **graph_style.legend_title_style)
# Apply graph styles.
T_dev_plot.set_title(label="Measured temperatures by two devices", **graph_style.title_style)
T_dev_plot.set_xlabel(xlabel="Time in minutes", **graph_style.axes_style)
T_dev_plot.set_ylabel(ylabel="Temperature in °C", **graph_style.axes_style)
T_dev_plot.tick_params( **graph_style.tick_style)
T_dev_plot.set(xlim=(0,576), ylim=(20, 35));
In the beginning, I poured warm water into the glass. Later, at around minute 200, I added a bit of boiling water. These are the two spikes seen in the graph. Both sensors seem to show approximately the same temperature (the lines lie on top of each other). We can also confirm that by looking at the numbers.
T_diff = temp_test_data.apply(lambda row : abs(row["T_dev_1"] - row["T_dev_2"]), axis=1)
print(f"The temperature difference is maximum {T_diff.max()} and on average {T_diff.mean():.2f} °C.")
The temperature difference is maximum 0.25 and on average 0.14 °C.