diff --git a/data/AERO4450-Group-10-Data-Analysis-v2.xls b/data/AERO4450-Group-10-Data-Analysis-v2.xls index 27a4d65..a0b45e4 100644 Binary files a/data/AERO4450-Group-10-Data-Analysis-v2.xls and b/data/AERO4450-Group-10-Data-Analysis-v2.xls differ diff --git a/main.py b/main.py index b9445e2..1fbe4e6 100644 --- a/main.py +++ b/main.py @@ -3,13 +3,37 @@ import numpy as np import pandas as pd +from makeGraph import makeGraph, UQ_COLOURS as UQC DATA_FILE_PATH = ".\\data\\AERO4450-Group-10-Data-Analysis-v2.xls" -DATA_FILE = pd.read_excel(DATA_FILE_PATH) +actualData = pd.read_excel(DATA_FILE_PATH, sheet_name="Actual Data") +calibrationData = pd.read_excel(DATA_FILE_PATH, sheet_name="Calibration Data Graphs") def main(): - print(DATA_FILE) + testRun = 0 + + cols = [f"Unnamed: {3 + testRun*5}", f"Unnamed: {4+ testRun*5}"] + data = actualData[cols].rename(columns = {cols[0]:'Time', cols[1]:'Strain'}) + + data = data[5:505].astype({"Time":"int", "Strain":"float"}) + data1 = data[0::2] + data2 = data[1::2] + + + makeGraph({ + "title": f"Strain over Time\nRun: {testRun+1}", + "xLabel": "Time (ms) - Δt = 10ms", + "yLabel": "Strain (ε)", + "plots":[ + {"type":"scatter", "x": data1["Time"], "y": data1["Strain"], "args":{"s":10, "zorder":2}, "label":"Upper", "colour": UQC["purple"]}, + {"type":"scatter", "x": data2["Time"], "y": data2["Strain"], "args":{"s":10, "zorder":1}, "label":"Lower", "colour": UQC["dark_grey"]}, + {"type":"plot", "x": data1["Time"], "y": data1["Strain"], "args":{"zorder":0, "alpha":0.25}, "label":"Upper", "colour": UQC["purple"]}, + {"type":"plot", "x": data2["Time"], "y": data2["Strain"], "args":{"zorder":0, "alpha":0.25}, "label":"Lower", "colour": UQC["dark_grey"]}, + + ] + }) + if __name__ == "__main__": main() \ No newline at end of file diff --git a/makeGraph.py b/makeGraph.py new file mode 100644 index 0000000..c554d6c --- /dev/null +++ b/makeGraph.py @@ -0,0 +1,275 @@ +### MatPlotLib Graph Wrapper +#### Written by Cal.W 2020, originally for MECH2700 but continually +#### expanded upon. +#### 2023 - Added UQ Colors + + +__author__ = "Cal Wing" +__version__ = "0.1.6" + +import numpy as np +import matplotlib +import matplotlib.pyplot as plt +import matplotlib.colors as colors +from mpl_toolkits.axes_grid1 import make_axes_locatable + +UQ_COLOURS = { + # Primary + "purple": "#51247A", + "white" : "#FFFFFF", + "black" : "#000000", + + # Secondary + "light_purple": "#962A8B", + "red" : "#E62645", + "green" : "#2EA836", + "gold" : "#BB9D65", + "neutral" : "#D7D1CC", + "orange" : "#EB602B", + "yellow" : "#FBB800", + "blue" : "#4085C6", + "aqua" : "#00A2C7", + "dark_grey" : "#999490" +} + +#Colorbar Function by Joseph Long & Mike Lampton +# Retrieved from https://joseph-long.com/writing/colorbars/ on 31/10/2021 +# Minor Modifications made by Cal.W 2021 +def colorbar(mappable, size="5%", pad=0.05, lsize=None, lpad=None, lax=True, **kwargs): + last_axes = plt.gca() + ax = mappable.axes + fig = ax.figure + divider = make_axes_locatable(ax) + if lax: + lsize = lsize if lsize is not None else size + lpad = lpad if lpad is not None else pad + dax = divider.append_axes("left", size=lsize, pad=lpad) + dax.set_frame_on(False) + dax.grid(False) + dax.set_yticks([]) + dax.set_xticks([]) + cax = divider.append_axes("right", size=size, pad=pad) + cbar = fig.colorbar(mappable, cax=cax, **kwargs) + plt.sca(last_axes) + return cbar + +def makeGraph(graphData, showPlot=True, doProgramBlock=True): + """ Generate a matplotlib graph based on a simple dictionary object + Input: + dict(graphData): The dictionary containing all the graph data - see example for more info + bool(showPlot[True]): Should the function display the plot + bool(doProgramBlock[True]): Should the function block the main python thread + + Returns: The the figure and axes from matplotlib.pyplot.subplots() + From 'matplotlib.pyplot.subplots(): + fig : `matplotlib.figure.Figure` + ax : `matplotlib.axes.Axes` or array of Axes + *ax* can be either a single `~matplotlib.axes.Axes` object or an + array of Axes objects if more than one subplot was created. + Example: + makeGraph({ + "title": "Simple Plot", + "xLabel": "x label", + "yLabel": "y label", + "plots": [ + {"x":[0,1,2,3,4], "y":[0,1,2,3,4], "label":"Linear"}, + {"x":[0,1,2,3,4], "y":[5,5,5,5,5]}, + {"x":[4,3,2,1,0], "y":[4,3,2,1,0], "label":"Linear2"}, + {"x":0, "type":"axvLine", "label":"Red Vertical Line", "color":"red"}, + {"y":6, "type":"axhLine", "label":"Dashed Horizontal Line", "args":{"linestyle":"--"}}, + {"type":"scatter", "x":4, "y":4, "label":"A Random Point", "colour":"purple", "args":{"zorder":2}} + ] + }) + """ + + doKeyCopy = True + plotDim = (1,) + if "subPlots" in graphData: + if "plotDim" in graphData: plotDim = graphData["plotDim"] + else: plotDim = (1,len(graphData["subPlots"])) + else: + graphData["subPlots"] = [graphData] + doKeyCopy = False + + figSize = graphData["figSize"] if "figSize" in graphData else None + + fig, axes = plt.subplots(*plotDim, figsize=figSize) # Create a figure and an axes. + + #if len(graphData["subPlots"]) <= 1: + # axes = [axes] + #Makes everything nice and linear + # IE ((1,2), (3,4)) = (1,2,3,4) + flatAxes = np.array(axes).flatten().tolist() + + loopKeys = [ + "xLabel", "yLabel", "title", "axis", "grid", "xPos", "yPos", + "xLabelPos", "yLabelPos", "xTickPos", "yTickPos", "xScale", "yScale", + "xTickMap", "yTickMap", "plots", "xLim", "yLim" + ] + + #Feel like this could be optimised + if doKeyCopy: + for key in loopKeys: + if key not in graphData: continue + if key in graphData: + for axGraphData in graphData["subPlots"]: + if key not in axGraphData: + axGraphData[key] = graphData[key] + + + for i, axGraphData in enumerate(graphData["subPlots"]): + ax = flatAxes[i] + + #Draw many plots as needed + # Also provide functions for drawing other types of lines + if "plots" in axGraphData: + getSafeValue = lambda key: pData[key] if key in pData else None #Only return the key-value if present in pData + getSafeValue2 = lambda key, key2: pData[key][key2] if key in pData and key2 in pData[key] else None + for pData in axGraphData["plots"]: + getSafeColour = getSafeValue("colour") or getSafeValue("color") #Figen American Spelling + optArgs = {} if "args" not in pData else pData["args"] #Allow for other args to be passed in + if "type" not in pData or pData["type"] == "plot": + ax.plot(pData["x"], pData["y"], label=getSafeValue("label"), color=getSafeColour, **optArgs) + elif pData["type"] == "hLine": + ax.hlines(pData["y"], *pData["x"], label=getSafeValue("label"), color=getSafeColour, **optArgs) + elif pData["type"] == "vLine": + ax.vlines(pData["x"], *pData["y"], label=getSafeValue("label"), color=getSafeColour, **optArgs) + elif pData["type"] == "axvLine": + if "y" not in pData: pData["y"] = (0, 1) #Span the whole graph + ax.axvline(pData["x"], *pData["y"], label=getSafeValue("label"), color=getSafeColour, **optArgs) + elif pData["type"] == "axhLine": + if "x" not in pData: pData["x"] = (0, 1) #Span the whole graph + ax.axhline(pData["y"], *pData["x"], label=getSafeValue("label"), color=getSafeColour, **optArgs) + elif pData["type"] == "contour": + cs = ax.contour(getSafeValue("x"), getSafeValue("y"), pData["z"], levels=getSafeValue("levels"), colors=getSafeColour, **optArgs) + if "label" in pData: cs.collections[0].set_label(getSafeValue("label")) + elif pData["type"] == "scatter": + ax.scatter(pData["x"], pData["y"], marker=getSafeValue("marker"), label=getSafeValue("label"), color=getSafeColour, **optArgs) + elif pData["type"] == "matshow": + ms = ax.matshow(pData["matrix"], origin=getSafeValue("origin"), label=getSafeValue("label"), **optArgs) + if "colourBar" in pData: + colorbar(ms, extend=getSafeValue2("colourBar", "extend")) + elif pData["type"] == "pColourMesh": + mesh = [] + if "X" in pData or "Y" in pData: + mesh = [pData["X"], pData["Y"], pData["Z"]] + if "x" in pData or "y" in pData: + x = pData["x"]; y = pData["y"] + + if type(x) in [int, float]: x = (0, x, None) + if type(y) in [int, float]: y = (0, x, None) + x = tuple(x); y = tuple(y) + if len(x) < 3: x = (x[0], x[1], None) + if len(y) < 3: y = (y[0], y[1], None) + + x = np.arange(x[0], x[1], x[2]) + y = np.arange(y[0], y[1], y[2]) + X, Y = np.meshgrid(x, y) + + mesh = [X, Y, pData["Z"]] + else: + mesh = [pData["Z"]] + + cNorm = None + if "norm" in pData: + cNorm = colors.LogNorm(vmin=pData["norm"][0], vmax=pData["norm"][1]) + + pcMesh = ax.pcolormesh(*mesh, norm=cNorm, shading=getSafeValue("shading"), label=getSafeValue("label"), **optArgs) + #pcMesh = ax.imshow(pData["Z"], norm=cNorm, origin="lower") + + if "colourBar" in pData: + cBarOptArgs = pData["colourBar"]["optArgs"] if "optArgs" in pData["colourBar"] else {} + fig.colorbar(pcMesh, ax=ax, extend=getSafeValue2("colourBar", "extend"), **cBarOptArgs) + elif pData["type"] == "imshow": + cNorm = None + if "norm" in pData: + cNorm = colors.LogNorm(vmin=pData["norm"][0], vmax=pData["norm"][1]) + + ims = ax.imshow(pData["data"], norm=cNorm, origin=getSafeValue("origin"), label=getSafeValue("label"), **optArgs) + + if "colourBar" in pData: + cBarOptArgs = pData["colourBar"]["optArgs"] if "optArgs" in pData["colourBar"] else {} + colorbar(ims, extend=getSafeValue2("colourBar", "extend"), **cBarOptArgs) + + #Set extra options as needed + if "xLabel" in axGraphData: ax.set_xlabel(axGraphData["xLabel"]) # Add an x-label to the axes. + if "yLabel" in axGraphData: ax.set_ylabel(axGraphData["yLabel"]) # Add an y-label to the axes. + if "title" in axGraphData: ax.set_title(axGraphData["title"]) # Add an title to the axes. + if "axis" in axGraphData: ax.axis(axGraphData["axis"]) # Set the axis type + if "grid" in axGraphData: ax.grid(axGraphData["grid"]) # Add grids to the graph + if "xPos" in axGraphData: # Add the abilty to move the x axis label and ticks + ax.xaxis.set_label_position(axGraphData["xPos"]) + ax.xaxis.set_ticks_position(axGraphData["xPos"]) + if "yPos" in axGraphData: # Add the abilty to move the y axis label and ticks + ax.yaxis.set_label_position(axGraphData["yPos"]) + ax.yaxis.set_ticks_position(axGraphData["yPos"]) + if "xLabelPos" in axGraphData: ax.xaxis.set_label_position(axGraphData["xLabelPos"]) # Add the abilty to move the x axis label + if "yLabelPos" in axGraphData: ax.yaxis.set_label_position(axGraphData["yLabelPos"]) # Add the abilty to move the y axis label + if "xTickPos" in axGraphData: ax.xaxis.set_ticks_position(axGraphData["xTickPos"]) # Add the abilty to move the x axis ticks + if "yTickPos" in axGraphData: ax.yaxis.set_ticks_position(axGraphData["yTickPos"]) # Add the abilty to move the y axis ticks + if "xScale" in axGraphData: ax.set_xscale(axGraphData["xScale"]) #Add x axis scaling if needed + if "yScale" in axGraphData: ax.set_yscale(axGraphData["yScale"]) #Add y axis scaling if needed + if "xLim" in axGraphData: + xLimit = () + if type(axGraphData["xLim"]) in [int, float]: + xLimit = (0, axGraphData["xLim"]) + else: + xLimit = axGraphData["xLim"] + ax.set_xlim(xLimit) + if "yLim" in axGraphData: + yLimit = () + if type(axGraphData["yLim"]) in [int, float]: + yLimit = (0, axGraphData["yLim"]) + else: + yLimit = axGraphData["yLim"] + ax.set_ylim(yLimit) + if "xTickMap" in axGraphData: #Allow for the mapping / transformation of the xAxis Ticks + xTicks = matplotlib.ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(axGraphData["xTickMap"](x))) + ax.xaxis.set_major_formatter(xTicks) + if "yTickMap" in axGraphData: #Allow for the mapping / transformation of the yAxis Ticks + yTicks = matplotlib.ticker.FuncFormatter(lambda y, pos: '{0:g}'.format(axGraphData["yTickMap"](y))) + ax.yaxis.set_major_formatter(yTicks) + if "plots" in axGraphData and bool(sum([("label" in pData) for pData in axGraphData["plots"]])): + ax.legend() #Only draw the legend if there are any defined + + if "title" in graphData and not "figTitle" in graphData: fig.canvas.manager.set_window_title(graphData["title"].replace("\n", " ")) #Set the figure title correctly + if "figTitle" in graphData: + getSafeValue = lambda key: graphData[key] if key in graphData else None #Only return the key-value if present in graphData + + fig.suptitle(graphData["figTitle"], fontsize=getSafeValue("figTitleFontSize")) + fig.canvas.manager.set_window_title(graphData["figTitle"].replace("\n", " ")) + + plt.tight_layout() #Fix labels being cut off sometimes + if showPlot: + plt.show(block=doProgramBlock) #Show the plot and also block the program - doing things OO style allow for more flexible programs + + return fig, axes + +if __name__ == '__main__': + #This is an example of drawing 4 plots by generating them + graphData = { + "figTitle": "Simple Plot", + "figTitleFontSize": 16, + "figSize": (8,8), #Yay America, this is in inches :/ # Note: cm = 1/2.54 + "xLabel": "x label", + "yLabel": "y label", + "plotDim": (2,2), + "subPlots":[] + } + + #Create 4 identical plots with differnt names + for i in range(4): + newPlot = { + "title": f"Graph {i+1}", + "plots": [ + {"x":[0,1,2,3,4], "y":[0,1,2,3,4], "label":"Linear"}, + {"x":[0,1,2,3,4], "y":[5,5,5,5,5]}, + {"x":[4,3,2,1,0], "y":[4,3,2,1,0], "label":"Linear2"}, + {"x":0, "type":"axvLine", "label":"Red Vertical Line", "color":"red"}, + {"y":6, "type":"axhLine", "label":"Dashed Horizontal Line", "args":{"linestyle":"--"}}, + {"type":"scatter", "x":4, "y":4, "label":"A Random Point", "colour":"purple", "args":{"zorder":2}} + ] + } + graphData["subPlots"].append(newPlot) + + makeGraph(graphData) \ No newline at end of file