from copy import deepcopy
from datetime import datetime, timedelta
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import matplotlib.colors as colors
from matplotlib.dates import (
DateFormatter,
DayLocator,
AutoDateLocator,
AutoDateFormatter,
)
from matplotlib.figure import Figure
from matplotlib.collections import PathCollection
from typing import List, Dict, Tuple, Union, Any
# import from package
from resistics.common.base import ResisticsBase
from resistics.common.print import arrayToString
from resistics.window.utils import gArray2datetime
from resistics.common.plot import (
getPlotRowsAndCols,
getViewFonts,
colorbar2dOther,
colorbar2dTime,
)
[docs]class StatisticData(ResisticsBase):
"""Class for holding information about a statistic
Attributes
----------
statName : str
The name of the statistic
refTime : datetime.datetime
The reference time of the project
sampleFreq : float
The sampling frequency of the data
winSize : int
Window size in samples
winOlap : int
Window overlap in samples
numWindows : int
Number of windows
winStats : List[str]
Values calculated for the statistic
numStatsPerWindow : int
The number of statistics per window (length of winStats)
stats : np.ndarray
The statistic data of size number of windows * number evaluation frequencies * number of window stats
evalFreq : List, np.ndarray
The evaluation frequencies for the statistic data
freq2index : Dict
Mapping from evaluation frequency to index
globalIndices : List, np.ndarray
Array of global indices. Allows local -> global conversion
global2localMap : Dict
Dictionary which maps global indices to local. Allows global -> local conversion
comments : List[str]
Statistic comments
dtype : str (default "float")
The data type of the statistic values
maxcols : int (default 4)
The number of columns in plots
globalDatesStart : List, np.ndarray
Global window start datetimes
globalDatesStop : List, np.ndarray
Global window stop datetimes
Methods
-------
__init__(sampleFreq, numLevels, evalFreq, kwargs)
Initialise maskData
getStats(includewindows, maskwindows)
Get the statistics array and choose to either include some windows or mask some windows
getUnmaskedIndices(maskwindows)
Return the local indices to use given a set of global indices to exclude
getStatLocal(localIndex)
Get statistic values for a local window index
getStatGlobal(globalIndex)
Get statistic values for a global window index
getGlobalIndex(localIndex)
Get the global window index for a local window index
getLocalIndicesF(globalIndices)
Get local indices for an list or array of global indices
getGlobalDates()
Get the global index dates for all the windows
setStatParams(numWindows, winStats, evalFreq)
Set the statistic parameters and prepare the data arrays
addStat(localIndex, globalIndex, stat)
Add a statistic, providing the local index, the corresponding global index and the statististic values
getComments()
Get a deepcopy of the comments
addComment(comment)
Add a comment to the comments
view(eFreqI, **kwargs)
View statistic values for a evaluation frequency (time on x axis, statistic values on y axis)
histogram(eFreqI, **kwargs)
View a histogram of the statistic values for each statistic component for a single evaluation frequency
crossplot(eFreqI, **kwargs)
View a crossplot of the data
addColourbar(plot, cax, title, plotfonts)
Add a colourbar to a plot
addColourbarDates(plot, cax, title, plotfonts)
Add a coloubar the represents dates to the plot
dateTicks(gIndices, dates, timeNum)
Deal with the dateTicks of the plot
calcColourData(plotData, val, eFreqI, keywords)
Calculate colours
getRowsCols(maxcols, numStats)
Get the number of rows and columns for a plot
printList()
Class status returned as list of strings
"""
def __init__(
self,
statName: str,
refTime: datetime,
sampleFreq: float,
winSize: int,
winOlap: int,
**kwargs
):
"""Initialise statistic data
Parameters
----------
statName : str
Name of the statistic
refTime : datetime.datetime
Reference time for the project
sampleFreq : float
Sample frequency of the data in Hz
winSize : int
The window size in samples
winOlap : int
The window overlap in samples
kwargs : Dict
Two optional arguments: "name" for statistic name and "stats" for the statistics to use
"""
self.statName: str = statName
self.refTime = refTime
if isinstance(self.refTime, str):
self.refTime = datetime.strptime(self.refTime, "%Y-%m-%d %H:%M:%S.%f")
self.sampleFreq: float = sampleFreq
self.winSize: int = winSize
self.winOlap: int = winOlap
# defaults from keywords
self.numWindows: int = 0
self.winStats: List[str] = []
self.numStatsPerWindow: int = 0
self.evalFreq: Union[List, np.ndarray] = []
self.freq2index: Dict = {}
self.globalIndices: Union[List, np.ndarray] = []
self.global2localMap: Dict = {}
self.comments: List[str] = []
self.dtype: str = "float"
# global dates
self.globalDatesStarts: Union[List, np.ndarray] = []
self.globalDatesStops: Union[List, np.ndarray] = []
# plot params
self.maxcols: int = 4
# parse keywords
self.initialiseFromKeywords(kwargs)
[docs] def initialiseFromKeywords(self, keywords):
"""Initialise object properties using the keywords"""
if "numWindows" in keywords:
self.numWindows = keywords["numWindows"]
if "winStats" in keywords:
self.winStats = keywords["winStats"]
self.numStatsPerWindow = len(self.winStats)
if "evalFreq" in keywords:
self.evalFreq = keywords["evalFreq"]
for idx, eFreq in enumerate(self.evalFreq):
self.freq2index[eFreq] = idx
if "stats" in keywords:
self.stats = keywords["stats"]
if "globalIndices" in keywords:
self.globalIndices = keywords["globalIndices"]
# make the global 2 local map
for ii in np.arange(0, len(self.globalIndices)):
self.global2localMap[self.globalIndices[ii]] = ii
if "comment" in keywords:
self.comments = keywords["comment"]
if "dtype" in keywords:
self.dtype = keywords["dtype"]
[docs] def getStats(
self,
includewindows: Union[np.ndarray, List, None] = None,
maskwindows: Union[np.ndarray, List, None] = None,
) -> np.ndarray:
"""Get statistics when either selecting a set of indices or wanting to mask a set of indices
Parameters
----------
includewindows : List, np.ndarray, optional
Windows to get. This should be a list of numpy array of global indices.
maskwindows : List, np.ndarray, optional
Windows to exclude. This is expected to be a list, set or numpy array of global indices.
Returns
-------
np.ndarray
Statistics limited by the window selection options
"""
if includewindows is not None:
includewindows = self.getLocalIndices(includewindows)
return self.stats[includewindows, :]
elif maskwindows is not None and len(maskwindows) > 0:
includewindows = self.getUnmaskedIndices(maskwindows)
if includewindows is not None:
return self.stats[includewindows, :]
return self.stats
[docs] def getUnmaskedIndices(
self, maskWindows: Union[List, np.ndarray]
) -> Union[List, None]:
"""Given a list of global windows to mask, returns the set of indices to include in the plot
Parameters
----------
maskWindows : List, np.ndarray
Global indices of windows to mask
Returns
-------
List, None
List of indices to include. None if all windows should be included
"""
includewindows = set(self.globalIndices) - set(maskWindows)
if len(includewindows) == self.numWindows:
# nothing to mask
return None
return self.getLocalIndices(includewindows)
[docs] def getStatLocal(self, localIndex: int) -> np.ndarray:
"""Get statistics for a local window index
Parameters
----------
localIndex : int
Local index of the window
Returns
-------
out : np.ndarray
Statistics for the local window
"""
return self.stats[localIndex, :]
[docs] def getStatGlobal(self, globalIndex: int) -> np.ndarray:
"""Get statistics for a local window index
Parameters
----------
globalIndex : int
Global index of the window
Returns
-------
out : np.ndarray
Statistics for the local window
"""
if globalIndex not in self.global2localMap:
self.printError(
"There are two statistics with the same global window index"
)
self.printError("This should never happen")
self.printError("Exiting", quitRun=True)
localIndex = self.global2localMap[globalIndex]
return self.stats[localIndex]
[docs] def getGlobalIndex(self, localIndex):
"""Get global index for local index
Parameters
----------
localIndex : int
Local index of the window
Returns
-------
globalIndex : int
Global index for local index
"""
return self.globalIndices[localIndex]
[docs] def getLocalIndices(
self, globalIndices: Tuple[List[int], np.ndarray]
) -> List[int]:
"""Converts a list or array of global indices to local indices
Parameters
----------
globalIndices : List, np.ndarray
Global indices to convert into local indices
Returns
-------
List
List of local indices
"""
indices = []
for win in globalIndices:
indices.append(self.global2localMap[win])
return indices
[docs] def getGlobalDates(self):
"""Get the global start dates and end dates for each window
Returns
-------
np.ndarray
The global window start times
"""
# want to get the start date for each window here
if len(self.globalDatesStarts) != self.numWindows:
self.globalDatesStarts, self.globalDatesStops = gArray2datetime(
self.globalIndices,
self.refTime,
self.sampleFreq,
self.winSize,
self.winOlap,
)
return self.globalDatesStarts
[docs] def setStatParams(
self, numWindows: int, winStats: List[str], evalFreq, dtype: Any = None
):
"""Set the statistic parameters
Parameters
----------
numWindows : int
Number of windows for which the statistic has been or will be calculated
winStats : int
The parameters in the statistic
evalFreq : List
A list of evaluation frequencies
dtype : str, optional
The datatype of the statistic values
"""
self.numWindows = numWindows
# details about the statistics
self.winStats = winStats
self.numStatsPerWindow = len(winStats)
# details about the evaluation frequencies
self.evalFreq = evalFreq
for idx, eFreq in enumerate(evalFreq):
self.freq2index[eFreq] = idx
# data type
if not (dtype is None):
self.dtype = dtype
self.stats = np.empty(
shape=(self.numWindows, self.evalFreq.size, self.numStatsPerWindow),
dtype=self.dtype,
)
# an array to hold global indices and a dictionary to map them back to local indices
self.globalIndices = np.empty(shape=(self.numWindows), dtype=int)
self.global2localMap = {}
[docs] def addStat(self, localIndex: int, globalIndex: int, stat) -> None:
"""Add statistic values for a window for all evaluation frequencies
Parameters
----------
localIndex : int
Local index of the window
globalIndex : int
Global index of the window
stat : Dict
An dictionary of dictionaries. First set of key, values are evaluationFrequencies and dictionaries, second set of key, values are statistic names in winStats and their corresponding value for the window
"""
# add the data - the data is in the format [eFreq][key][val]
for idx, eFreq in enumerate(self.evalFreq):
# do it in this order because sorted
for ist, st in enumerate(self.winStats):
self.stats[localIndex, idx, ist] = stat[eFreq][st]
# then add the global index
self.globalIndices[localIndex] = globalIndex
self.global2localMap[globalIndex] = localIndex
[docs] def view(self, eFreqI: int, **kwargs) -> Figure:
"""Plot statistics for evaluation frequency index
Plots a simple scatter of each statistic with datetime on the xaxis (datetime of the window start dates). Number of subplots is equal to numStaStatPerWindow.
Parameters
----------
eFreqI : int
Evaluation frequency index
maskwindows : List, np.ndarray
Global windows to exclude
fig : matplotlib.pyplot.figure, optional
A figure object
plotfonts : Dict, optional
A dictionary of plot fonts
label : str, optional
Label for the plots
clim : List, optional
Limits for colourbar axis
xlim : List, optional
Limits for the x axis
ylim : List, optional
Limits for the y axis
colortitle : str, optional
Title for the colourbar
legened : bool
Boolean flag for adding a legend
Returns
-------
plt.figure
Matplotlib figure object
"""
# get windows to plot and global dates
maskWindows = kwargs["maskwindows"] if "maskwindows" in kwargs else []
plotIndices = self.getUnmaskedIndices(maskWindows)
globalDates = self.getGlobalDates()
eFreq = self.evalFreq[eFreqI]
# plot params
nrows, ncols = self.getRowsCols(self.maxcols)
plotfonts = kwargs["plotfonts"] if "plotfonts" in kwargs else getViewFonts()
fig: plt.figure = (
plt.figure(kwargs["fig"].number)
if "fig" in kwargs
else plt.figure(figsize=(4 * ncols, 5 * nrows))
)
st = fig.suptitle(
"{} data for evaluation frequency: {}".format(self.statName, eFreq),
fontsize=plotfonts["suptitle"],
)
st.set_y(0.98)
# plot the data
for idx, val in enumerate(self.winStats):
ax = plt.subplot(nrows, ncols, idx + 1)
plt.title("Value {}".format(val), fontsize=plotfonts["title"])
label = kwargs["label"] if "label" in kwargs else eFreq
# limit the data by plotIndices if not False
plotData = np.squeeze(self.stats[:, eFreqI, idx])
plotDates = globalDates
if plotIndices is not None:
plotData = plotData[plotIndices]
plotDates = plotDates[plotIndices]
# the colourdata
colourbool, colourdata, cmap = self.calcColourData(
plotData, val, eFreqI, kwargs
)
# scatter plot
if not colourbool:
scat = plt.scatter(
plotDates,
plotData,
edgecolors="none",
marker="o",
s=12,
label=label,
)
else:
scat = plt.scatter(
plotDates,
plotData,
c=colourdata,
edgecolors="none",
marker="o",
s=12,
cmap=cmap,
label=label,
)
clim = (
kwargs["clim"]
if ("clim" in kwargs and len(kwargs["clim"]) > 0)
else [colourdata.min(), colourdata.max()]
)
scat.set_clim(clim)
# x axis options
plt.xlabel("Time", fontsize=plotfonts["axisLabel"])
xlim = (
kwargs["xlim"]
if ("xlim" in kwargs and len(kwargs["xlim"]) > 0)
else [globalDates[0], globalDates[-1]]
)
plt.xlim(xlim)
ax.format_xdata = DateFormatter("%H-%M-%S")
fig.autofmt_xdate()
# y axis options
if "ylim" in kwargs and len(kwargs["ylim"]) > 0:
plt.ylim(kwargs["ylim"])
plt.ylabel("Value {}".format(val), fontsize=plotfonts["axisLabel"])
# set tick sizes
for label in ax.get_xticklabels() + ax.get_yticklabels():
label.set_fontsize(plotfonts["axisTicks"])
plt.grid(True, ls="--")
# legend
if "legend" in kwargs and kwargs["legend"]:
plt.legend(loc=4)
# show if the figure is not in keywords
if "fig" not in kwargs:
if colourbool:
fig.tight_layout(rect=[0.02, 0.02, 0.85, 0.92])
cax = fig.add_axes([0.88, 0.10, 0.03, 0.80])
colourtitle = (
kwargs["colortitle"] if "colortitle" in kwargs else "Value"
)
self.addColourbar(scat, cax, colourtitle, plotfonts)
else:
fig.tight_layout(rect=[0.02, 0.02, 0.98, 0.92])
plt.show()
return fig
[docs] def histogram(self, eFreqI: int, **kwargs) -> Figure:
"""Plot statistics for evaluation frequency index
Plots a histogram of each statistic with bins on the xaxis and count on the yaxis. Ideal for exploring the distribution of statistic values over the windows.
Parameters
----------
eFreqI : int
Evaluation frequency index
maskwindows : List, np.ndarray
Global windows to exclude
numbins : int
The number of bins for the histogram data binning
fig : matplotlib.pyplot.figure, optional
A figure object
plotfonts : Dict, optional
A dictionary of plot fonts
label : str, optional
Label for the plots
xlim : List, optional
Limits for the x axis
legened : bool
Boolean flag for adding a legend
Returns
-------
plt.figure
Matplotlib figure object
"""
# deal with maskwindows, which are global indices
maskWindows = kwargs["maskwindows"] if "maskwindows" in kwargs else []
plotIndices = self.getUnmaskedIndices(maskWindows)
eFreq = self.evalFreq[eFreqI]
# plot options
numbins = kwargs["numbins"] if "numbins" in kwargs else 40
nrows, ncols = self.getRowsCols(self.maxcols)
plotfonts = kwargs["plotfonts"] if "plotfonts" in kwargs else getViewFonts()
fig = (
plt.figure(kwargs["fig"].number)
if "fig" in kwargs
else plt.figure(figsize=(4 * ncols, 4 * nrows))
)
st = fig.suptitle(
"{} data for evaluation frequency: {}".format(self.statName, eFreq),
fontsize=plotfonts["suptitle"],
)
st.set_y(0.98)
# plot the data
for idx, val in enumerate(self.winStats):
ax = plt.subplot(nrows, ncols, idx + 1)
plt.title("Value {}".format(val), fontsize=plotfonts["title"])
label = kwargs["label"] if "label" in kwargs else eFreq
# data
plotData = np.squeeze(self.stats[:, eFreqI, idx])
if plotIndices is not None:
plotData = plotData[plotIndices]
# remove infinities and nans
plotData = plotData[np.isfinite(plotData)]
# x axis options
xlim = (
kwargs["xlim"]
if "xlim" in kwargs
else [np.min(plotData), np.max(plotData)]
)
plt.xlim(xlim)
plt.xlabel("Value", fontsize=plotfonts["axisLabel"])
# now plot with xlim in mind
plt.hist(plotData, numbins, range=xlim, facecolor="red", alpha=0.75)
# y axis options
plt.ylabel("Count", fontsize=plotfonts["axisLabel"])
# set tick sizes
for label in ax.get_xticklabels() + ax.get_yticklabels():
label.set_fontsize(plotfonts["axisTicks"])
plt.grid(True, ls="--")
# legend
if "legend" in kwargs and kwargs["legend"]:
plt.legend(loc=4)
# show if the figure is not in keywords
if "fig" not in kwargs:
fig.tight_layout(rect=[0.02, 0.02, 0.98, 0.92])
plt.show()
return fig
[docs] def crossplot(self, eFreqI: int, **kwargs) -> Figure:
"""Plots crossplots of statistic components for evaluation frequency index
Notes
-----
By default, the crossplots plotted are
statistic component 1 vs statistic component 2
statistic component 3 vs statistic component 4
etc
But crossplots can be explicity set by using the crossplots keyword. They should be specified as a list of a list of strings
e.g.
crossplots = [[component2, component3], [component1, component4]]
Parameters
----------
eFreqI : int
Evaluation frequency index
maskwindows : List, np.ndarray
Global windows to exclude
crossplots : List[List[str]], optional
The parameters to crossplot
fig : matplotlib.pyplot.figure, optional
A figure object
plotfonts : Dict, optional
A dictionary of plot fonts
label : str, optional
Label for the plots
clim : List, optional
Limits for colourbar axis
xlim : List, optional
Limits for the x axis
ylim : List, optional
Limits for the y axis
colortitle : str, optional
Title for the colourbar
legened : bool
Boolean flag for adding a legend
Returns
-------
plt.figure
Matplotlib figure object
"""
# deal with maskwindows, which are global indices
maskWindows = kwargs["maskwindows"] if "maskwindows" in kwargs else []
plotIndices = self.getUnmaskedIndices(maskWindows)
# figure out the crossplots
if "crossplots" in kwargs:
crossplots = kwargs["crossplots"]
else:
crossplots = list(zip(self.winStats[::2], self.winStats[1::2]))
# plot parameters
nrows, ncols = self.getRowsCols(self.maxcols, numStats=len(crossplots))
eFreq = self.evalFreq[eFreqI]
plotfonts = kwargs["plotfonts"] if "plotfonts" in kwargs else getViewFonts()
fig = (
plt.figure(kwargs["fig"].number)
if "fig" in kwargs
else plt.figure(figsize=(4 * ncols, 5 * nrows))
)
st = fig.suptitle(
"{} crossplots for evaluation frequency: {:.3f} Hz".format(
self.statName, eFreq
),
fontsize=plotfonts["suptitle"],
)
st.set_y(0.98)
# now plot the data
for idx, cplot in enumerate(crossplots):
ax = plt.subplot(nrows, ncols, idx + 1)
plt.title(
"{} vs. {}".format(cplot[0], cplot[1]), fontsize=plotfonts["title"]
)
label = kwargs["label"] if "label" in kwargs else eFreq
# the colourdata
colourbool, colourdata, cmap = self.calcColourData(
self.globalIndices, cplot[0], eFreqI, kwargs
)
# get plot data
plotI1 = self.winStats.index(cplot[0])
plotData1 = np.squeeze(self.stats[:, eFreqI, plotI1])
plotI2 = self.winStats.index(cplot[1])
plotData2 = np.squeeze(self.stats[:, eFreqI, plotI2])
if plotIndices is not None:
plotData1 = plotData1[plotIndices]
plotData2 = plotData2[plotIndices]
colourdata = colourdata[plotIndices]
# scatter plot
scat = plt.scatter(
plotData1,
plotData2,
c=colourdata,
edgecolors="none",
marker="o",
s=12,
cmap=cmap,
label=label,
)
clim = (
kwargs["clim"]
if "clim" in kwargs
else [colourdata.min(), colourdata.max()]
)
scat.set_clim(clim)
# x axis options
plt.xlabel("Value {}".format(cplot[0]), fontsize=plotfonts["axisLabel"])
if "xlim" in kwargs:
plt.xlim(kwargs["xlim"])
# y axis options
plt.ylabel("Value {}".format(cplot[1]), fontsize=plotfonts["axisLabel"])
if "ylim" in kwargs:
plt.ylim(kwargs["ylim"])
# set tick sizes
for label in ax.get_xticklabels() + ax.get_yticklabels():
label.set_fontsize(plotfonts["axisTicks"])
plt.grid(True, ls="--")
# legend
if "legend" in kwargs and kwargs["legend"]:
plt.legend(loc=4)
# show if the figure is not in keywords
if "fig" not in kwargs:
fig.tight_layout(rect=[0.02, 0.02, 0.85, 0.92])
cax = fig.add_axes([0.88, 0.10, 0.03, 0.80])
if not colourbool:
self.addColourbarDates(scat, cax, "Time", plotfonts)
else:
colourtitle = (
kwargs["colortitle"] if "colortitle" in kwargs else "Value"
)
self.addColourbar(scat, cax, colourtitle, plotfonts)
plt.show()
return fig
[docs] def densityplot(self, eFreqI: int, **kwargs) -> Figure:
"""Plots density plots of statistic components for evaluation frequency index
Notes
-----
By default, the density plots plotted are
statistic component 1 vs statistic component 2
statistic component 3 vs statistic component 4
etc
But density plots can be explicity set by using the crossplots keyword. They should be specified as a list of a list of strings
e.g.
crossplots = [[component2, component3], [component1, component4]]
Parameters
----------
eFreqI : int
Evaluation frequency index
maskwindows : List, np.ndarray
Global windows to exclude
crossplots : List[List[str]], optional
The parameters to crossplot
fig : matplotlib.pyplot.figure, optional
A figure object
plotfonts : Dict, optional
A dictionary of plot fonts
label : str, optional
Label for the plots
xlim : List, optional
Limits for the x axis
ylim : List, optional
Limits for the y axis
colortitle : str, optional
Title for the colourbar
legened : bool
Boolean flag for adding a legend
Returns
-------
plt.figure
Matplotlib figure object
"""
# deal with maskwindows, which are global indices
maskWindows = kwargs["maskwindows"] if "maskwindows" in kwargs else []
plotIndices = self.getUnmaskedIndices(maskWindows)
# figure out the crossplots
if "crossplots" in kwargs:
crossplots = kwargs["crossplots"]
else:
crossplots = list(zip(self.winStats[::2], self.winStats[1::2]))
# plot parameters
nrows, ncols = self.getRowsCols(self.maxcols, numStats=len(crossplots))
eFreq = self.evalFreq[eFreqI]
plotfonts = kwargs["plotfonts"] if "plotfonts" in kwargs else getViewFonts()
fig = (
plt.figure(kwargs["fig"].number)
if "fig" in kwargs
else plt.figure(figsize=(4 * ncols, 5 * nrows))
)
st = fig.suptitle(
"{} density plots for evaluation frequency: {:.3f} Hz".format(
self.statName, eFreq
),
fontsize=plotfonts["suptitle"],
)
st.set_y(0.98)
# now plot the data
for idx, cplot in enumerate(crossplots):
ax = plt.subplot(nrows, ncols, idx + 1)
plt.title(
"{} vs. {}".format(cplot[0], cplot[1]), fontsize=plotfonts["title"]
)
label = kwargs["label"] if "label" in kwargs else eFreq
# get plot data
plotI1 = self.winStats.index(cplot[0])
plotData1 = np.squeeze(self.stats[:, eFreqI, plotI1])
plotI2 = self.winStats.index(cplot[1])
plotData2 = np.squeeze(self.stats[:, eFreqI, plotI2])
if plotIndices is not None:
plotData1 = plotData1[plotIndices]
plotData2 = plotData2[plotIndices]
nbins = 200
if "xlim" in kwargs:
plt.xlim(kwargs["xlim"])
rangex = kwargs["xlim"]
else:
minx = np.percentile(plotData1, 2)
maxx = np.percentile(plotData1, 98)
plt.xlim(minx, maxx)
rangex = [minx, maxx]
if "ylim" in kwargs:
plt.ylim(kwargs["ylim"])
rangey = kwargs["ylim"]
else:
miny = np.percentile(plotData2, 2)
maxy = np.percentile(plotData2, 98)
plt.ylim(miny, maxy)
rangey = [miny, maxy]
plt.hist2d(
plotData1,
plotData2,
bins=(nbins, nbins),
range=[rangex, rangey],
cmap=plt.cm.inferno,
)
# axis options
plt.xlabel("Value {}".format(cplot[0]), fontsize=plotfonts["axisLabel"])
plt.ylabel("Value {}".format(cplot[1]), fontsize=plotfonts["axisLabel"])
# set tick sizes
for label in ax.get_xticklabels() + ax.get_yticklabels():
label.set_fontsize(plotfonts["axisTicks"])
plt.grid(True, ls="--")
# legend
if "legend" in kwargs and kwargs["legend"]:
plt.legend(loc=4)
# show if the figure is not in keywords
if "fig" not in kwargs:
fig.tight_layout(rect=[0.02, 0.02, 0.85, 0.92])
plt.show()
return fig
[docs] def addColourbar(
self, plot: PathCollection, cax, title: str, plotfonts: Dict
) -> None:
"""Add a colourbar to a plot
Parameters
----------
plot : matplotlib.collections.PathCollection
A plot e.g. a scatter graph
cax :
Colourbar axis
title : str
The tital for the colourbar
plotfonts : Dict
A dictionary with font types as keys and sizes as values
"""
plt.colorbar(plot, cax=cax)
cax.set_title(title, y=1.02, fontsize=plotfonts["title"])
[docs] def addColourbarDates(self, plot: PathCollection, cax, title: str, plotfonts: Dict):
"""Make the colourbar show dates for identifying window times where there is not date axis
Parameters
----------
plot : matplotlib.collections.PathCollection
A plot e.g. a scatter graph
cax :
Colourbar axis
title : str
The tital for the colourbar
plotfonts : Dict
A dictionary with font types as keys and sizes as values
"""
ticks, tickLabels = self.dateTicks(self.globalIndices, self.getGlobalDates(), 5)
cb = plt.colorbar(plot, cax=cax)
cb.set_ticks(ticks)
cb.set_ticklabels(tickLabels)
cax.set_title(title, y=1.02, fontsize=plotfonts["title"])
[docs] def dateTicks(self, gIndices, dates, timeNum):
"""Format dateticks
.. todo::
Write more complete documentation
"""
numVals = len(gIndices)
if timeNum >= numVals:
timeNum = numVals
plotIndices = []
for i in range(0, timeNum):
plotIndices.append(int(i * numVals * 1.0 / (timeNum - 1)))
plotIndices[-1] = numVals - 1
ticks = []
tickLabels = []
for i in plotIndices:
ticks.append(gIndices[i])
tickLabels.append(dates[i].strftime("%m-%d %H:%M:%S"))
return ticks, tickLabels
[docs] def calcColourData(self, plotData, val, eFreqI, keywords):
"""Calculate the colour data
.. todo::
Write more complete documentation
"""
if "colorstat" in keywords and "colormap" in keywords:
colourVal = keywords["colormap"][val]
colourIndex = keywords["colorstat"].winStats.index(colourVal)
return (
True,
np.squeeze(keywords["colorstat"].stats[:, eFreqI, colourIndex]),
colorbar2dOther(),
)
return False, plotData, colorbar2dTime()
[docs] def getRowsCols(self, maxcols: int, numStats: int = 0) -> Tuple[int, int]:
"""Get the numbers of rows and columns for the plots
Parameters
----------
maxcols : int
The maximum number of columns
numStats : int
The number of statistics to plot (in case this is not all of the winStats)
"""
if numStats < 1:
numStats = self.numStatsPerWindow
return getPlotRowsAndCols(maxcols, numStats)
[docs] def printList(self) -> List[str]:
"""Class information as a list of strings
Returns
-------
out : List[str]
List of strings with information
"""
textLst: List[str] = []
textLst.append("Statistic Name = {}".format(self.statName))
textLst.append(
"Reference time = {}".format(self.refTime.strftime("%Y-%m-%d %H:%M:%S.%f"))
)
textLst.append("Sample freq [Hz] = {}".format(self.sampleFreq))
textLst.append("Window size = {}".format(self.winSize))
textLst.append("Window overlap = {}".format(self.winOlap))
textLst.append("Number of windows = {}".format(self.numWindows))
textLst.append("Statistics per Window = {}".format(", ".join(self.winStats)))
textLst.append(
"Evalutation frequencies = {}".format(arrayToString(self.evalFreq))
)
# comments
textLst.append("Comments...")
if len(self.comments) == 0:
textLst.append("No comments")
else:
for comment in self.comments:
textLst.append("\t{}".format(comment))
return textLst